Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vrrp: T1884: Keep transition-script native behaviour and implement tr… #207

Merged
merged 1 commit into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions interface-definitions/vrrp.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@
</constraint>
</properties>
</leafNode>
<leafNode name="stop">
<properties>
<help>Script to run on VRRP state transition to stop</help>
<constraint>
<validator name="script"/>
</constraint>
</properties>
</leafNode>
</children>
</node>
<leafNode name="virtual-address">
Expand Down
31 changes: 9 additions & 22 deletions python/vyos/keepalived.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
stats_file = '/tmp/keepalived.stats'
json_file = '/tmp/keepalived.json'

state_dir = '/var/run/vyos/vrrp/'

def vrrp_running():
if not os.path.exists(vyos.keepalived.pid_file) \
or not vyos.util.process_running(vyos.keepalived.pid_file):
Expand All @@ -38,6 +36,15 @@ def vrrp_running():
def keepalived_running():
return vyos.util.process_running(pid_file)

## Clear VRRP data after showing
def remove_vrrp_data(data_file):
if data_file == "json" and os.path.exists(json_file):
os.remove(json_file)
elif data_file == "stats" and os.path.exists(stats_file):
os.remove(stats_file)
elif data_file == "state" and os.path.exists(state_file):
os.remove(state_file)

def force_state_data_dump():
pid = vyos.util.read_file(pid_file)
os.kill(int(pid), signal.SIGUSR1)
Expand Down Expand Up @@ -76,26 +83,6 @@ def decode_state(code):

return state

## The functions are mainly for transition script wrappers
## to compensate for the fact that keepalived doesn't keep persistent
## state between reloads.
def get_old_state(group):
file = os.path.join(state_dir, "{0}.state".format(group))
if os.path.exists(file):
with open(file, 'r') as f:
data = f.read().strip()
return data
else:
return None

def save_state(group, state):
if not os.path.exists(state_dir):
os.makedirs(state_dir)

file = os.path.join(state_dir, "{0}.state".format(group))
with open(file, 'w') as f:
f.write(state)

## These functions are for the old, and hopefully obsolete plaintext
## (non machine-readable) data format introduced by Vyatta back in the days
## They are kept here just in case, if JSON output option turns out or becomes
Expand Down
6 changes: 6 additions & 0 deletions src/conf_mode/vrrp.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

global_defs {
dynamic_interfaces
script_user root
}

{% for group in groups -%}
Expand Down Expand Up @@ -117,6 +118,10 @@
{% if group.fault_script -%}
notify_fault "/usr/libexec/vyos/system/vrrp-script-wrapper.py --state fault --group {{ group.name }} --interface {{ group.interface }} {{ group.fault_script }}"
{% endif -%}

{% if group.stop_script -%}
notify_stop "/usr/libexec/vyos/system/vrrp-script-wrapper.py --state stop --group {{ group.name }} --interface {{ group.interface }} {{ group.stop_script }}"
{% endif -%}
}

{% endfor -%}
Expand Down Expand Up @@ -178,6 +183,7 @@ def get_config():
group["master_script"] = config.return_value("transition-script master")
group["backup_script"] = config.return_value("transition-script backup")
group["fault_script"] = config.return_value("transition-script fault")
group["stop_script"] = config.return_value("transition-script stop")

if config.exists("no-preempt"):
group["preempt"] = False
Expand Down
3 changes: 3 additions & 0 deletions src/op_mode/vrrp.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def print_summary():
# Replace with inotify or similar if it proves problematic
time.sleep(0.2)
json_data = vyos.keepalived.get_json_data()
vyos.keepalived.remove_vrrp_data("json")
except:
print("VRRP information is not available")
sys.exit(1)
Expand Down Expand Up @@ -63,6 +64,7 @@ def print_statistics():
time.sleep(0.2)
output = vyos.keepalived.get_statistics()
print(output)
vyos.keepalived.remove_vrrp_data("stats")
except:
print("VRRP statistics are not available")
sys.exit(1)
Expand All @@ -73,6 +75,7 @@ def print_state_data():
time.sleep(0.2)
output = vyos.keepalived.get_state_data()
print(output)
vyos.keepalived.remove_vrrp_data("state")
except:
print("VRRP information is not available")
sys.exit(1)
Expand Down
49 changes: 16 additions & 33 deletions src/system/vrrp-script-wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import syslog

import vyos.util
import vyos.keepalived


parser = argparse.ArgumentParser()
Expand All @@ -44,38 +43,22 @@
# to pass arguments to the script
args.script = " ".join(args.script)

# Get the old state if it exists and compare it to the current state received
# in command line options to avoid executing scripts if no real transition occured.
# This is necessary because keepalived does not keep persistent state data even between
# config reloads and will cheerfully execute everything whether it's required or not.

old_state = vyos.keepalived.get_old_state(args.group)

if (old_state is None) or (old_state != args.state):
exitcode = 0

# Run the script and save the new state

# Change the process GID to the config owners group to avoid screwing up
# running config permissions
os.setgid(vyos.util.get_cfg_group_id())

syslog.syslog(syslog.LOG_NOTICE, 'Running transition script {0} for VRRP group {1}'.format(args.script, args.group))
try:
ret = subprocess.call("%s %s %s %s" % ( args.script, args.state, args.interface, args.group), shell=True)
if ret != 0:
syslog.syslog(syslog.LOG_ERR, "Transition script {0} failed, exit status: {1}".format(args.script, ret))
exitcode = ret
except Exception as e:
syslog.syslog(syslog.LOG_ERR, "Failed to execute transition script {0}: {1}".format(args.script, e))
exitcode = 1

if exitcode == 0:
syslog.syslog(syslog.LOG_NOTICE, "Transition script {0} executed successfully".format(args.script))

vyos.keepalived.save_state(args.group, args.state)
else:
syslog.syslog(syslog.LOG_NOTICE, "State of the group {0} has not changed, not running transition script".format(args.group))
exitcode = 0
# Change the process GID to the config owners group to avoid screwing up
# running config permissions
os.setgid(vyos.util.get_cfg_group_id())
syslog.syslog(syslog.LOG_NOTICE, 'Running transition script {0} for VRRP group {1}'.format(args.script, args.group))
try:
ret = subprocess.call("%s %s %s %s" % ( args.script, args.state, args.interface, args.group), shell=True)
if ret != 0:
syslog.syslog(syslog.LOG_ERR, "Transition script {0} failed, exit status: {1}".format(args.script, ret))
exitcode = ret
except Exception as e:
syslog.syslog(syslog.LOG_ERR, "Failed to execute transition script {0}: {1}".format(args.script, e))
exitcode = 1

if exitcode == 0:
syslog.syslog(syslog.LOG_NOTICE, "Transition script {0} executed successfully".format(args.script))

syslog.closelog()
sys.exit(exitcode)