summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/salt-minion.py105
-rwxr-xr-xsrc/migration-scripts/salt/0-to-158
-rwxr-xr-xsrc/op_mode/powerctrl.py291
3 files changed, 257 insertions, 197 deletions
diff --git a/src/conf_mode/salt-minion.py b/src/conf_mode/salt-minion.py
index 236480854..dffe7fcd4 100755
--- a/src/conf_mode/salt-minion.py
+++ b/src/conf_mode/salt-minion.py
@@ -17,117 +17,108 @@
import os
from copy import deepcopy
-from pwd import getpwnam
from socket import gethostname
from sys import exit
from urllib3 import PoolManager
from vyos.config import Config
-from vyos import ConfigError
-from vyos.util import call
from vyos.template import render
-
+from vyos.util import call, chown
+from vyos import ConfigError
config_file = r'/etc/salt/minion'
+master_keyfile = r'/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub'
default_config_data = {
- 'hash_type': 'sha256',
- 'log_file': '/var/log/salt/minion',
+ 'hash': 'sha256',
'log_level': 'warning',
'master' : 'salt',
'user': 'minion',
+ 'group': 'vyattacfg',
'salt_id': gethostname(),
'mine_interval': '60',
- 'verify_master_pubkey_sign': 'false'
+ 'verify_master_pubkey_sign': 'false',
+ 'master_key': ''
}
def get_config():
salt = deepcopy(default_config_data)
conf = Config()
- if not conf.exists('service salt-minion'):
+ base = ['service', 'salt-minion']
+
+ if not conf.exists(base):
return None
else:
- conf.set_level('service salt-minion')
-
- if conf.exists('hash_type'):
- salt['hash_type'] = conf.return_value('hash_type')
+ conf.set_level(base)
- if conf.exists('log_file'):
- salt['log_file'] = conf.return_value('log_file')
+ if conf.exists(['hash']):
+ salt['hash'] = conf.return_value(['hash'])
- if conf.exists('log_level'):
- salt['log_level'] = conf.return_value('log_level')
+ if conf.exists(['master']):
+ salt['master'] = conf.return_values(['master'])
- if conf.exists('master'):
- master = conf.return_values('master')
- salt['master'] = master
+ if conf.exists(['id']):
+ salt['salt_id'] = conf.return_value(['id'])
- if conf.exists('id'):
- salt['salt_id'] = conf.return_value('id')
+ if conf.exists(['user']):
+ salt['user'] = conf.return_value(['user'])
- if conf.exists('user'):
- salt['user'] = conf.return_value('user')
+ if conf.exists(['interval']):
+ salt['interval'] = conf.return_value(['interval'])
- if conf.exists('mine_interval'):
- salt['mine_interval'] = conf.return_value('mine_interval')
-
- salt['master-key'] = None
- if conf.exists('master-key'):
- salt['master-key'] = conf.return_value('master-key')
+ if conf.exists(['master-key']):
+ salt['master_key'] = conf.return_value(['master-key'])
salt['verify_master_pubkey_sign'] = 'true'
return salt
-def generate(salt):
- paths = ['/etc/salt/','/var/run/salt','/opt/vyatta/etc/config/salt/']
- directory = '/opt/vyatta/etc/config/salt/pki/minion'
- uid = getpwnam(salt['user']).pw_uid
- http = PoolManager()
+def verify(salt):
+ return None
- if salt is None:
+def generate(salt):
+ if not salt:
return None
- if not os.path.exists(directory):
- os.makedirs(directory)
+ for file in [config_file, master_keyfile]:
+ dirname = os.path.dirname(file)
+ if not os.path.exists(dirname):
+ os.mkdir(dirname)
+ chown(dirname, salt['user'], salt['group'])
render(config_file, 'salt-minion/minion.tmpl', salt)
+ chown(config_file, salt['user'], salt['group'])
- path = "/etc/salt/"
- for path in paths:
- for root, dirs, files in os.walk(path):
- for usgr in dirs:
- os.chown(os.path.join(root, usgr), uid, 100)
- for usgr in files:
- os.chown(os.path.join(root, usgr), uid, 100)
-
- if not os.path.exists('/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub'):
- if not salt['master-key'] is None:
- r = http.request('GET', salt['master-key'], preload_content=False)
+ if not os.path.exists(master_keyfile):
+ if salt['master_key']:
+ req = PoolManager().request('GET', salt['master_key'], preload_content=False)
- with open('/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub', 'wb') as out:
+ with open(master_keyfile, 'wb') as f:
while True:
- data = r.read(1024)
+ data = req.read(1024)
if not data:
break
- out.write(data)
+ f.write(data)
- r.release_conn()
+ req.release_conn()
+ chown(master_keyfile, salt['user'], salt['group'])
return None
def apply(salt):
- if salt is not None:
- call("sudo systemctl restart salt-minion")
+ if not salt:
+ # Salt removed from running config
+ call('systemctl stop salt-minion.service')
+ if os.path.exists(config_file):
+ os.unlink(config_file)
else:
- # Salt access is removed in the commit
- call("sudo systemctl stop salt-minion")
- os.unlink(config_file)
+ call('systemctl restart salt-minion.service')
return None
if __name__ == '__main__':
try:
c = get_config()
+ verify(c)
generate(c)
apply(c)
except ConfigError as e:
diff --git a/src/migration-scripts/salt/0-to-1 b/src/migration-scripts/salt/0-to-1
new file mode 100755
index 000000000..79053c056
--- /dev/null
+++ b/src/migration-scripts/salt/0-to-1
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Delete log_file, log_level and user nodes
+# rename hash_type to hash
+# rename mine_interval to interval
+
+from sys import argv,exit
+
+from vyos.configtree import ConfigTree
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+base = ['service', 'salt-minion']
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+else:
+
+ # delete nodes which are now populated with sane defaults
+ for node in ['log_file', 'log_level', 'user']:
+ if config.exists(base + [node]):
+ config.delete(base + [node])
+
+ if config.exists(base + ['hash_type']):
+ config.rename(base + ['hash_type'], 'hash')
+
+ if config.exists(base + ['mine_interval']):
+ config.rename(base + ['mine_interval'], 'interval')
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py
index 4ab91384b..b2fb5fe5a 100755
--- a/src/op_mode/powerctrl.py
+++ b/src/op_mode/powerctrl.py
@@ -15,168 +15,179 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import sys
-import argparse
import re
+from argparse import ArgumentParser
from datetime import datetime, timedelta, time as type_time, date as type_date
-from vyos.util import ask_yes_no
-from vyos.util import cmd
-from vyos.util import call
-from vyos.util import run
-from vyos.util import STDOUT
+from sys import exit
+from time import time
+
+from vyos.util import ask_yes_no, cmd, call, run, STDOUT
systemd_sched_file = "/run/systemd/shutdown/scheduled"
-def parse_time(s):
- try:
- if re.match(r'^\d{1,2}$', s):
- return datetime.strptime(s, "%M").time()
- else:
- return datetime.strptime(s, "%H:%M").time()
- except ValueError:
- return None
+def utc2local(datetime):
+ now = time()
+ offs = datetime.fromtimestamp(now) - datetime.utcfromtimestamp(now)
+ return datetime + offs
-def parse_date(s):
- for fmt in ["%d%m%Y", "%d/%m/%Y", "%d.%m.%Y", "%d:%m:%Y", "%Y-%m-%d"]:
+def parse_time(s):
try:
- return datetime.strptime(s, fmt).date()
+ if re.match(r'^\d{1,2}$', s):
+ return datetime.strptime(s, "%M").time()
+ else:
+ return datetime.strptime(s, "%H:%M").time()
except ValueError:
- continue
- # If nothing matched...
- return None
+ return None
+
+
+def parse_date(s):
+ for fmt in ["%d%m%Y", "%d/%m/%Y", "%d.%m.%Y", "%d:%m:%Y", "%Y-%m-%d"]:
+ try:
+ return datetime.strptime(s, fmt).date()
+ except ValueError:
+ continue
+ # If nothing matched...
+ return None
+
def get_shutdown_status():
- if os.path.exists(systemd_sched_file):
- # Get scheduled from systemd file
- with open(systemd_sched_file, 'r') as f:
- data = f.read().rstrip('\n')
- r_data = {}
- for line in data.splitlines():
- tmp_split = line.split("=")
- if tmp_split[0] == "USEC":
- # Convert USEC to human readable format
- r_data['DATETIME'] = datetime.utcfromtimestamp(int(tmp_split[1])/1000000).strftime('%Y-%m-%d %H:%M:%S')
- else:
- r_data[tmp_split[0]] = tmp_split[1]
- return r_data
- return None
+ if os.path.exists(systemd_sched_file):
+ # Get scheduled from systemd file
+ with open(systemd_sched_file, 'r') as f:
+ data = f.read().rstrip('\n')
+ r_data = {}
+ for line in data.splitlines():
+ tmp_split = line.split("=")
+ if tmp_split[0] == "USEC":
+ # Convert USEC to human readable format
+ r_data['DATETIME'] = datetime.utcfromtimestamp(
+ int(tmp_split[1])/1000000).strftime('%Y-%m-%d %H:%M:%S')
+ else:
+ r_data[tmp_split[0]] = tmp_split[1]
+ return r_data
+ return None
+
def check_shutdown():
- output = get_shutdown_status()
- if output and 'MODE' in output:
- if output['MODE'] == 'reboot':
- print("Reboot is scheduled", output['DATETIME'])
- elif output['MODE'] == 'poweroff':
- print("Poweroff is scheduled", output['DATETIME'])
- else:
- print("Reboot or poweroff is not scheduled")
+ output = get_shutdown_status()
+ dt = datetime.strptime(output['DATETIME'], '%Y-%m-%d %H:%M:%S')
+ if output and 'MODE' in output:
+ if output['MODE'] == 'reboot':
+ print("Reboot is scheduled", utc2local(dt))
+ elif output['MODE'] == 'poweroff':
+ print("Poweroff is scheduled", utc2local(dt))
+ else:
+ print("Reboot or poweroff is not scheduled")
+
def cancel_shutdown():
- output = get_shutdown_status()
- if output and 'MODE' in output:
- timenow = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- try:
- cmd('/sbin/shutdown -c --no-wall')
- except OSError as e:
- sys.exit("Could not cancel a reboot or poweroff: %s" % e)
- message = "Scheduled %s has been cancelled %s" % (output['MODE'], timenow)
- run(f'wall {message}')
- else:
- print("Reboot or poweroff is not scheduled")
-
-def execute_shutdown(time, reboot = True, ask=True):
- if not ask:
- action = "reboot" if reboot else "poweroff"
- if not ask_yes_no("Are you sure you want to %s this system?" % action):
- sys.exit(0)
-
- action = "-r" if reboot else "-P"
-
- if len(time) == 0:
- ### T870 legacy reboot job support
- chk_vyatta_based_reboots()
- ###
-
- out = cmd(f'/sbin/shutdown {action} now', stderr=STDOUT)
- print(out.split(",",1)[0])
- return
- elif len(time) == 1:
- # Assume the argument is just time
- ts = parse_time(time[0])
- if ts:
- cmd(f'/sbin/shutdown {action} {time[0]}', stderr=STDOUT)
+ output = get_shutdown_status()
+ if output and 'MODE' in output:
+ timenow = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ try:
+ run('/sbin/shutdown -c --no-wall')
+ except OSError as e:
+ exit("Could not cancel a reboot or poweroff: %s" % e)
+
+ message = 'Scheduled {} has been cancelled {}'.format(output['MODE'], timenow)
+ run(f'wall {message} > /dev/null 2>&1')
else:
- sys.exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0]))
- elif len(time) == 2:
- # Assume it's date and time
- ts = parse_time(time[0])
- ds = parse_date(time[1])
- if ts and ds:
- t = datetime.combine(ds, ts)
- td = t - datetime.now()
- t2 = 1 + int(td.total_seconds())//60 # Get total minutes
- cmd('/sbin/shutdown {action} {t2}', stderr=STDOUT)
+ print("Reboot or poweroff is not scheduled")
+
+
+def execute_shutdown(time, reboot=True, ask=True):
+ if not ask:
+ action = "reboot" if reboot else "poweroff"
+ if not ask_yes_no("Are you sure you want to %s this system?" % action):
+ exit(0)
+
+ action = "-r" if reboot else "-P"
+
+ if len(time) == 0:
+ # T870 legacy reboot job support
+ chk_vyatta_based_reboots()
+ ###
+
+ out = cmd(f'/sbin/shutdown {action} now', stderr=STDOUT)
+ print(out.split(",", 1)[0])
+ return
+ elif len(time) == 1:
+ # Assume the argument is just time
+ ts = parse_time(time[0])
+ if ts:
+ cmd(f'/sbin/shutdown {action} {time[0]}', stderr=STDOUT)
+ else:
+ exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0]))
+ elif len(time) == 2:
+ # Assume it's date and time
+ ts = parse_time(time[0])
+ ds = parse_date(time[1])
+ if ts and ds:
+ t = datetime.combine(ds, ts)
+ td = t - datetime.now()
+ t2 = 1 + int(td.total_seconds())//60 # Get total minutes
+ cmd('/sbin/shutdown {action} {t2}', stderr=STDOUT)
+ else:
+ if not ts:
+ exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0]))
+ else:
+ exit("Invalid time \"{0}\". A valid format is YYYY-MM-DD [HH:MM]".format(time[1]))
else:
- if not ts:
- sys.exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0]))
- else:
- sys.exit("Invalid time \"{0}\". A valid format is YYYY-MM-DD [HH:MM]".format(time[1]))
- else:
- sys.exit("Could not decode date and time. Valids formats are HH:MM or YYYY-MM-DD HH:MM")
- check_shutdown()
+ exit("Could not decode date and time. Valids formats are HH:MM or YYYY-MM-DD HH:MM")
+ check_shutdown()
+
def chk_vyatta_based_reboots():
- ### T870 commit-confirm is still using the vyatta code base, once gone, the code below can be removed
- ### legacy scheduled reboot s are using at and store the is as /var/run/<name>.job
- ### name is the node of scheduled the job, commit-confirm checks for that
+ # T870 commit-confirm is still using the vyatta code base, once gone, the code below can be removed
+ # legacy scheduled reboot s are using at and store the is as /var/run/<name>.job
+ # name is the node of scheduled the job, commit-confirm checks for that
+
+ f = r'/var/run/confirm.job'
+ if os.path.exists(f):
+ jid = open(f).read().strip()
+ if jid != 0:
+ call(f'sudo atrm {jid}')
+ os.remove(f)
- f = r'/var/run/confirm.job'
- if os.path.exists(f):
- jid = open(f).read().strip()
- if jid != 0:
- call(f'sudo atrm {jid}')
- os.remove(f)
def main():
- parser = argparse.ArgumentParser()
- parser.add_argument("--yes", "-y",
- help="Do not ask for confirmation",
- action="store_true",
- dest="yes")
- action = parser.add_mutually_exclusive_group(required=True)
- action.add_argument("--reboot", "-r",
- help="Reboot the system",
- nargs="*",
- metavar="Minutes|HH:MM")
-
- action.add_argument("--poweroff", "-p",
- help="Poweroff the system",
- nargs="*",
- metavar="Minutes|HH:MM")
-
- action.add_argument("--cancel", "-c",
- help="Cancel pending shutdown",
- action="store_true")
-
- action.add_argument("--check",
- help="Check pending chutdown",
- action="store_true")
- args = parser.parse_args()
-
- try:
- if args.reboot is not None:
- execute_shutdown(args.reboot, reboot=True, ask=args.yes)
- if args.poweroff is not None:
- execute_shutdown(args.poweroff, reboot=False,ask=args.yes)
- if args.cancel:
- cancel_shutdown()
- if args.check:
- check_shutdown()
- except KeyboardInterrupt:
- sys.exit("Interrupted")
+ parser = ArgumentParser()
+ parser.add_argument("--yes", "-y",
+ help="Do not ask for confirmation",
+ action="store_true",
+ dest="yes")
+ action = parser.add_mutually_exclusive_group(required=True)
+ action.add_argument("--reboot", "-r",
+ help="Reboot the system",
+ nargs="*",
+ metavar="Minutes|HH:MM")
+
+ action.add_argument("--poweroff", "-p",
+ help="Poweroff the system",
+ nargs="*",
+ metavar="Minutes|HH:MM")
+
+ action.add_argument("--cancel", "-c",
+ help="Cancel pending shutdown",
+ action="store_true")
+
+ action.add_argument("--check",
+ help="Check pending chutdown",
+ action="store_true")
+ args = parser.parse_args()
+ try:
+ if args.reboot is not None:
+ execute_shutdown(args.reboot, reboot=True, ask=args.yes)
+ if args.poweroff is not None:
+ execute_shutdown(args.poweroff, reboot=False, ask=args.yes)
+ if args.cancel:
+ cancel_shutdown()
+ if args.check:
+ check_shutdown()
+ except KeyboardInterrupt:
+ exit("Interrupted")
if __name__ == "__main__":
- main()
-
+ main()