diff options
author | Christian Poessinger <christian@poessinger.com> | 2020-04-07 08:27:36 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-07 08:27:36 +0200 |
commit | 09ad28b28c9ebd9308cfe9048686b3b0ef9cfd9c (patch) | |
tree | 6e7b0971ecd8859cff864b3ebb37f86f8ba288f5 | |
parent | e0f13b79a669e7fc8cadac8757b2f5fbbf51dc99 (diff) | |
parent | 7256810914e6664bf92041dcd7c3daf649ce0001 (diff) | |
download | vyos-1x-09ad28b28c9ebd9308cfe9048686b3b0ef9cfd9c.tar.gz vyos-1x-09ad28b28c9ebd9308cfe9048686b3b0ef9cfd9c.zip |
Merge pull request #307 from thomas-mangin/T2226
util: T2226: convert all call to use vyos.util.{popen, cmd, run}
92 files changed, 653 insertions, 712 deletions
diff --git a/python/vyos/authutils.py b/python/vyos/authutils.py index 234294649..90a46ffb4 100644 --- a/python/vyos/authutils.py +++ b/python/vyos/authutils.py @@ -15,16 +15,14 @@ import re -from subprocess import Popen, PIPE, STDOUT +from vyos.util import cmd def make_password_hash(password): """ Makes a password hash for /etc/shadow using mkpasswd """ - mkpasswd = Popen(['mkpasswd', '--method=sha-512', '--stdin'], stdout=PIPE, stdin=PIPE, stderr=PIPE) - hash = mkpasswd.communicate(input=password.encode(), timeout=5)[0].decode().strip() - - return hash + mkpassword = 'mkpasswd --method=sha-512 --stdin' + return cmd(mkpassword, input=password.encode(), timeout=5) def split_ssh_public_key(key_string, defaultname=""): """ Splits an SSH public key into its components """ diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index c18d2e72f..291b326bf 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -18,7 +18,7 @@ import re from vyos.ifconfig.interface import Interface from vyos.ifconfig.vlan import VLAN -from vyos.util import popen +from vyos.util import run from vyos.validate import * @@ -45,7 +45,7 @@ class EthernetIf(Interface): @staticmethod def feature(ifname, option, value): - out, code = popen(f'/sbin/ethtool -K {ifname} {option} {value}','ifconfig') + run(f'/sbin/ethtool -K {ifname} {option} {value}','ifconfig') return False _command_set = {**Interface._command_set, **{ diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index def5ab7c5..e2b8a5924 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -16,7 +16,6 @@ import os import time -import subprocess from datetime import timedelta from vyos.config import Config diff --git a/python/vyos/remote.py b/python/vyos/remote.py index f8a21f068..f0bf41cd4 100644 --- a/python/vyos/remote.py +++ b/python/vyos/remote.py @@ -17,7 +17,8 @@ import sys import os import re import fileinput -import subprocess + +from vyos.util import cmd, DEVNULL def check_and_add_host_key(host_name): @@ -33,10 +34,8 @@ def check_and_add_host_key(host_name): keyscan_cmd = 'ssh-keyscan -t rsa {} 2>/dev/null'.format(host_name) try: - host_key = subprocess.check_output(keyscan_cmd, shell=True, - stderr=subprocess.DEVNULL, - universal_newlines=True) - except subprocess.CalledProcessError as err: + host_key = cmd(keyscan_cmd, stderr=DEVNULL, universal_newlines=True) + except OSError: sys.exit("Can not get RSA host key") # libssh2 (jessie; stretch) does not recognize ec host keys, and curl @@ -64,10 +63,8 @@ def check_and_add_host_key(host_name): fingerprint_cmd = 'ssh-keygen -lf /dev/stdin <<< "{}"'.format(host_key) try: - fingerprint = subprocess.check_output(fingerprint_cmd, shell=True, - stderr=subprocess.DEVNULL, - universal_newlines=True) - except subprocess.CalledProcessError as err: + fingerprint = cmd(fingerprint_cmd, stderr=DEVNULL, universal_newlines=True) + except OSError: sys.exit("Can not get RSA host key fingerprint.") print("RSA host key fingerprint is {}".format(fingerprint.split()[1])) @@ -128,9 +125,8 @@ def get_remote_config(remote_file): # Try header first, and look for 'OK' or 'Moved' codes: curl_cmd = 'curl {0} -q -I {1}'.format(redirect_opt, remote_file) try: - curl_output = subprocess.check_output(curl_cmd, shell=True, - universal_newlines=True) - except subprocess.CalledProcessError: + curl_output = cmd(curl_cmd, shell=True, universal_newlines=True) + except OSError: sys.exit(1) return_vals = re.findall(r'^HTTP\/\d+\.?\d\s+(\d+)\s+(.*)$', @@ -146,9 +142,6 @@ def get_remote_config(remote_file): curl_cmd = 'curl {0} -# {1}'.format(redirect_opt, remote_file) try: - config_file = subprocess.check_output(curl_cmd, shell=True, - universal_newlines=True) - except subprocess.CalledProcessError: - config_file = None - - return config_file + return cmd(curl_cmd, universal_newlines=True) + except OSError: + return None diff --git a/python/vyos/util.py b/python/vyos/util.py index c8ab43c11..fa2b4dd99 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -16,7 +16,7 @@ import os import re import sys -from subprocess import Popen, PIPE, STDOUT +from subprocess import Popen, PIPE, STDOUT, DEVNULL # debugging @@ -33,36 +33,65 @@ def debug_msg(message, section=''): # commands - -def popen(command, section=''): - p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True) - tmp = p.communicate()[0].strip() +# popen does not raise +# it returns the output of the command and the error code +def popen(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None): + use_shell = shell + if shell is None: + use_shell = True if ' ' in command else False + p = Popen( + command, + stdout=stdout, stderr=stderr, + env=env, shell=use_shell, + universal_newlines=universal_newlines, + ) + tmp = p.communicate(input, timeout)[0].strip() debug_msg(f"cmd '{command}'", section) - decoded = tmp.decode() + decoded = tmp.decode(decode) if decode else tmp.decode() if decoded: debug_msg(f"returned:\n{decoded}", section) return decoded, p.returncode - -def cmd(command, section=''): - decoded, code = popen(command, section) +# run does not raise +# it returns the error code +def run(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None): + _, code = popen( + command, section, + stdout=stdout, stderr=stderr, + input=input, timeout=timeout, + env=env, shell=shell, + universal_newlines=universal_newlines, + decode=decode, + ) + return code + +# cmd does raise +# it returns the output +def cmd(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None, raising=None, message=''): + decoded, code = popen( + command, section, + stdout=stdout, stderr=stderr, + input=input, timeout=timeout, + env=env, shell=shell, + universal_newlines=universal_newlines, + decode=decode, + ) if code != 0: - # error code can be recovered with .errno - raise OSError(code, f'{command}\nreturned: {decoded}') + feedback = message + '\n' if message else '' + feedback += f'failed to run command: {command}\n' + feedback += f'returned: {decoded}\n' + feedback += f'exit code: {code}' + if raising is None: + # error code can be recovered with .errno + raise OSError(code, feedback) + else: + raise raising(feedback) return decoded # file manipulation - -def subprocess_cmd(command): - """ execute arbitrary command via Popen """ - from subprocess import Popen, PIPE - p = Popen(command, stdout=PIPE, shell=True) - p.communicate() - - def read_file(path): """ Read a file to string """ with open(path, 'r') as f: @@ -224,12 +253,11 @@ def commit_in_progress(): # Since this will be used in scripts that modify the config outside of the CLI # framework, those knowingly have root permissions. # For everything else, we add a safeguard. - from subprocess import check_output from psutil import process_iter, NoSuchProcess from vyos.defaults import commit_lock - id = check_output(['/usr/bin/id', '-u']).decode().strip() - if id != '0': + idu = cmd('/usr/bin/id -u') + if idu != '0': raise OSError("This functions needs root permissions to return correct results") for proc in process_iter(): diff --git a/src/completion/list_dumpable_interfaces.py b/src/completion/list_dumpable_interfaces.py index 53ee89633..101c92fbe 100755 --- a/src/completion/list_dumpable_interfaces.py +++ b/src/completion/list_dumpable_interfaces.py @@ -3,12 +3,10 @@ # Extract the list of interfaces available for traffic dumps from tcpdump -D import re -import subprocess -if __name__ == '__main__': - out = subprocess.check_output(['/usr/sbin/tcpdump', '-D']).decode().strip() - out = out.split("\n") +from vyos.util import cmd +if __name__ == '__main__': + out = cmd('/usr/sbin/tcpdump -D').split('\n') intfs = " ".join(map(lambda s: re.search(r'\d+\.(\S+)\s', s).group(1), out)) - print(intfs) diff --git a/src/completion/list_local.py b/src/completion/list_local.py index ddb65c05c..40cc95f1e 100755 --- a/src/completion/list_local.py +++ b/src/completion/list_local.py @@ -2,7 +2,8 @@ import json import argparse -import subprocess + +from vyos.util import cmd # [{"ifindex":1,"ifname":"lo","flags":["LOOPBACK","UP","LOWER_UP"],"mtu":65536,"qdisc":"noqueue","operstate":"UNKNOWN","group":"default","txqlen":1000,"link_type":"loopback","address":"00:00:00:00:00:00","broadcast":"00:00:00:00:00:00","addr_info":[{"family":"inet","local":"127.0.0.1","prefixlen":8,"scope":"host","label":"lo","valid_life_time":4294967295,"preferred_life_time":4294967295},{"family":"inet6","local":"::1","prefixlen":128,"scope":"host","valid_life_time":4294967295,"preferred_life_time":4294967295}]},{"ifindex":2,"ifname":"eth0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"pfifo_fast","operstate":"UP","group":"default","txqlen":1000,"link_type":"ether","address":"08:00:27:fa:12:53","broadcast":"ff:ff:ff:ff:ff:ff","addr_info":[{"family":"inet","local":"10.0.2.15","prefixlen":24,"broadcast":"10.0.2.255","scope":"global","label":"eth0","valid_life_time":4294967295,"preferred_life_time":4294967295},{"family":"inet6","local":"fe80::a00:27ff:fefa:1253","prefixlen":64,"scope":"link","valid_life_time":4294967295,"preferred_life_time":4294967295}]},{"ifindex":3,"ifname":"eth1","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"pfifo_fast","operstate":"UP","group":"default","txqlen":1000,"link_type":"ether","address":"08:00:27:0d:25:dc","broadcast":"ff:ff:ff:ff:ff:ff","addr_info":[{"family":"inet6","local":"fe80::a00:27ff:fe0d:25dc","prefixlen":64,"scope":"link","valid_life_time":4294967295,"preferred_life_time":4294967295}]},{"ifindex":4,"ifname":"eth2","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"pfifo_fast","operstate":"UP","group":"default","txqlen":1000,"link_type":"ether","address":"08:00:27:68:d0:b1","broadcast":"ff:ff:ff:ff:ff:ff","addr_info":[{"family":"inet6","local":"fe80::a00:27ff:fe68:d0b1","prefixlen":64,"scope":"link","valid_life_time":4294967295,"preferred_life_time":4294967295}]},{"ifindex":5,"ifname":"eth3","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"pfifo_fast","operstate":"UP","group":"default","txqlen":1000,"link_type":"ether","address":"08:00:27:f0:17:c5","broadcast":"ff:ff:ff:ff:ff:ff","addr_info":[{"family":"inet6","local":"fe80::a00:27ff:fef0:17c5","prefixlen":64,"scope":"link","valid_life_time":4294967295,"preferred_life_time":4294967295}]}] @@ -10,8 +11,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() - cmd = 'ip -j address show' - out = subprocess.check_output(cmd.split()).decode().strip() + out = cmd('ip -j address show') data = json.loads(out) diff --git a/src/conf_mode/accel_l2tp.py b/src/conf_mode/accel_l2tp.py index 77e1ee874..4ca5a858a 100755 --- a/src/conf_mode/accel_l2tp.py +++ b/src/conf_mode/accel_l2tp.py @@ -17,7 +17,7 @@ import sys import os import re -import subprocess +import jinja2 import socket import time @@ -26,6 +26,8 @@ from jinja2 import FileSystemLoader, Environment from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + pidfile = r'/var/run/accel_l2tp.pid' l2tp_cnf_dir = r'/etc/accel-ppp/l2tp' @@ -61,17 +63,10 @@ def chk_con(): break -def accel_cmd(cmd=''): - if not cmd: - return None - try: - ret = subprocess.check_output( - ['/usr/bin/accel-cmd', '-p', '2004', cmd]).decode().strip() - return ret - except: - return 1 +def _accel_cmd(command): + return run(f'/usr/bin/accel-cmd -p 2004 {command}') -### +### # inline helper functions end ### @@ -375,21 +370,20 @@ def generate(c): def apply(c): if c == None: if os.path.exists(pidfile): - accel_cmd('shutdown hard') + _accel_cmd('shutdown hard') if os.path.exists(pidfile): os.remove(pidfile) return None if not os.path.exists(pidfile): - ret = subprocess.call( - ['/usr/sbin/accel-pppd', '-c', l2tp_conf, '-p', pidfile, '-d']) + ret = run(f'/usr/sbin/accel-pppd -c {l2tp_conf} -p {pidfile} -d') chk_con() if ret != 0 and os.path.exists(pidfile): os.remove(pidfile) raise ConfigError('accel-pppd failed to start') else: # if gw ip changes, only restart doesn't work - accel_cmd('restart') + _accel_cmd('restart') if __name__ == '__main__': diff --git a/src/conf_mode/arp.py b/src/conf_mode/arp.py index aeca08432..3daa892d7 100755 --- a/src/conf_mode/arp.py +++ b/src/conf_mode/arp.py @@ -20,9 +20,9 @@ import sys import os import re import syslog as sl -import subprocess from vyos.config import Config +from vyos.util import run from vyos import ConfigError arp_cmd = '/usr/sbin/arp' @@ -82,11 +82,12 @@ def generate(c): def apply(c): for ip_addr in c['remove']: sl.syslog(sl.LOG_NOTICE, "arp -d " + ip_addr) - subprocess.call([arp_cmd + ' -d ' + ip_addr + ' >/dev/null 2>&1'], shell=True) + run(f'{arp_cmd} -d {ip_addr} >/dev/null 2>&1') for ip_addr in c['update']: sl.syslog(sl.LOG_NOTICE, "arp -s " + ip_addr + " " + c['update'][ip_addr]) - subprocess.call([arp_cmd + ' -s ' + ip_addr + ' ' + c['update'][ip_addr] ], shell=True) + updated = c['update'][ip_addr] + run(f'{arp_cmd} -s {ip_addr} {updated}') if __name__ == '__main__': diff --git a/src/conf_mode/bcast_relay.py b/src/conf_mode/bcast_relay.py index 96576ddd4..f6d90776c 100755 --- a/src/conf_mode/bcast_relay.py +++ b/src/conf_mode/bcast_relay.py @@ -24,6 +24,7 @@ from jinja2 import FileSystemLoader, Environment from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run config_file = r'/etc/default/udp-broadcast-relay' @@ -154,7 +155,7 @@ def generate(relay): def apply(relay): # first stop all running services - os.system('sudo systemctl stop udp-broadcast-relay@{1..99}') + run('sudo systemctl stop udp-broadcast-relay@{1..99}') if (relay is None) or relay['disabled']: return None @@ -164,7 +165,7 @@ def apply(relay): # Don't start individual instance when it's disabled if r['disabled']: continue - os.system('sudo systemctl start udp-broadcast-relay@{0}'.format(r['id'])) + run('sudo systemctl start udp-broadcast-relay@{0}'.format(r['id'])) return None diff --git a/src/conf_mode/dhcp_relay.py b/src/conf_mode/dhcp_relay.py index 6f8d66e7b..1d6d4c6e3 100755 --- a/src/conf_mode/dhcp_relay.py +++ b/src/conf_mode/dhcp_relay.py @@ -22,6 +22,7 @@ from sys import exit from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run config_file = r'/etc/default/isc-dhcp-relay' @@ -112,10 +113,10 @@ def generate(relay): def apply(relay): if relay is not None: - os.system('sudo systemctl restart isc-dhcp-relay.service') + run('sudo systemctl restart isc-dhcp-relay.service') else: # DHCP relay support is removed in the commit - os.system('sudo systemctl stop isc-dhcp-relay.service') + run('sudo systemctl stop isc-dhcp-relay.service') os.unlink(config_file) return None diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index 3d75414f5..69aebe2f4 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -26,6 +26,8 @@ from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos.validate import is_subnet_connected from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/dhcp/dhcpd.conf' lease_file = r'/config/dhcpd.leases' @@ -626,7 +628,7 @@ def generate(dhcp): def apply(dhcp): if (dhcp is None) or dhcp['disabled']: # DHCP server is removed in the commit - os.system('sudo systemctl stop isc-dhcpv4-server.service') + run('sudo systemctl stop isc-dhcpv4-server.service') if os.path.exists(config_file): os.unlink(config_file) if os.path.exists(daemon_config_file): @@ -636,7 +638,7 @@ def apply(dhcp): if not os.path.exists(lease_file): os.mknod(lease_file) - os.system('sudo systemctl restart isc-dhcpv4-server.service') + run('sudo systemctl restart isc-dhcpv4-server.service') return None diff --git a/src/conf_mode/dhcpv6_relay.py b/src/conf_mode/dhcpv6_relay.py index d942daf37..a67deb6c7 100755 --- a/src/conf_mode/dhcpv6_relay.py +++ b/src/conf_mode/dhcpv6_relay.py @@ -23,6 +23,8 @@ from jinja2 import FileSystemLoader, Environment from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/default/isc-dhcpv6-relay' @@ -98,10 +100,10 @@ def generate(relay): def apply(relay): if relay is not None: - os.system('sudo systemctl restart isc-dhcpv6-relay.service') + run('sudo systemctl restart isc-dhcpv6-relay.service') else: # DHCPv6 relay support is removed in the commit - os.system('sudo systemctl stop isc-dhcpv6-relay.service') + run('sudo systemctl stop isc-dhcpv6-relay.service') os.unlink(config_file) return None diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py index 10b40baa4..003e80915 100755 --- a/src/conf_mode/dhcpv6_server.py +++ b/src/conf_mode/dhcpv6_server.py @@ -25,6 +25,8 @@ from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos.validate import is_subnet_connected from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/dhcp/dhcpdv6.conf' lease_file = r'/config/dhcpdv6.leases' @@ -362,7 +364,7 @@ def generate(dhcpv6): def apply(dhcpv6): if (dhcpv6 is None) or dhcpv6['disabled']: # DHCP server is removed in the commit - os.system('sudo systemctl stop isc-dhcpv6-server.service') + run('sudo systemctl stop isc-dhcpv6-server.service') if os.path.exists(config_file): os.unlink(config_file) if os.path.exists(daemon_config_file): @@ -372,7 +374,7 @@ def apply(dhcpv6): if not os.path.exists(lease_file): os.mknod(lease_file) - os.system('sudo systemctl restart isc-dhcpv6-server.service') + run('sudo systemctl restart isc-dhcpv6-server.service') return None diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index bbb69cdf7..5dc599425 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -26,6 +26,7 @@ from vyos.defaults import directories as vyos_data_dir from vyos.hostsd_client import Client as hostsd_client from vyos.util import wait_for_commit_lock from vyos import ConfigError +from vyos.util import run parser = argparse.ArgumentParser() parser.add_argument("--dhclient", action="store_true", @@ -166,11 +167,11 @@ def generate(dns): def apply(dns): if dns is None: # DNS forwarding is removed in the commit - os.system("systemctl stop pdns-recursor") + run("systemctl stop pdns-recursor") if os.path.isfile(config_file): os.unlink(config_file) else: - os.system("systemctl restart pdns-recursor") + run("systemctl restart pdns-recursor") if __name__ == '__main__': args = parser.parse_args() diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py index 56ce4fedc..b9163f7b3 100755 --- a/src/conf_mode/dynamic_dns.py +++ b/src/conf_mode/dynamic_dns.py @@ -24,6 +24,8 @@ from stat import S_IRUSR, S_IWUSR from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/ddclient/ddclient.conf' cache_file = r'/var/cache/ddclient/ddclient.cache' @@ -255,11 +257,11 @@ def apply(dyndns): os.unlink('/etc/ddclient.conf') if dyndns['deleted']: - os.system('/etc/init.d/ddclient stop') + run('/etc/init.d/ddclient stop') if os.path.exists(dyndns['pid_file']): os.unlink(dyndns['pid_file']) else: - os.system('/etc/init.d/ddclient restart') + run('/etc/init.d/ddclient restart') return None diff --git a/src/conf_mode/firewall_options.py b/src/conf_mode/firewall_options.py index 2be80cdbf..90f004bc4 100755 --- a/src/conf_mode/firewall_options.py +++ b/src/conf_mode/firewall_options.py @@ -21,6 +21,8 @@ import copy from vyos.config import Config from vyos import ConfigError +from vyos.util import run + default_config_data = { 'intf_opts': [], @@ -85,19 +87,19 @@ def apply(tcp): target = 'VYOS_FW_OPTIONS' # always cleanup iptables - os.system('iptables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) - os.system('iptables --table mangle --flush {} >&/dev/null'.format(target)) - os.system('iptables --table mangle --delete-chain {} >&/dev/null'.format(target)) + run('iptables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) + run('iptables --table mangle --flush {} >&/dev/null'.format(target)) + run('iptables --table mangle --delete-chain {} >&/dev/null'.format(target)) # always cleanup ip6tables - os.system('ip6tables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) - os.system('ip6tables --table mangle --flush {} >&/dev/null'.format(target)) - os.system('ip6tables --table mangle --delete-chain {} >&/dev/null'.format(target)) + run('ip6tables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) + run('ip6tables --table mangle --flush {} >&/dev/null'.format(target)) + run('ip6tables --table mangle --delete-chain {} >&/dev/null'.format(target)) # Setup new iptables rules if tcp['new_chain4']: - os.system('iptables --table mangle --new-chain {} >&/dev/null'.format(target)) - os.system('iptables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) + run('iptables --table mangle --new-chain {} >&/dev/null'.format(target)) + run('iptables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) for opts in tcp['intf_opts']: intf = opts['intf'] @@ -109,13 +111,13 @@ def apply(tcp): # adjust TCP MSS per interface if mss: - os.system('iptables --table mangle --append {} --out-interface {} --protocol tcp ' \ + run('iptables --table mangle --append {} --out-interface {} --protocol tcp ' \ '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) # Setup new ip6tables rules if tcp['new_chain6']: - os.system('ip6tables --table mangle --new-chain {} >&/dev/null'.format(target)) - os.system('ip6tables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) + run('ip6tables --table mangle --new-chain {} >&/dev/null'.format(target)) + run('ip6tables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) for opts in tcp['intf_opts']: intf = opts['intf'] @@ -127,8 +129,8 @@ def apply(tcp): # adjust TCP MSS per interface if mss: - os.system('ip6tables --table mangle --append {} --out-interface {} --protocol tcp ' \ - '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) + run('ip6tables --table mangle --append {} --out-interface {} --protocol tcp ' + '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) return None diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index b040c8b64..1008f3fae 100755 --- a/src/conf_mode/flow_accounting_conf.py +++ b/src/conf_mode/flow_accounting_conf.py @@ -16,7 +16,7 @@ import os import re -import subprocess +import ipaddress from ipaddress import ip_address from jinja2 import FileSystemLoader, Environment @@ -26,6 +26,8 @@ from vyos.ifconfig import Interface from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import cmd + # default values default_sflow_server_port = 6343 @@ -80,11 +82,7 @@ def _iptables_get_nflog(): for iptables_variant in ['iptables', 'ip6tables']: # run iptables, save output and split it by lines iptables_command = "sudo {0} -t {1} -S {2}".format(iptables_variant, iptables_nflog_table, iptables_nflog_chain) - process = subprocess.Popen(iptables_command, stdout=subprocess.PIPE, shell=True, universal_newlines=True) - stdout, stderr = process.communicate() - if not process.returncode == 0: - print("Failed to get flows list: command \"{}\" returned exit code: {}\nError: {}".format(command, process.returncode, stderr)) - exit(1) + cmd(iptables_command, universal_newlines=True, message='Failed to get flows list') iptables_out = stdout.splitlines() # parse each line and add information to list @@ -113,10 +111,17 @@ def _iptables_config(configured_ifaces): # compare current active list with configured one and delete excessive interfaces, add missed active_nflog_ifaces = [] for rule in active_nflog_rules: - if rule['interface'] not in configured_ifaces: - iptable_commands.append("sudo {0} -t {1} -D {2}".format(rule['iptables_variant'], rule['table'], rule['rule_definition'])) + iptables = rule['iptables_variant'] + interface = rule['interface'] + if interface not in configured_ifaces: + table = rule['table'] + rule = rule['rule_definition'] + iptable_commands.append(f'sudo {iptables} -t {table} -D {rule}') else: - active_nflog_ifaces.append({ 'iface': rule['interface'], 'iptables_variant': rule['iptables_variant'] }) + active_nflog_ifaces.append({ + 'iface': interface, + 'iptables_variant': iptables, + }) # do not create new rules for already configured interfaces for iface in active_nflog_ifaces: @@ -125,14 +130,14 @@ def _iptables_config(configured_ifaces): # create missed rules for iface_extended in configured_ifaces_extended: - rule_definition = "{0} -i {1} -m comment --comment FLOW_ACCOUNTING_RULE -j NFLOG --nflog-group 2 --nflog-size {2} --nflog-threshold 100".format(iptables_nflog_chain, iface_extended['iface'], default_captured_packet_size) - iptable_commands.append("sudo {0} -t {1} -I {2}".format(iface_extended['iptables_variant'], iptables_nflog_table, rule_definition)) + iface = iface_extended['iface'] + iptables = iface_extended['iptables_variant'] + rule_definition = f'{iptables_nflog_chain} -i {iface} -m comment --comment FLOW_ACCOUNTING_RULE -j NFLOG --nflog-group 2 --nflog-size {default_captured_packet_size} --nflog-threshold 100' + iptable_commands.append(f'sudo {iptables} -t {iptables_nflog_table} -I {rule_definition}') # change iptables for command in iptable_commands: - return_code = subprocess.call(command.split(' ')) - if not return_code == 0: - raise ConfigError("Failed to run command: {}\nExit code {}".format(command, return_code)) + cmd(command, raising=ConfigError) def get_config(): @@ -351,9 +356,7 @@ def apply(config): command = '/usr/bin/sudo /bin/systemctl restart uacctd' # run command to start or stop flow-accounting - return_code = subprocess.call(command.split(' ')) - if not return_code == 0: - raise ConfigError("Failed to start/stop flow-accounting: command {} returned exit code {}".format(command, return_code)) + cmd(command, raising=ConfigError, message='Failed to start/stop flow-accounting') # configure iptables rules for defined interfaces if config['interfaces']: diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py index 47cf232e9..690d1e030 100755 --- a/src/conf_mode/host_name.py +++ b/src/conf_mode/host_name.py @@ -25,7 +25,6 @@ import re import sys import copy import glob -import subprocess import argparse import jinja2 @@ -34,6 +33,7 @@ import vyos.hostsd_client from vyos.config import Config from vyos import ConfigError +from vyos.util import cmd, run default_config_data = { @@ -156,21 +156,22 @@ def apply(config): # rsyslog runs into a race condition at boot time with systemd # restart rsyslog only if the hostname changed. - hostname_old = subprocess.check_output(['hostnamectl', '--static']).decode().strip() - - os.system("hostnamectl set-hostname --static {0}".format(hostname_new)) + hostname_old = cmd('hostnamectl --static') + cmd(f'hostnamectl set-hostname --static {hostname_new}') # Restart services that use the hostname if hostname_new != hostname_old: - os.system("systemctl restart rsyslog.service") + run("systemctl restart rsyslog.service") # If SNMP is running, restart it too - if os.system("pgrep snmpd > /dev/null") == 0: - os.system("systemctl restart snmpd.service") + ret = run("pgrep snmpd > /dev/null") + if ret == 0: + run("systemctl restart snmpd.service") # restart pdns if it is used - if os.system("/usr/bin/rec_control ping >/dev/null 2>&1") == 0: - os.system("/etc/init.d/pdns-recursor restart >/dev/null") + ret = run('/usr/bin/rec_control ping >/dev/null 2>&1') + if ret == 0: + run('/etc/init.d/pdns-recursor restart >/dev/null') return None diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py index 0c2e029e9..91b8aa34b 100755 --- a/src/conf_mode/http-api.py +++ b/src/conf_mode/http-api.py @@ -18,13 +18,13 @@ import sys import os -import subprocess import json from copy import deepcopy import vyos.defaults from vyos.config import Config from vyos import ConfigError +from vyos.util import cmd, run config_file = '/etc/vyos/http-api.conf' @@ -91,16 +91,12 @@ def generate(http_api): def apply(http_api): if http_api is not None: - os.system('sudo systemctl restart vyos-http-api.service') + run('sudo systemctl restart vyos-http-api.service') else: - os.system('sudo systemctl stop vyos-http-api.service') + run('sudo systemctl stop vyos-http-api.service') for dep in dependencies: - cmd = '{0}/{1}'.format(vyos_conf_scripts_dir, dep) - try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as err: - raise ConfigError("{}.".format(err)) + cmd(f'{vyos_conf_scripts_dir}/{dep}', raising=ConfigError) if __name__ == '__main__': try: diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index 83a5f3602..e46f1a4e7 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -26,6 +26,8 @@ import vyos.certbot_util from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = '/etc/nginx/sites-available/default' @@ -144,9 +146,9 @@ def generate(https): def apply(https): if https is not None: - os.system('sudo systemctl restart nginx.service') + run('sudo systemctl restart nginx.service') else: - os.system('sudo systemctl stop nginx.service') + run('sudo systemctl stop nginx.service') if __name__ == '__main__': try: diff --git a/src/conf_mode/igmp_proxy.py b/src/conf_mode/igmp_proxy.py index aa46f2c4e..abe473530 100755 --- a/src/conf_mode/igmp_proxy.py +++ b/src/conf_mode/igmp_proxy.py @@ -24,6 +24,8 @@ from netifaces import interfaces from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/igmpproxy.conf' @@ -129,11 +131,11 @@ def generate(igmp_proxy): def apply(igmp_proxy): if igmp_proxy is None or igmp_proxy['disable']: # IGMP Proxy support is removed in the commit - os.system('sudo systemctl stop igmpproxy.service') + run('sudo systemctl stop igmpproxy.service') if os.path.exists(config_file): os.unlink(config_file) else: - os.system('systemctl restart igmpproxy.service') + run('systemctl restart igmpproxy.service') return None diff --git a/src/conf_mode/intel_qat.py b/src/conf_mode/intel_qat.py index a1abd5e81..cc7d4a915 100755 --- a/src/conf_mode/intel_qat.py +++ b/src/conf_mode/intel_qat.py @@ -19,10 +19,10 @@ import sys import os import re -import subprocess from vyos.config import Config from vyos import ConfigError +from vyos.util import popen, run # Define for recovering gl_ipsec_conf = None @@ -49,13 +49,10 @@ def get_config(): # Control configured VPN service which can use QAT def vpn_control(action): + # XXX: Should these commands report failure if action == 'restore' and gl_ipsec_conf: - ret = subprocess.Popen(['sudo', 'ipsec', 'start'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() - return - - ret = subprocess.Popen(['sudo', 'ipsec', action], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() + return run('sudo ipsec start') + return run(f'sudo ipsec {action}') def verify(c): # Check if QAT service installed @@ -66,10 +63,9 @@ def verify(c): return # Check if QAT device exist - ret = subprocess.Popen(['sudo', 'lspci', '-nn'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() + output, err = popen('sudo lspci -nn', decode='utf-8') if not err: - data = re.findall('(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)', output.decode("utf-8")) + data = re.findall('(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)', output) #If QAT devices found if not data: print("\t No QAT acceleration device found") @@ -82,17 +78,13 @@ def apply(c): # Disable QAT service if c['qat_conf'] == None: - ret = subprocess.Popen(['sudo', '/etc/init.d/vyos-qat-utilities', 'stop'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() + run('sudo /etc/init.d/vyos-qat-utilities stop') if c['ipsec_conf']: vpn_control('start') - return # Run qat init.d script - ret = subprocess.Popen(['sudo', '/etc/init.d/vyos-qat-utilities', 'start'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() - + run('sudo /etc/init.d/vyos-qat-utilities start') if c['ipsec_conf']: # Recovery VPN service vpn_control('start') diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index cc119b91a..19f43f725 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -25,6 +25,8 @@ from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config from vyos.configdict import list_diff, vlan_to_dict from vyos.config import Config from vyos import ConfigError +from vyos.util import run + default_config_data = { 'address': [], @@ -90,7 +92,7 @@ def get_config(): if not os.path.isfile('/sys/class/net/bonding_masters'): import syslog syslog.syslog(syslog.LOG_NOTICE, "loading bonding kernel module") - if os.system('modprobe bonding max_bonds=0 miimon=250') != 0: + if run('modprobe bonding max_bonds=0 miimon=250') != 0: syslog.syslog(syslog.LOG_NOTICE, "failed loading bonding kernel module") raise ConfigError("failed loading bonding kernel module") diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index af1d3f482..4b5fc8306 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -22,6 +22,7 @@ from copy import deepcopy from vyos.config import Config from vyos.ifconfig import L2TPv3If, Interface from vyos import ConfigError +from vyos.util import run from netifaces import interfaces default_config_data = { @@ -50,7 +51,7 @@ def check_kmod(): modules = ['l2tp_eth', 'l2tp_netlink', 'l2tp_ip', 'l2tp_ip6'] for module in modules: if not os.path.exists(f'/sys/module/{module}'): - if os.system(f'modprobe {module}') != 0: + if run(f'modprobe {module}') != 0: raise ConfigError(f'Loading Kernel module {module} failed') def get_config(): diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index faaee9ac0..1fe1143cd 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -25,14 +25,13 @@ from grp import getgrnam from ipaddress import ip_address,ip_network,IPv4Interface from netifaces import interfaces from pwd import getpwnam -from subprocess import Popen, PIPE from time import sleep from shutil import rmtree from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos.ifconfig import VTunIf -from vyos.util import process_running +from vyos.util import process_running, cmd from vyos.validate import is_addr_assigned from vyos import ConfigError @@ -96,9 +95,6 @@ default_config_data = { 'gid': group, } -def subprocess_cmd(command): - p = Popen(command, stdout=PIPE, shell=True) - p.communicate() def get_config_name(intf): cfg_file = r'/opt/vyatta/etc/openvpn/openvpn-{}.conf'.format(intf) @@ -744,12 +740,12 @@ def apply(openvpn): # service as the configuration is not re-read. Stop daemon only if it's # running - it could have died or killed by someone evil if process_running(pidfile): - cmd = 'start-stop-daemon' - cmd += ' --stop ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + pidfile - subprocess_cmd(cmd) + command = 'start-stop-daemon' + command += ' --stop ' + command += ' --quiet' + command += ' --oknodo' + command += ' --pidfile ' + pidfile + cmd(command) # cleanup old PID file if os.path.isfile(pidfile): @@ -780,19 +776,19 @@ def apply(openvpn): # No matching OpenVPN process running - maybe it got killed or none # existed - nevertheless, spawn new OpenVPN process - cmd = 'start-stop-daemon' - cmd += ' --start ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + pidfile - cmd += ' --exec /usr/sbin/openvpn' + command = 'start-stop-daemon' + command += ' --start ' + command += ' --quiet' + command += ' --oknodo' + command += ' --pidfile ' + pidfile + command += ' --exec /usr/sbin/openvpn' # now pass arguments to openvpn binary - cmd += ' --' - cmd += ' --daemon openvpn-' + openvpn['intf'] - cmd += ' --config ' + get_config_name(openvpn['intf']) + command += ' --' + command += ' --daemon openvpn-' + openvpn['intf'] + command += ' --config ' + get_config_name(openvpn['intf']) # execute assembled command - subprocess_cmd(cmd) + cmd(command) # better late then sorry ... but we can only set interface alias after # OpenVPN has been launched and created the interface diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index a396af4ea..407547175 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -24,7 +24,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos.ifconfig import Interface -from vyos.util import chown_file, chmod_x, subprocess_cmd +from vyos.util import chown_file, chmod_x, cmd from vyos import ConfigError default_config_data = { @@ -182,8 +182,7 @@ def generate(pppoe): os.mkdir(dirname) # Always hang-up PPPoE connection prior generating new configuration file - cmd = f'systemctl stop ppp@{intf}.service' - subprocess_cmd(cmd) + cmd(f'systemctl stop ppp@{intf}.service') if pppoe['deleted']: # Delete PPP configuration files @@ -238,8 +237,7 @@ def apply(pppoe): if not pppoe['disable']: # "dial" PPPoE connection intf = pppoe['intf'] - cmd = f'systemctl start ppp@{intf}.service' - subprocess_cmd(cmd) + cmd(f'systemctl start ppp@{intf}.service') # make logfile owned by root / vyattacfg chown_file(pppoe['logfile'], 'root', 'vyattacfg') diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 5c0c07dc4..4fa0dd8c0 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -17,7 +17,6 @@ import sys import os import re -import subprocess from copy import deepcopy from netifaces import interfaces @@ -25,27 +24,26 @@ from netifaces import interfaces from vyos import ConfigError from vyos.config import Config from vyos.configdict import list_diff +from vyos.util import run from vyos.ifconfig import WireGuardIf kdir = r'/config/auth/wireguard' def _check_kmod(): if not os.path.exists('/sys/module/wireguard'): - if os.system('modprobe wireguard') != 0: + if run('modprobe wireguard') != 0: raise ConfigError("modprobe wireguard failed") def _migrate_default_keys(): - if os.path.exists('{}/private.key'.format(kdir)) and not os.path.exists('{}/default/private.key'.format(kdir)): + if os.path.exists(f'{kdir}/private.key') and not os.path.exists(f'{kdir}/default/private.key'): old_umask = os.umask(0o027) - location = '{}/default'.format(kdir) - subprocess.call(['sudo mkdir -p ' + location], shell=True) - subprocess.call(['sudo chgrp vyattacfg ' + location], shell=True) - subprocess.call(['sudo chmod 750 ' + location], shell=True) - os.rename('{}/private.key'.format(kdir), - '{}/private.key'.format(location)) - os.rename('{}/public.key'.format(kdir), - '{}/public.key'.format(location)) + location = f'{kdir}/default' + run(f'sudo mkdir -p {location}') + run(f'sudo chgrp vyattacfg {location}') + run(f'sudo chmod 750 {location}') + os.rename(f'{kdir}/private.key', f'{location}/private.key') + os.rename(f'{kdir}/public.key', f'{location}/public.key') os.umask(old_umask) @@ -208,8 +206,8 @@ def apply(c): if re.search("DEVTYPE=wireguard", buf, re.I | re.M): wg_intf = re.sub("INTERFACE=", "", re.search( "INTERFACE=.*", buf, re.I | re.M).group(0)) - subprocess.call( - ['ip l d dev ' + wg_intf + ' >/dev/null'], shell=True) + # XXX: we are ignoring any errors here + run(f'ip l d dev {wg_intf} >/dev/null') return None # init wg class diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index da8470f7e..188d0ee22 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -21,7 +21,6 @@ from re import findall from copy import deepcopy from jinja2 import FileSystemLoader, Environment -from subprocess import Popen, PIPE from netifaces import interfaces from netaddr import EUI, mac_unix_expanded @@ -30,7 +29,7 @@ from vyos.configdict import list_diff, vlan_to_dict from vyos.defaults import directories as vyos_data_dir from vyos.ifconfig import WiFiIf from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config -from vyos.util import process_running, chmod_x, chown_file +from vyos.util import process_running, chmod_x, chown_file, run from vyos import ConfigError user = 'root' @@ -152,9 +151,6 @@ def get_wpa_suppl_config_name(intf): cfg_file = cfg_dir + r'/{}.cfg'.format(intf) return cfg_file -def subprocess_cmd(command): - p = Popen(command, stdout=PIPE, shell=True) - p.communicate() def get_config(): wifi = deepcopy(default_config_data) @@ -631,22 +627,22 @@ def generate(wifi): # always stop hostapd service first before reconfiguring it pidfile = get_pid('hostapd', wifi['intf']) if process_running(pidfile): - cmd = 'start-stop-daemon' - cmd += ' --stop ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + pidfile - subprocess_cmd(cmd) + command = 'start-stop-daemon' + command += ' --stop ' + command += ' --quiet' + command += ' --oknodo' + command += ' --pidfile ' + pidfile + run(command) # always stop wpa_supplicant service first before reconfiguring it pidfile = get_pid('wpa_supplicant', wifi['intf']) if process_running(pidfile): - cmd = 'start-stop-daemon' - cmd += ' --stop ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + pidfile - subprocess_cmd(cmd) + command = 'start-stop-daemon' + command += ' --stop ' + command += ' --quiet' + command += ' --oknodo' + command += ' --pidfile ' + pidfile + run(command) # Delete config files if interface is removed if wifi['deleted']: @@ -807,37 +803,38 @@ def apply(wifi): # Physical interface is now configured. Proceed by starting hostapd or # wpa_supplicant daemon. When type is monitor we can just skip this. if wifi['op_mode'] == 'ap': - cmd = 'start-stop-daemon' - cmd += ' --start ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + get_pid('hostapd', wifi['intf']) - cmd += ' --exec /usr/sbin/hostapd' + command = 'start-stop-daemon' + command += ' --start ' + command += ' --quiet' + command += ' --oknodo' + command += ' --pidfile ' + get_pid('hostapd', wifi['intf']) + command += ' --exec /usr/sbin/hostapd' # now pass arguments to hostapd binary - cmd += ' -- ' - cmd += ' -B' - cmd += ' -P ' + get_pid('hostapd', wifi['intf']) - cmd += ' ' + get_conf_file('hostapd', wifi['intf']) + command += ' -- ' + command += ' -B' + command += ' -P ' + get_pid('hostapd', wifi['intf']) + command += ' ' + get_conf_file('hostapd', wifi['intf']) # execute assembled command - subprocess_cmd(cmd) + run(command) elif wifi['op_mode'] == 'station': - cmd = 'start-stop-daemon' - cmd += ' --start ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + get_pid('hostapd', wifi['intf']) - cmd += ' --exec /sbin/wpa_supplicant' + command = 'start-stop-daemon' + command += ' --start ' + command += ' --quiet' + command += ' --oknodo' + command += ' --pidfile ' + get_pid('hostapd', wifi['intf']) + command += ' --exec /sbin/wpa_supplicant' # now pass arguments to hostapd binary - cmd += ' -- ' - cmd += ' -s -B -D nl80211' - cmd += ' -P ' + get_pid('wpa_supplicant', wifi['intf']) - cmd += ' -i ' + wifi['intf'] - cmd += ' -c ' + get_conf_file('wpa_supplicant', wifi['intf']) + command += ' -- ' + command += ' -s -B -D nl80211' + command += ' -P ' + get_pid('wpa_supplicant', wifi['intf']) + command += ' -i ' + wifi['intf'] + command += ' -c ' + \ + get_conf_file('wpa_supplicant', wifi['intf']) # execute assembled command - subprocess_cmd(cmd) + run(command) return None diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py index da33d54e4..5e10cfce7 100755 --- a/src/conf_mode/interfaces-wirelessmodem.py +++ b/src/conf_mode/interfaces-wirelessmodem.py @@ -23,7 +23,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.defaults import directories as vyos_data_dir -from vyos.util import chown_file, chmod_x, subprocess_cmd +from vyos.util import chown_file, chmod_x, cmd, run from vyos import ConfigError default_config_data = { @@ -48,7 +48,7 @@ def check_kmod(): modules = ['option', 'usb_wwan', 'usbserial'] for module in modules: if not os.path.exists(f'/sys/module/{module}'): - if os.system(f'modprobe {module}') != 0: + if run(f'modprobe {module}') != 0: raise ConfigError(f'Loading Kernel module {module} failed') def get_config(): @@ -156,8 +156,7 @@ def generate(wwan): os.mkdir(dirname) # Always hang-up WWAN connection prior generating new configuration file - cmd = f'systemctl stop ppp@{intf}.service' - subprocess_cmd(cmd) + cmd(f'systemctl stop ppp@{intf}.service') if wwan['deleted']: # Delete PPP configuration files @@ -211,9 +210,7 @@ def apply(wwan): if not wwan['disable']: # "dial" WWAN connection intf = wwan['intf'] - cmd = f'systemctl start ppp@{intf}.service' - subprocess_cmd(cmd) - + cmd(f'systemctl start ppp@{intf}.service') # make logfile owned by root / vyattacfg chown_file(wwan['logfile'], 'root', 'vyattacfg') diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py index 90b6b0d57..c2f5c8e07 100755 --- a/src/conf_mode/ipsec-settings.py +++ b/src/conf_mode/ipsec-settings.py @@ -24,6 +24,7 @@ from sys import exit from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run ra_conn_name = "remote-access" charon_conf_file = "/etc/strongswan.d/charon.conf" @@ -98,7 +99,7 @@ def get_config(): ### Remove config from file by delimiter def remove_confs(delim_begin, delim_end, conf_file): - os.system("sed -i '/"+delim_begin+"/,/"+delim_end+"/d' "+conf_file) + run("sed -i '/"+delim_begin+"/,/"+delim_end+"/d' "+conf_file) ### Checking certificate storage and notice if certificate not in /config directory @@ -111,7 +112,7 @@ def check_cert_file_store(cert_name, file_path, dts_path): else: ### Cpy file to /etc/ipsec.d/certs/ /etc/ipsec.d/cacerts/ # todo make check - ret = os.system('cp -f '+file_path+' '+dts_path) + ret = run('cp -f '+file_path+' '+dts_path) if ret: raise ConfigError("L2TP VPN configuration error: Cannot copy "+file_path) @@ -192,12 +193,12 @@ def generate(data): remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_flie) def restart_ipsec(): - os.system('ipsec restart >&/dev/null') + run('ipsec restart >&/dev/null') # counter for apply swanctl config counter = 10 while counter <= 10: if os.path.exists(charon_pidfile): - os.system('swanctl -q >&/dev/null') + run('swanctl -q >&/dev/null') break counter -=1 sleep(1) diff --git a/src/conf_mode/le_cert.py b/src/conf_mode/le_cert.py index c657098e1..a4dbecbaa 100755 --- a/src/conf_mode/le_cert.py +++ b/src/conf_mode/le_cert.py @@ -18,11 +18,12 @@ import sys import os -import subprocess import vyos.defaults from vyos.config import Config from vyos import ConfigError +from vyos.util import cmd, run + vyos_conf_scripts_dir = vyos.defaults.directories['conf_mode'] @@ -45,9 +46,9 @@ def request_certbot(cert): certbot_cmd = 'certbot certonly -n --nginx --agree-tos --no-eff-email --expand {0} {1}'.format(email_flag, domain_flag) - completed = subprocess.run(certbot_cmd, shell=True) - - return completed.returncode + cmd(certbot_cmd, + raising=ConfigError, + message="The certbot request failed for the specified domains.") def get_config(): conf = Config() @@ -84,28 +85,21 @@ def generate(cert): # certbot will attempt to reload nginx, even with 'certonly'; # start nginx if not active - ret = os.system('systemctl is-active --quiet nginx.ervice') + ret = run('systemctl is-active --quiet nginx.ervice') if ret: - os.system('sudo systemctl start nginx.service') + run('sudo systemctl start nginx.service') - ret = request_certbot(cert) - if ret: - raise ConfigError("The certbot request failed for the" - " specified domains.") + request_certbot(cert) def apply(cert): if cert is not None: - os.system('sudo systemctl restart certbot.timer') + run('sudo systemctl restart certbot.timer') else: - os.system('sudo systemctl stop certbot.timer') + run('sudo systemctl stop certbot.timer') return None for dep in dependencies: - cmd = '{0}/{1}'.format(vyos_conf_scripts_dir, dep) - try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as err: - raise ConfigError(str(err)) + cmd(f'{vyos_conf_scripts_dir}/{dep}', raising=ConfigError) if __name__ == '__main__': try: diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 4e3dfc0b6..c090bba83 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -26,6 +26,8 @@ from vyos.validate import is_addr_assigned,is_loopback_addr from vyos.defaults import directories as vyos_data_dir from vyos.version import get_version_data from vyos import ConfigError +from vyos.util import run + config_file = "/etc/default/lldpd" vyos_config_file = "/etc/lldpd.d/01-vyos.conf" @@ -239,10 +241,10 @@ def generate(lldp): def apply(lldp): if lldp: # start/restart lldp service - os.system('sudo systemctl restart lldpd.service') + run('sudo systemctl restart lldpd.service') else: # LLDP service has been terminated - os.system('sudo systemctl stop lldpd.service') + run('sudo systemctl stop lldpd.service') os.unlink(config_file) os.unlink(vyos_config_file) diff --git a/src/conf_mode/mdns_repeater.py b/src/conf_mode/mdns_repeater.py index f738cc6a6..2bccd9153 100755 --- a/src/conf_mode/mdns_repeater.py +++ b/src/conf_mode/mdns_repeater.py @@ -24,6 +24,8 @@ from netifaces import ifaddresses, AF_INET from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/default/mdns-repeater' @@ -94,11 +96,11 @@ def generate(mdns): def apply(mdns): if (mdns is None) or mdns['disabled']: - os.system('sudo systemctl stop mdns-repeater') + run('sudo systemctl stop mdns-repeater') if os.path.exists(config_file): os.unlink(config_file) else: - os.system('sudo systemctl restart mdns-repeater') + run('sudo systemctl restart mdns-repeater') return None diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py index 0f635556b..998022a8c 100755 --- a/src/conf_mode/ntp.py +++ b/src/conf_mode/ntp.py @@ -24,6 +24,8 @@ from sys import exit from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/ntp.conf' @@ -112,10 +114,10 @@ def generate(ntp): def apply(ntp): if ntp is not None: - os.system('sudo systemctl restart ntp.service') + run('sudo systemctl restart ntp.service') else: # NTP support is removed in the commit - os.system('sudo systemctl stop ntp.service') + run('sudo systemctl stop ntp.service') os.unlink(config_file) return None diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 9940c80c5..52a9e54c2 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -24,6 +24,8 @@ from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos.validate import is_ipv6_link_local, is_ipv6 from vyos import ConfigError +from vyos.util import run + config_file = r'/tmp/bfd.frr' @@ -205,7 +207,16 @@ def apply(bfd): if bfd is None: return None +<<<<<<< HEAD os.system(f'vtysh -d bfdd -f {config_file}') +======= + tmpl = jinja2.Template(config_tmpl) + config_text = tmpl.render(bfd) + with open(config_file, 'w') as f: + f.write(config_text) + + run("sudo vtysh -d bfdd -f " + config_file) +>>>>>>> util: T2226: covert most calls from os.system to util if os.path.exists(config_file): os.remove(config_file) diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py index 0148b5dac..6e819a15a 100755 --- a/src/conf_mode/protocols_igmp.py +++ b/src/conf_mode/protocols_igmp.py @@ -23,6 +23,8 @@ from sys import exit from vyos import ConfigError from vyos.config import Config from vyos.defaults import directories as vyos_data_dir +from vyos.util import run + config_file = r'/tmp/igmp.frr' @@ -103,7 +105,7 @@ def apply(igmp): return None if os.path.exists(config_file): - os.system("sudo vtysh -d pimd -f " + config_file) + run("sudo vtysh -d pimd -f " + config_file) os.remove(config_file) return None diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py index 514fe5efb..6e5d08397 100755 --- a/src/conf_mode/protocols_mpls.py +++ b/src/conf_mode/protocols_mpls.py @@ -21,11 +21,13 @@ from jinja2 import FileSystemLoader, Environment from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/tmp/ldpd.frr' def sysctl(name, value): - os.system('sysctl -wq {}={}'.format(name, value)) + run('sysctl -wq {}={}'.format(name, value)) def get_config(): conf = Config() @@ -160,7 +162,7 @@ def apply(mpls): operate_mpls_on_intfc(diactive_ifaces, 0) if os.path.exists(config_file): - os.system("sudo vtysh -d ldpd -f " + config_file) + run("sudo vtysh -d ldpd -f " + config_file) os.remove(config_file) return None diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py index 7b360d62c..0e22d3a6b 100755 --- a/src/conf_mode/protocols_pim.py +++ b/src/conf_mode/protocols_pim.py @@ -23,6 +23,8 @@ from sys import exit from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/tmp/pimd.frr' @@ -130,7 +132,11 @@ def apply(pim): return None if os.path.exists(config_file): +<<<<<<< HEAD os.system("vtysh -d pimd -f " + config_file) +======= + run("sudo vtysh -d pimd -f " + config_file) +>>>>>>> util: T2226: covert most calls from os.system to util os.remove(config_file) return None diff --git a/src/conf_mode/salt-minion.py b/src/conf_mode/salt-minion.py index bc1767454..bd1d44bc8 100755 --- a/src/conf_mode/salt-minion.py +++ b/src/conf_mode/salt-minion.py @@ -26,6 +26,8 @@ from urllib3 import PoolManager from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/salt/minion' @@ -124,10 +126,10 @@ def generate(salt): def apply(salt): if salt is not None: - os.system("sudo systemctl restart salt-minion") + run("sudo systemctl restart salt-minion") else: # Salt access is removed in the commit - os.system("sudo systemctl stop salt-minion") + run("sudo systemctl stop salt-minion") os.unlink(config_file) return None diff --git a/src/conf_mode/service-ipoe.py b/src/conf_mode/service-ipoe.py index dd9616a62..5bd4aea2e 100755 --- a/src/conf_mode/service-ipoe.py +++ b/src/conf_mode/service-ipoe.py @@ -16,7 +16,6 @@ import os import re -import subprocess from jinja2 import FileSystemLoader, Environment from socket import socket, AF_INET, SOCK_STREAM @@ -26,6 +25,7 @@ from time import sleep from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run ipoe_cnf_dir = r'/etc/accel-ppp/ipoe' ipoe_cnf = ipoe_cnf_dir + r'/ipoe.config' @@ -64,15 +64,8 @@ def _chk_con(): break -def _accel_cmd(cmd=''): - if not cmd: - return None - try: - ret = subprocess.check_output( - ['/usr/bin/accel-cmd', '-p', cmd_port, cmd]).decode().strip() - return ret - except: - return 1 +def _accel_cmd(command): + return run('/usr/bin/accel-cmd -p {cmd_port} {command}') ##### Inline functions end #### @@ -306,8 +299,7 @@ def apply(c): return None if not os.path.exists(pidfile): - ret = subprocess.call( - ['/usr/sbin/accel-pppd', '-c', ipoe_cnf, '-p', pidfile, '-d']) + ret = run(f'/usr/sbin/accel-pppd -c {ipoe_cnf} -p {pidfile} -d') _chk_con() if ret != 0 and os.path.exists(pidfile): os.remove(pidfile) diff --git a/src/conf_mode/service-pppoe.py b/src/conf_mode/service-pppoe.py index afcc5ba99..d3fc82406 100755 --- a/src/conf_mode/service-pppoe.py +++ b/src/conf_mode/service-pppoe.py @@ -16,7 +16,6 @@ import os import re -import subprocess from jinja2 import FileSystemLoader, Environment from socket import socket, AF_INET, SOCK_STREAM @@ -26,6 +25,7 @@ from time import sleep from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run pidfile = r'/var/run/accel_pppoe.pid' pppoe_cnf_dir = r'/etc/accel-ppp/pppoe' @@ -57,15 +57,8 @@ def _chk_con(): raise("failed to start pppoe server") -def _accel_cmd(cmd=''): - if not cmd: - return None - try: - ret = subprocess.check_output( - ['/usr/bin/accel-cmd', cmd]).decode().strip() - return ret - except: - return 1 +def _accel_cmd(command): + return run(f'/usr/bin/accel-cmd {command}') def get_config(): @@ -426,8 +419,7 @@ def apply(c): return None if not os.path.exists(pidfile): - ret = subprocess.call( - ['/usr/sbin/accel-pppd', '-c', pppoe_conf, '-p', pidfile, '-d']) + ret = run(f'/usr/sbin/accel-pppd -c {pppoe_conf} -p {pidfile} -d') _chk_con() if ret != 0 and os.path.exists(pidfile): os.remove(pidfile) diff --git a/src/conf_mode/service-router-advert.py b/src/conf_mode/service-router-advert.py index 38c5cb2dc..0173b7242 100755 --- a/src/conf_mode/service-router-advert.py +++ b/src/conf_mode/service-router-advert.py @@ -23,6 +23,8 @@ from sys import exit from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/radvd.conf' @@ -156,13 +158,13 @@ def generate(rtradv): def apply(rtradv): if not rtradv['interfaces']: # bail out early - looks like removal from running config - os.system('systemctl stop radvd.service') + run('systemctl stop radvd.service') if os.path.exists(config_file): os.unlink(config_file) return None - os.system('systemctl restart radvd.service') + run('systemctl restart radvd.service') return None if __name__ == '__main__': diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index ed8c1d7e1..414236c88 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -27,6 +27,8 @@ from vyos.defaults import directories as vyos_data_dir from vyos.validate import is_ipv4, is_addr_assigned from vyos.version import get_version_data from vyos import ConfigError +from vyos.util import run + config_file_client = r'/etc/snmp/snmp.conf' config_file_daemon = r'/etc/snmp/snmpd.conf' @@ -507,7 +509,7 @@ def generate(snmp): # # As we are manipulating the snmpd user database we have to stop it first! # This is even save if service is going to be removed - os.system("systemctl stop snmpd.service") + run('systemctl stop snmpd.service') config_files = [config_file_client, config_file_daemon, config_file_access, config_file_user] for file in config_files: @@ -552,7 +554,7 @@ def apply(snmp): return None # start SNMP daemon - os.system("systemctl restart snmpd.service") + run("systemctl restart snmpd.service") # Passwords are not available immediately in the configuration file, # after daemon startup - we wait until they have been processed by @@ -593,15 +595,15 @@ def apply(snmp): # Now update the running configuration # - # Currently when executing os.system() the environment does not + # Currently when executing run() the environment does not # have the vyos_libexec_dir variable set, see Phabricator T685. - os.system('/opt/vyatta/sbin/my_set service snmp v3 user "{0}" auth encrypted-key "{1}" > /dev/null'.format(cfg['user'], cfg['auth_pw'])) - os.system('/opt/vyatta/sbin/my_set service snmp v3 user "{0}" privacy encrypted-key "{1}" > /dev/null'.format(cfg['user'], cfg['priv_pw'])) - os.system('/opt/vyatta/sbin/my_delete service snmp v3 user "{0}" auth plaintext-key > /dev/null'.format(cfg['user'])) - os.system('/opt/vyatta/sbin/my_delete service snmp v3 user "{0}" privacy plaintext-key > /dev/null'.format(cfg['user'])) + run('/opt/vyatta/sbin/my_set service snmp v3 user "{0}" auth encrypted-key "{1}" > /dev/null'.format(cfg['user'], cfg['auth_pw'])) + run('/opt/vyatta/sbin/my_set service snmp v3 user "{0}" privacy encrypted-key "{1}" > /dev/null'.format(cfg['user'], cfg['priv_pw'])) + run('/opt/vyatta/sbin/my_delete service snmp v3 user "{0}" auth plaintext-key > /dev/null'.format(cfg['user'])) + run('/opt/vyatta/sbin/my_delete service snmp v3 user "{0}" privacy plaintext-key > /dev/null'.format(cfg['user'])) # Enable AgentX in FRR - os.system('vtysh -c "configure terminal" -c "agentx" >/dev/null') + run('vtysh -c "configure terminal" -c "agentx" >/dev/null') return None diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py index 014045796..a85dcd7f2 100755 --- a/src/conf_mode/ssh.py +++ b/src/conf_mode/ssh.py @@ -21,6 +21,8 @@ from sys import exit from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/ssh/sshd_config' @@ -131,10 +133,10 @@ def generate(ssh): def apply(ssh): if ssh is not None and 'port' in ssh.keys(): - os.system("sudo systemctl restart ssh.service") + run("sudo systemctl restart ssh.service") else: # SSH access is removed in the commit - os.system("sudo systemctl stop ssh.service") + run("sudo systemctl stop ssh.service") if os.path.isfile(config_file): os.unlink(config_file) diff --git a/src/conf_mode/system-ip.py b/src/conf_mode/system-ip.py index 335507411..66f563939 100755 --- a/src/conf_mode/system-ip.py +++ b/src/conf_mode/system-ip.py @@ -20,6 +20,8 @@ from sys import exit from copy import deepcopy from vyos.config import Config from vyos import ConfigError +from vyos.util import run + default_config_data = { 'arp_table': 8192, @@ -29,7 +31,7 @@ default_config_data = { } def sysctl(name, value): - os.system('sysctl -wq {}={}'.format(name, value)) + run('sysctl -wq {}={}'.format(name, value)) def get_config(): ip_opt = deepcopy(default_config_data) diff --git a/src/conf_mode/system-ipv6.py b/src/conf_mode/system-ipv6.py index bd28ec357..4e3de6fe9 100755 --- a/src/conf_mode/system-ipv6.py +++ b/src/conf_mode/system-ipv6.py @@ -21,6 +21,8 @@ from sys import exit from copy import deepcopy from vyos.config import Config from vyos import ConfigError +from vyos.util import run + ipv6_disable_file = '/etc/modprobe.d/vyos_disable_ipv6.conf' @@ -35,7 +37,7 @@ default_config_data = { } def sysctl(name, value): - os.system('sysctl -wq {}={}'.format(name, value)) + run('sysctl -wq {}={}'.format(name, value)) def get_config(): ip_opt = deepcopy(default_config_data) diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 7acb0a9a2..7c99fce39 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -20,13 +20,13 @@ from jinja2 import FileSystemLoader, Environment from psutil import users from pwd import getpwall, getpwnam from stat import S_IRUSR, S_IWUSR, S_IRWXU, S_IRGRP, S_IXGRP -from subprocess import Popen, PIPE, STDOUT from sys import exit from vyos.config import Config from vyos.configdict import list_diff from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import cmd, run radius_config_file = "/etc/pam_radius_auth.conf" @@ -52,10 +52,7 @@ def get_local_users(): def get_crypt_pw(password): - command = '/usr/bin/mkpasswd --method=sha-512 {}'.format(password) - p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True) - tmp = p.communicate()[0].strip() - return tmp.decode() + return cmd(f'/usr/bin/mkpasswd --method=sha-512 {password}') def get_config(): @@ -210,8 +207,8 @@ def generate(login): # remove old plaintext password # and set new encrypted password - os.system("vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set system login user '{}' authentication plaintext-password '' >/dev/null".format(user['name'])) - os.system("vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set system login user '{}' authentication encrypted-password '{}' >/dev/null".format(user['name'], user['password_encrypted'])) + run("vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set system login user '{}' authentication plaintext-password '' >/dev/null".format(user['name'])) + run("vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set system login user '{}' authentication encrypted-password '{}' >/dev/null".format(user['name'], user['password_encrypted'])) if len(login['radius_server']) > 0: # Prepare Jinja2 template loader from files @@ -238,27 +235,27 @@ def apply(login): for user in login['add_users']: # make new user using vyatta shell and make home directory (-m), # default group of 100 (users) - cmd = "useradd -m -N" + command = "useradd -m -N" # check if user already exists: if user['name'] in get_local_users(): # update existing account - cmd = "usermod" + command = "usermod" # we need to use '' quotes when passing formatted data to the shell # else it will not work as some data parts are lost in translation - cmd += " -p '{}'".format(user['password_encrypted']) - cmd += " -s /bin/vbash" + command += " -p '{}'".format(user['password_encrypted']) + command += " -s /bin/vbash" if user['full_name']: - cmd += " -c '{}'".format(user['full_name']) + command += " -c '{}'".format(user['full_name']) if user['home_dir']: - cmd += " -d '{}'".format(user['home_dir']) + command += " -d '{}'".format(user['home_dir']) - cmd += " -G frrvty,vyattacfg,sudo,adm,dip,disk" - cmd += " {}".format(user['name']) + command += " -G frrvty,vyattacfg,sudo,adm,dip,disk" + command += " {}".format(user['name']) try: - os.system(cmd) + run(command) uid = getpwnam(user['name']).pw_uid gid = getpwnam(user['name']).pw_gid @@ -298,10 +295,10 @@ def apply(login): # Logout user if he is logged in if user in list(set([tmp[0] for tmp in users()])): print('{} is logged in, forcing logout'.format(user)) - os.system('pkill -HUP -u {}'.format(user)) + run('pkill -HUP -u {}'.format(user)) # Remove user account but leave home directory to be safe - os.system('userdel -r {} 2>/dev/null'.format(user)) + run('userdel -r {} 2>/dev/null'.format(user)) except Exception as e: raise ConfigError('Deleting user "{}" raised an exception: {}'.format(user, e)) @@ -312,10 +309,10 @@ def apply(login): if len(login['radius_server']) > 0: try: # Enable RADIUS in PAM - os.system("DEBIAN_FRONTEND=noninteractive pam-auth-update --package --enable radius") + run("DEBIAN_FRONTEND=noninteractive pam-auth-update --package --enable radius") # Make NSS system aware of RADIUS, too - cmd = "sed -i -e \'/\smapname/b\' \ + command = "sed -i -e \'/\smapname/b\' \ -e \'/^passwd:/s/\s\s*/&mapuid /\' \ -e \'/^passwd:.*#/s/#.*/mapname &/\' \ -e \'/^passwd:[^#]*$/s/$/ mapname &/\' \ @@ -323,7 +320,7 @@ def apply(login): -e \'/^group:[^#]*$/s/: */&mapname /\' \ /etc/nsswitch.conf" - os.system(cmd) + run(command) except Exception as e: raise ConfigError('RADIUS configuration failed: {}'.format(e)) @@ -331,15 +328,15 @@ def apply(login): else: try: # Disable RADIUS in PAM - os.system("DEBIAN_FRONTEND=noninteractive pam-auth-update --package --remove radius") + run("DEBIAN_FRONTEND=noninteractive pam-auth-update --package --remove radius") - cmd = "sed -i -e \'/^passwd:.*mapuid[ \t]/s/mapuid[ \t]//\' \ + command = "sed -i -e \'/^passwd:.*mapuid[ \t]/s/mapuid[ \t]//\' \ -e \'/^passwd:.*[ \t]mapname/s/[ \t]mapname//\' \ -e \'/^group:.*[ \t]mapname/s/[ \t]mapname//\' \ -e \'s/[ \t]*$//\' \ /etc/nsswitch.conf" - os.system(cmd) + run(command) except Exception as e: raise ConfigError('Removing RADIUS configuration failed'.format(e)) diff --git a/src/conf_mode/system-options.py b/src/conf_mode/system-options.py index a893e98b3..063a82463 100755 --- a/src/conf_mode/system-options.py +++ b/src/conf_mode/system-options.py @@ -20,6 +20,7 @@ from sys import exit from copy import deepcopy from vyos.config import Config from vyos import ConfigError +from vyos.util import run systemd_ctrl_alt_del = '/lib/systemd/system/ctrl-alt-del.target' @@ -51,9 +52,9 @@ def generate(opt): def apply(opt): # Beep action if opt['beep_if_fully_booted']: - os.system('systemctl enable vyos-beep.service >/dev/null 2>&1') + run('systemctl enable vyos-beep.service >/dev/null 2>&1') else: - os.system('systemctl disable vyos-beep.service >/dev/null 2>&1') + run('systemctl disable vyos-beep.service >/dev/null 2>&1') # Ctrl-Alt-Delete action if opt['ctrl_alt_del'] == 'ignore': diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py index 8c0a6629c..25b9b5bed 100755 --- a/src/conf_mode/system-syslog.py +++ b/src/conf_mode/system-syslog.py @@ -22,8 +22,8 @@ from sys import exit from vyos.config import Config from vyos.defaults import directories as vyos_data_dir -from vyos.util import subprocess_cmd from vyos import ConfigError +from vyos.util import run def get_config(): c = Config() @@ -253,10 +253,8 @@ def verify(c): def apply(c): if not c: - subprocess_cmd('systemctl stop syslog') - return None - - subprocess_cmd('systemctl restart syslog') + return run('systemctl stop syslog') + return run('systemctl restart syslog') if __name__ == '__main__': try: diff --git a/src/conf_mode/system-timezone.py b/src/conf_mode/system-timezone.py index d715bd27e..2f8dc9e89 100755 --- a/src/conf_mode/system-timezone.py +++ b/src/conf_mode/system-timezone.py @@ -20,6 +20,8 @@ import os from copy import deepcopy from vyos.config import Config from vyos import ConfigError +from vyos.util import run + default_config_data = { 'name': 'UTC' @@ -40,9 +42,7 @@ def generate(tz): pass def apply(tz): - cmd = '/usr/bin/timedatectl set-timezone {}'.format(tz['name']) - os.system(cmd) - pass + run('/usr/bin/timedatectl set-timezone {}'.format(tz['name'])) if __name__ == '__main__': try: diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py index fe2da8455..df8155084 100755 --- a/src/conf_mode/tftp_server.py +++ b/src/conf_mode/tftp_server.py @@ -27,6 +27,8 @@ from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos.validate import is_ipv4, is_addr_assigned from vyos import ConfigError +from vyos.util import run + config_file = r'/etc/default/tftpd' @@ -113,7 +115,7 @@ def generate(tftpd): def apply(tftpd): # stop all services first - then we will decide - os.system('systemctl stop tftpd@{0..20}') + run('systemctl stop tftpd@{0..20}') # bail out early - e.g. service deletion if tftpd is None: @@ -138,7 +140,7 @@ def apply(tftpd): idx = 0 for listen in tftpd['listen']: - os.system('systemctl restart tftpd@{0}.service'.format(idx)) + run('systemctl restart tftpd@{0}.service'.format(idx)) idx = idx + 1 return None diff --git a/src/conf_mode/vpn-pptp.py b/src/conf_mode/vpn-pptp.py index b1204a505..45b2c4b40 100755 --- a/src/conf_mode/vpn-pptp.py +++ b/src/conf_mode/vpn-pptp.py @@ -16,7 +16,6 @@ import os import re -import subprocess from jinja2 import FileSystemLoader, Environment from socket import socket, AF_INET, SOCK_STREAM @@ -26,6 +25,7 @@ from time import sleep from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run pidfile = r'/var/run/accel_pptp.pid' pptp_cnf_dir = r'/etc/accel-ppp/pptp' @@ -50,17 +50,9 @@ def _chk_con(): raise("failed to start pptp server") break -# chap_secrets file if auth mode local -def _accel_cmd(cmd=''): - if not cmd: - return None - try: - ret = subprocess.check_output( - ['/usr/bin/accel-cmd', '-p', '2003', cmd]).decode().strip() - return ret - except: - return 1 +def _accel_cmd(command): + return run('/usr/bin/accel-cmd -p 2003 {command}') ### # inline helper functions end @@ -256,8 +248,7 @@ def apply(c): return None if not os.path.exists(pidfile): - ret = subprocess.call( - ['/usr/sbin/accel-pppd', '-c', pptp_conf, '-p', pidfile, '-d']) + ret = run(f'/usr/sbin/accel-pppd -c {pptp_conf} -p {pidfile} -d') _chk_con() if ret != 0 and os.path.exists(pidfile): os.remove(pidfile) diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py index 66b1822df..ca0844c50 100755 --- a/src/conf_mode/vpn_sstp.py +++ b/src/conf_mode/vpn_sstp.py @@ -18,16 +18,16 @@ import os from time import sleep from sys import exit -from subprocess import check_output from socket import socket, AF_INET, SOCK_STREAM from copy import deepcopy from stat import S_IRUSR, S_IWUSR, S_IRGRP from jinja2 import FileSystemLoader, Environment from vyos.config import Config -from vyos.defaults import directories as vyos_data_dir -from vyos.util import process_running, subprocess_cmd from vyos import ConfigError +from vyos.defaults import directories as vyos_data_dir +from vyos.util import process_running +from vyos.util import process_running, cmd, run pidfile = r'/var/run/accel_sstp.pid' sstp_cnf_dir = r'/etc/accel-ppp/sstp' @@ -53,16 +53,9 @@ def chk_con(): raise("failed to start sstp server") break -def accel_cmd(cmd): - if not cmd: - return None - - try: - ret = check_output(['/usr/bin/accel-cmd', '-p', '2005', cmd]) - return ret.decode().strip() - except: - return 1 +def _accel_cmd(command): + return run(f'/usr/bin/accel-cmd -p 2005 {command}') default_config_data = { 'local_users' : [], @@ -363,12 +356,12 @@ def generate(sstp): def apply(sstp): if sstp is None: if process_running(pidfile): - cmd = 'start-stop-daemon' - cmd += ' --stop ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + pidfile - subprocess_cmd(cmd) + command = 'start-stop-daemon' + command += ' --stop ' + command += ' --quiet' + command += ' --oknodo' + command += ' --pidfile ' + pidfile + cmd(command) if os.path.exists(pidfile): os.remove(pidfile) @@ -379,23 +372,23 @@ def apply(sstp): if os.path.exists(pidfile): os.remove(pidfile) - cmd = 'start-stop-daemon' - cmd += ' --start ' - cmd += ' --quiet' - cmd += ' --oknodo' - cmd += ' --pidfile ' + pidfile - cmd += ' --exec /usr/sbin/accel-pppd' + command = 'start-stop-daemon' + command += ' --start ' + command += ' --quiet' + command += ' --oknodo' + command += ' --pidfile ' + pidfile + command += ' --exec /usr/sbin/accel-pppd' # now pass arguments to accel-pppd binary - cmd += ' --' - cmd += ' -c ' + sstp_conf - cmd += ' -p ' + pidfile - cmd += ' -d' - subprocess_cmd(cmd) + command += ' --' + command += ' -c ' + sstp_conf + command += ' -p ' + pidfile + command += ' -d' + cmd(command) chk_con() else: - accel_cmd('restart') + _accel_cmd('restart') if __name__ == '__main__': diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index 8cf4b72ae..07466f3aa 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -20,13 +20,12 @@ from sys import exit from copy import deepcopy from jinja2 import FileSystemLoader, Environment from json import loads -from subprocess import check_output, CalledProcessError from vyos.config import Config from vyos.configdict import list_diff from vyos.defaults import directories as vyos_data_dir from vyos.ifconfig import Interface -from vyos.util import read_file +from vyos.util import read_file, cmd from vyos import ConfigError config_file = r'/etc/iproute2/rt_tables.d/vyos-vrf.conf' @@ -40,14 +39,11 @@ default_config_data = { } def _cmd(command): - try: - check_output(command.split()) - except CalledProcessError as e: - raise ConfigError(f'Error changing VRF: {e}') + cmd(command, raising=ConfigError, message='Error changing VRF') def list_rules(): command = 'ip -j -4 rule show' - answer = loads(check_output(command.split()).decode()) + answer = loads(cmd(command)) return [_ for _ in answer if _] def vrf_interfaces(c, match): diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index 8683faca7..d3e3710d1 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -15,7 +15,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import subprocess from sys import exit from ipaddress import ip_address, ip_interface, IPv4Interface, IPv6Interface, IPv4Address, IPv6Address @@ -28,6 +27,7 @@ import vyos.keepalived from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import run daemon_file = "/etc/default/keepalived" config_file = "/etc/keepalived/keepalived.conf" @@ -242,17 +242,17 @@ def apply(data): if not vyos.keepalived.vrrp_running(): print("Starting the VRRP process") - ret = subprocess.call("sudo systemctl restart keepalived.service", shell=True) + ret = run("sudo systemctl restart keepalived.service") else: print("Reloading the VRRP process") - ret = subprocess.call("sudo systemctl reload keepalived.service", shell=True) + ret = run("sudo systemctl reload keepalived.service") if ret != 0: raise ConfigError("keepalived failed to start") else: # VRRP is removed in the commit print("Stopping the VRRP process") - subprocess.call("sudo systemctl stop keepalived.service", shell=True) + run("sudo systemctl stop keepalived.service") os.unlink(config_file) return None diff --git a/src/conf_mode/vyos_cert.py b/src/conf_mode/vyos_cert.py index 4a44573ca..8b8953cb7 100755 --- a/src/conf_mode/vyos_cert.py +++ b/src/conf_mode/vyos_cert.py @@ -18,7 +18,6 @@ import sys import os -import subprocess import tempfile import pathlib import ssl @@ -26,6 +25,7 @@ import ssl import vyos.defaults from vyos.config import Config from vyos import ConfigError +from vyos.util import cmd vyos_conf_scripts_dir = vyos.defaults.directories['conf_mode'] @@ -49,16 +49,16 @@ def status_self_signed(cert_data): # check if certificate is 1/2 past lifetime, with openssl -checkend end_days = int(cert_data['lifetime']) end_seconds = int(0.5*60*60*24*end_days) - checkend_cmd = ('openssl x509 -checkend {end} -noout -in {crt}' - ''.format(end=end_seconds, **cert_data)) + checkend_cmd = 'openssl x509 -checkend {end} -noout -in {crt}'.format(end=end_seconds, **cert_data) try: - subprocess.check_call(checkend_cmd, shell=True) + cmd(checkend_cmd, message='Called process error') return True - except subprocess.CalledProcessError as err: - if err.returncode == 1: + except OSError as err: + if err.errno == 1: return False - else: - print("Called process error: {}.".format(err)) + print(err) + # XXX: This seems wrong to continue on failure + # implicitely returning None def generate_self_signed(cert_data): san_config = None @@ -86,9 +86,10 @@ def generate_self_signed(cert_data): ''.format(**cert_data)) try: - subprocess.check_call(openssl_req_cmd, shell=True) - except subprocess.CalledProcessError as err: - print("Called process error: {}.".format(err)) + cmd(openssl_req_cmd, message='Called process error') + except OSError as err: + print(err) + # XXX: seems wrong to ignore the failure os.chmod('{key}'.format(**cert_data), 0o400) @@ -126,11 +127,8 @@ def generate(vyos_cert): def apply(vyos_cert): for dep in dependencies: - cmd = '{0}/{1}'.format(vyos_conf_scripts_dir, dep) - try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as err: - raise ConfigError("{}.".format(err)) + command = '{0}/{1}'.format(vyos_conf_scripts_dir, dep) + cmd(command, raising=ConfigError) if __name__ == '__main__': try: diff --git a/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py b/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py index f8f790bf1..dc751c45c 100755 --- a/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py +++ b/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py @@ -15,11 +15,11 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import sys -import subprocess import syslog as sl from vyos.config import Config from vyos import ConfigError +from vyos.util import run def get_config(): @@ -45,7 +45,7 @@ def apply(config): # bring the interface up cmd = ["ip", "link", "set", "dev", intf, "up"] sl.syslog(sl.LOG_NOTICE, " ".join(cmd)) - subprocess.call(cmd) + run(cmd) # add configured addresses to interface for addr in addresses: @@ -54,7 +54,7 @@ def apply(config): else: cmd = ["ip", "address", "add", addr, "dev", intf] sl.syslog(sl.LOG_NOTICE, " ".join(cmd)) - subprocess.call(cmd) + run(cmd) if __name__ == '__main__': diff --git a/src/helpers/run-config-migration.py b/src/helpers/run-config-migration.py index 3c06e38f8..cc7166c22 100755 --- a/src/helpers/run-config-migration.py +++ b/src/helpers/run-config-migration.py @@ -19,7 +19,8 @@ import os import sys import argparse import datetime -import subprocess + +from vyos.util import cmd from vyos.migrator import Migrator, VirtualMigrator def main(): @@ -61,12 +62,7 @@ def main(): '{0:%Y-%m-%d-%H%M%S}'.format(datetime.datetime.now()), 'pre-migration']) - try: - subprocess.check_call(['cp', '-p', config_file_name, - backup_file_name]) - except subprocess.CalledProcessError as err: - print("Called process error: {}.".format(err)) - sys.exit(1) + cmd(f'cp -p {config_file_name} {backup_file_name}') if not virtual: virtual_migration = VirtualMigrator(config_file_name) diff --git a/src/helpers/validate-value.py b/src/helpers/validate-value.py index 36f996d38..fab6ca81e 100755 --- a/src/helpers/validate-value.py +++ b/src/helpers/validate-value.py @@ -5,6 +5,8 @@ import os import sys import argparse +from vyos.util import run + parser = argparse.ArgumentParser() parser.add_argument('--regex', action='append') parser.add_argument('--exec', action='append') @@ -31,7 +33,7 @@ try: cmd = "{0} {1}".format(cmd, args.value) if debug: print(cmd) - res = os.system(cmd) + res = run(cmd) if res == 0: sys.exit(0) except Exception as exn: diff --git a/src/helpers/vyos-boot-config-loader.py b/src/helpers/vyos-boot-config-loader.py index 58483fe50..c5bf22f10 100755 --- a/src/helpers/vyos-boot-config-loader.py +++ b/src/helpers/vyos-boot-config-loader.py @@ -20,13 +20,13 @@ import os import sys import pwd import grp -import subprocess import traceback from datetime import datetime from vyos.defaults import directories from vyos.configsession import ConfigSession, ConfigSessionError from vyos.configtree import ConfigTree +from vyos.util import cmd STATUS_FILE = '/tmp/vyos-config-status' TRACE_FILE = '/tmp/boot-config-trace' @@ -102,12 +102,7 @@ def failsafe(config_file_name): 'authentication', 'encrypted-password']) - cmd = ("useradd -s /bin/bash -G 'users,sudo' -m -N -p '{0}' " - "vyos".format(passwd)) - try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as e: - sys.exit("{0}".format(e)) + cmd(f"useradd -s /bin/bash -G 'users,sudo' -m -N -p '{passwd}' vyos") if __name__ == '__main__': if len(sys.argv) < 2: diff --git a/src/helpers/vyos-bridge-sync.py b/src/helpers/vyos-bridge-sync.py index 495eb5d40..097d28d85 100755 --- a/src/helpers/vyos-bridge-sync.py +++ b/src/helpers/vyos-bridge-sync.py @@ -21,16 +21,12 @@ # to the bridge automatically once it's available import argparse -import subprocess - from sys import exit from time import sleep + from vyos.config import Config +from vyos.util import cmd, run -def subprocess_cmd(command): - process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True) - proc_stdout = process.communicate()[0].strip() - pass if __name__ == '__main__': parser = argparse.ArgumentParser() @@ -45,9 +41,11 @@ if __name__ == '__main__': for bridge in conf.list_nodes('interfaces bridge'): for member_if in conf.list_nodes('interfaces bridge {} member interface'.format(bridge)): if args.interface == member_if: - cmd = 'brctl addif "{}" "{}"'.format(bridge, args.interface) + command = 'brctl addif "{}" "{}"'.format(bridge, args.interface) # let interfaces etc. settle - especially required for OpenVPN bridged interfaces sleep(4) - subprocess_cmd(cmd) + # XXX: This is ignoring any issue, should be cmd but kept as it + # XXX: during the migration to not cause any regression + run(command) exit(0) diff --git a/src/helpers/vyos-load-config.py b/src/helpers/vyos-load-config.py index 693529c23..a9fa15778 100755 --- a/src/helpers/vyos-load-config.py +++ b/src/helpers/vyos-load-config.py @@ -30,25 +30,19 @@ import vyos.remote from vyos.config import Config, VyOSError from vyos.migrator import Migrator, VirtualMigrator, MigratorError -system_config_file = 'config.boot' - class LoadConfig(Config): """A subclass for calling 'loadFile'. This does not belong in config.py, and only has a single caller. """ - def load_config(self, file_path): - cmd = [self._cli_shell_api, 'loadFile', file_path] - self._run(cmd) + def load_config(self, path): + return self._run(['/bin/cli-shell-api','loadFile',path]) -if len(sys.argv) > 1: - file_name = sys.argv[1] -else: - file_name = system_config_file +file_name = sys.argv[1] if len(sys.argv) > 1 else 'config.boot' configdir = vyos.defaults.directories['config'] - protocols = ['scp', 'sftp', 'http', 'https', 'ftp', 'tftp'] + if any(x in file_name for x in protocols): config_file = vyos.remote.get_remote_config(file_name) if not config_file: diff --git a/src/helpers/vyos-merge-config.py b/src/helpers/vyos-merge-config.py index 10a5ea4bc..6546c03e3 100755 --- a/src/helpers/vyos-merge-config.py +++ b/src/helpers/vyos-merge-config.py @@ -17,13 +17,13 @@ import sys import os -import subprocess import tempfile import vyos.defaults import vyos.remote from vyos.config import Config from vyos.configtree import ConfigTree from vyos.migrator import Migrator, VirtualMigrator +from vyos.util import cmd if (len(sys.argv) < 2): @@ -100,12 +100,10 @@ if path: add_cmds = [ cmd for cmd in add_cmds if path in cmd ] for cmd in add_cmds: - cmd = "/opt/vyatta/sbin/my_" + cmd - try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as err: - print("Called process error: {}.".format(err)) + cmd(f'/opt/vyatta/sbin/my_{cmd}', message='Called process error') + except OSError as err: + print(err) if effective_config.session_changed(): print("Merge complete. Use 'commit' to make changes effective.") diff --git a/src/migration-scripts/system/12-to-13 b/src/migration-scripts/system/12-to-13 index 5f7413d46..5b068f4fc 100755 --- a/src/migration-scripts/system/12-to-13 +++ b/src/migration-scripts/system/12-to-13 @@ -12,12 +12,8 @@ import re import sys from vyos.configtree import ConfigTree -from subprocess import Popen, PIPE, STDOUT +from vyos.util import cmd -def _cmd(cmd): - p = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True) - tmp = p.communicate()[0].strip() - return tmp.decode() if (len(sys.argv) < 1): print("Must specify file name!") @@ -37,8 +33,11 @@ else: tz = config.return_value(tz_base) # retrieve all valid timezones - tz_data = _cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::') - tz_data = tz_data.split('\n') + try: + tz_datas = cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::') + except OSError: + tz_datas = '' + tz_data = tz_datas.split('\n') if re.match(r'[Ll][Oo][Ss].+', tz): tz = 'America/Los_Angeles' diff --git a/src/op_mode/clear_conntrack.py b/src/op_mode/clear_conntrack.py index 0e52b9086..423694187 100755 --- a/src/op_mode/clear_conntrack.py +++ b/src/op_mode/clear_conntrack.py @@ -14,13 +14,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import subprocess import sys from vyos.util import ask_yes_no +from vyos.util import cmd, DEVNULL if not ask_yes_no("This will clear all currently tracked and expected connections. Continue?"): sys.exit(1) else: - subprocess.check_call(['/usr/sbin/conntrack -F'], shell=True, stderr=subprocess.DEVNULL) - subprocess.check_call(['/usr/sbin/conntrack -F expect'], shell=True, stderr=subprocess.DEVNULL) + cmd('/usr/sbin/conntrack -F', stderr=DEVNULL) + cmd('/usr/sbin/conntrack -F expect', stderr=DEVNULL) diff --git a/src/op_mode/connect_disconnect.py b/src/op_mode/connect_disconnect.py index a22615096..192fd80ec 100755 --- a/src/op_mode/connect_disconnect.py +++ b/src/op_mode/connect_disconnect.py @@ -21,6 +21,9 @@ from sys import exit from psutil import process_iter from time import strftime, localtime, time +from vyos.util import run + + PPP_LOGFILE = '/var/log/vyatta/ppp_{}.log' def check_interface(interface): @@ -56,8 +59,7 @@ def connect(interface): tm = strftime("%a %d %b %Y %I:%M:%S %p %Z", localtime(time())) with open(PPP_LOGFILE.format(interface), 'a') as f: f.write('{}: user {} started PPP daemon for {} by connect command\n'.format(tm, user, interface)) - cmd = 'umask 0; setsid sh -c "nohup /usr/sbin/pppd call {0} > /tmp/{0}.log 2>&1 &"'.format(interface) - os.system(cmd) + run('umask 0; setsid sh -c "nohup /usr/sbin/pppd call {0} > /tmp/{0}.log 2>&1 &"'.format(interface)) def disconnect(interface): @@ -75,8 +77,7 @@ def disconnect(interface): tm = strftime("%a %d %b %Y %I:%M:%S %p %Z", localtime(time())) with open(PPP_LOGFILE.format(interface), 'a') as f: f.write('{}: user {} stopped PPP daemon for {} by disconnect command\n'.format(tm, user, interface)) - cmd = '/usr/bin/poff "{}"'.format(interface) - os.system(cmd) + run('/usr/bin/poff "{}"'.format(interface)) def main(): parser = argparse.ArgumentParser() diff --git a/src/op_mode/dns_forwarding_reset.py b/src/op_mode/dns_forwarding_reset.py index da4fba3a2..93c2444b9 100755 --- a/src/op_mode/dns_forwarding_reset.py +++ b/src/op_mode/dns_forwarding_reset.py @@ -25,6 +25,8 @@ import sys import argparse import vyos.config +from vyos.util import run + parser = argparse.ArgumentParser() parser.add_argument("-a", "--all", action="store_true", help="Reset all cache") @@ -40,10 +42,10 @@ if __name__ == '__main__': sys.exit(0) if args.all: - os.system("rec_control wipe-cache \'.$\'") + run("rec_control wipe-cache \'.$\'") sys.exit(1) elif args.domain: - os.system("rec_control wipe-cache \'{0}$\'".format(args.domain)) + run("rec_control wipe-cache \'{0}$\'".format(args.domain)) else: parser.print_help() sys.exit(1) diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py index f626244a8..c400a72cd 100755 --- a/src/op_mode/dns_forwarding_statistics.py +++ b/src/op_mode/dns_forwarding_statistics.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -import subprocess import jinja2 import sys from vyos.config import Config +from vyos.config import cmd PDNS_CMD='/usr/bin/rec_control' @@ -26,8 +26,8 @@ if __name__ == '__main__': data = {} - data['cache_entries'] = subprocess.check_output([PDNS_CMD, 'get cache-entries']).decode() - data['cache_size'] = "{0:.2f}".format( int(subprocess.check_output([PDNS_CMD, 'get cache-bytes']).decode()) / 1024 ) + data['cache_entries'] = cmd(f'{PDNS_CMD} get cache-entries') + data['cache_size'] = "{0:.2f}".format( int(cmd(f'{PDNS_CMD} get cache-bytes')) / 1024 ) tmpl = jinja2.Template(OUT_TMPL_SRC) print(tmpl.render(data)) diff --git a/src/op_mode/dynamic_dns.py b/src/op_mode/dynamic_dns.py index 0d457e247..d991848ad 100755 --- a/src/op_mode/dynamic_dns.py +++ b/src/op_mode/dynamic_dns.py @@ -21,6 +21,8 @@ import sys import time from vyos.config import Config +from vyos.util import run + cache_file = r'/var/cache/ddclient/ddclient.cache' @@ -84,9 +86,9 @@ def show_status(): def update_ddns(): - os.system('systemctl stop ddclient') + run('systemctl stop ddclient') os.remove(cache_file) - os.system('systemctl start ddclient') + run('systemctl start ddclient') def main(): diff --git a/src/op_mode/flow_accounting_op.py b/src/op_mode/flow_accounting_op.py index a39eaf871..7f3ad7476 100755 --- a/src/op_mode/flow_accounting_op.py +++ b/src/op_mode/flow_accounting_op.py @@ -19,10 +19,11 @@ import sys import argparse import re import ipaddress -import subprocess import os.path from tabulate import tabulate +from vyos.util import cmd, run + # some default values uacctd_pidfile = '/var/run/uacctd.pid' uacctd_pipefile = '/tmp/uacctd.pipe' @@ -69,26 +70,16 @@ def _is_host(host): # check if flow-accounting running def _uacctd_running(): - command = "/usr/bin/sudo /bin/systemctl status uacctd > /dev/null" - return_code = subprocess.call(command, shell=True) - if not return_code == 0: - return False - - # return True if all checks were passed - return True + command = '/usr/bin/sudo /bin/systemctl status uacctd > /dev/null' + return run(command) == 0 # get list of interfaces def _get_ifaces_dict(): # run command to get ifaces list - command = "/bin/ip link show" - process = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE, universal_newlines=True) - stdout, stderr = process.communicate() - if not process.returncode == 0: - print("Failed to get interfaces list: command \"{}\" returned exit code: {}".format(command, process.returncode)) - sys.exit(1) + out = cmd('/bin/ip link show', universal_newlines=True) # read output - ifaces_out = stdout.splitlines() + ifaces_out = out.splitlines() # make a dictionary with interfaces and indexes ifaces_dict = {} @@ -103,15 +94,12 @@ def _get_ifaces_dict(): # get list of flows def _get_flows_list(): # run command to get flows list - command = "/usr/bin/pmacct -s -O json -T flows -p {}".format(uacctd_pipefile) - process = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE, universal_newlines=True) - stdout, stderr = process.communicate() - if not process.returncode == 0: - print("Failed to get flows list: command \"{}\" returned exit code: {}\nError: {}".format(command, process.returncode, stderr)) - sys.exit(1) + out = cmd(f'/usr/bin/pmacct -s -O json -T flows -p {uacctd_pipefile}', + universal_newlines=True, + message='Failed to get flows list') # read output - flows_out = stdout.splitlines() + flows_out = out.splitlines() # make a list with flows flows_list = [] @@ -208,21 +196,15 @@ if not _uacctd_running(): # restart pmacct daemon if cmd_args.action == 'restart': # run command to restart flow-accounting - command = '/usr/bin/sudo /bin/systemctl restart uacctd' - return_code = subprocess.call(command.split(' ')) - if not return_code == 0: - print("Failed to restart flow-accounting: command \"{}\" returned exit code: {}".format(command, return_code)) - sys.exit(1) + cmd('/usr/bin/sudo /bin/systemctl restart uacctd', + message='Failed to restart flow-accounting') # clear in-memory collected flows if cmd_args.action == 'clear': _check_imt() # run command to clear flows - command = "/usr/bin/pmacct -e -p {}".format(uacctd_pipefile) - return_code = subprocess.call(command.split(' ')) - if not return_code == 0: - print("Failed to clear flows: command \"{}\" returned exit code: {}".format(command, return_code)) - sys.exit(1) + cmd(f'/usr/bin/pmacct -e -p {uacctd_pipefile}', + message='Failed to clear flows') # show table with flows if cmd_args.action == 'show': diff --git a/src/op_mode/format_disk.py b/src/op_mode/format_disk.py index 5a3b250ee..9d3797f17 100755 --- a/src/op_mode/format_disk.py +++ b/src/op_mode/format_disk.py @@ -17,13 +17,12 @@ import argparse import os import re -import subprocess import sys from datetime import datetime from time import sleep from vyos.util import is_admin, ask_yes_no - +from vyos.util import run, cmd, DEVNULL def list_disks(): disks = set() @@ -37,10 +36,7 @@ def list_disks(): def is_busy(disk: str): """Check if given disk device is busy by re-reading it's partition table""" - - cmd = 'sudo blockdev --rereadpt /dev/{}'.format(disk) - status = subprocess.call([cmd], shell=True, stderr=subprocess.DEVNULL) - return status != 0 + return run(f'sudo blockdev --rereadpt /dev/{disk}', stderr=DEVNULL) != 0 def backup_partitions(disk: str): @@ -49,8 +45,7 @@ def backup_partitions(disk: str): device_path = '/dev/' + disk backup_ts = datetime.now().strftime('%Y-%m-%d-%H:%M') backup_file = '/var/tmp/backup_{}.{}'.format(disk, backup_ts) - cmd = 'sudo /sbin/sfdisk -d {} > {}'.format(device_path, backup_file) - subprocess.check_call([cmd], shell=True) + cmd(f'sudo /sbin/sfdisk -d {device_path} > {backup_file}') def list_partitions(disk: str): @@ -68,13 +63,11 @@ def list_partitions(disk: str): def delete_partition(disk: str, partition_idx: int): - cmd = 'sudo /sbin/parted /dev/{} rm {}'.format(disk, partition_idx) - subprocess.check_call([cmd], shell=True) + cmd(f'sudo /sbin/parted /dev/{disk} rm {partition_idx}') def format_disk_like(target: str, proto: str): - cmd = 'sudo /sbin/sfdisk -d /dev/{} | sudo /sbin/sfdisk --force /dev/{}'.format(proto, target) - subprocess.check_call([cmd], shell=True) + cmd(f'sudo /sbin/sfdisk -d /dev/{proto} | sudo /sbin/sfdisk --force /dev/{target}') if __name__ == '__main__': diff --git a/src/op_mode/generate_ssh_server_key.py b/src/op_mode/generate_ssh_server_key.py index f205919b8..f65d383c0 100755 --- a/src/op_mode/generate_ssh_server_key.py +++ b/src/op_mode/generate_ssh_server_key.py @@ -14,14 +14,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import subprocess import sys from vyos.util import ask_yes_no +from vyos.util import cmd if not ask_yes_no('Do you really want to remove the existing SSH host keys?'): sys.exit(0) -else: - subprocess.check_call(['sudo rm -v /etc/ssh/ssh_host_*'], shell=True) - subprocess.check_call(['sudo dpkg-reconfigure openssh-server'], shell=True) - subprocess.check_call(['sudo systemctl restart ssh'], shell=True) + +cmd('sudo rm -v /etc/ssh/ssh_host_*') +cmd('sudo dpkg-reconfigure openssh-server') +cmd('sudo systemctl restart ssh') diff --git a/src/op_mode/lldp_op.py b/src/op_mode/lldp_op.py index 4d8fdbc99..c8a5543b6 100755 --- a/src/op_mode/lldp_op.py +++ b/src/op_mode/lldp_op.py @@ -20,9 +20,10 @@ import jinja2 from xml.dom import minidom from sys import exit -from subprocess import Popen, PIPE, STDOUT from tabulate import tabulate +from vyos.util import popen + parser = argparse.ArgumentParser() parser.add_argument("-a", "--all", action="store_true", help="Show LLDP neighbors on all interfaces") parser.add_argument("-i", "--interface", action="store", help="Show LLDP neighbors on specific interface") @@ -40,9 +41,8 @@ Device ID Local Proto Cap Platform Port ID def _get_neighbors(): command = '/usr/sbin/lldpcli -f xml show neighbors' - p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True) - tmp = p.communicate()[0].strip() - return tmp.decode() + out,_ = popen(command) + return out def extract_neighbor(neighbor): """ diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py index 54fc12be3..772bb8198 100755 --- a/src/op_mode/powerctrl.py +++ b/src/op_mode/powerctrl.py @@ -17,12 +17,11 @@ import os import sys import argparse -import subprocess import re from datetime import datetime, timedelta, time as type_time, date as type_date -from subprocess import check_output, CalledProcessError, STDOUT from vyos.util import ask_yes_no +from vyos.util import cmd, run systemd_sched_file = "/run/systemd/shutdown/scheduled" @@ -45,23 +44,20 @@ def parse_date(s): return None def get_shutdown_status(): - try: - 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 - except CalledProcessError: - 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() @@ -76,13 +72,13 @@ def check_shutdown(): def cancel_shutdown(): output = get_shutdown_status() if output and 'MODE' in output: + timenow = datetime.now().strftime('%Y-%m-%d %H:%M:%S') try: - timenow = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - cmd = check_output(["/sbin/shutdown","-c","--no-wall"]) - message = "Scheduled %s has been cancelled %s" % (output['MODE'], timenow) - os.system("wall %s" % message) - except CalledProcessError as e: + 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") @@ -99,14 +95,14 @@ def execute_shutdown(time, reboot = True, ask=True): chk_vyatta_based_reboots() ### - cmd = check_output(["/sbin/shutdown",action,"now"],stderr=STDOUT) - print(cmd.decode().split(",",1)[0]) + out = cmd(f'/sbin/shutdown {action} now') + print(out.split(",",1)[0]) return elif len(time) == 1: # Assume the argument is just time ts = parse_time(time[0]) if ts: - cmd = check_output(["/sbin/shutdown", action, time[0]], stderr=STDOUT) + cmd(f'/sbin/shutdown {action} {time[0]}') else: sys.exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0])) elif len(time) == 2: @@ -117,7 +113,7 @@ def execute_shutdown(time, reboot = True, ask=True): t = datetime.combine(ds, ts) td = t - datetime.now() t2 = 1 + int(td.total_seconds())//60 # Get total minutes - cmd = check_output(["/sbin/shutdown", action, str(t2)], stderr=STDOUT) + cmd('/sbin/shutdown {action} {t2}') else: if not ts: sys.exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0])) @@ -136,7 +132,7 @@ def chk_vyatta_based_reboots(): if os.path.exists(f): jid = open(f).read().strip() if jid != 0: - subprocess.call(['sudo', 'atrm', jid]) + run(f'sudo atrm {jid}') os.remove(f) def main(): diff --git a/src/op_mode/reset_openvpn.py b/src/op_mode/reset_openvpn.py index 4c29fbbba..618cad5ea 100755 --- a/src/op_mode/reset_openvpn.py +++ b/src/op_mode/reset_openvpn.py @@ -17,10 +17,9 @@ import sys import os -from subprocess import Popen, PIPE from time import sleep from netifaces import interfaces -from vyos.util import process_running +from vyos.util import process_running, cmd def get_config_name(intf): cfg_file = r'/opt/vyatta/etc/openvpn/openvpn-{}.conf'.format(intf) @@ -30,9 +29,6 @@ def get_pid_file(intf): pid_file = r'/var/run/openvpn/{}.pid'.format(intf) return pid_file -def subprocess_cmd(command): - p = Popen(command, stdout=PIPE, shell=True) - p.communicate() if __name__ == '__main__': if (len(sys.argv) < 1): @@ -43,12 +39,12 @@ if __name__ == '__main__': if os.path.isfile(get_config_name(interface)): pidfile = '/var/run/openvpn/{}.pid'.format(interface) if process_running(pidfile): - cmd = 'start-stop-daemon' - cmd += ' --stop' - cmd += ' --oknodo' - cmd += ' --quiet' - cmd += ' --pidfile ' + pidfile - subprocess_cmd(cmd) + command = 'start-stop-daemon' + command += ' --stop' + command += ' --oknodo' + command += ' --quiet' + command += ' --pidfile ' + pidfile + cmd(command) # When stopping OpenVPN we need to wait for the 'old' interface to # vanish from the Kernel, if it is not gone, OpenVPN will report: @@ -57,18 +53,18 @@ if __name__ == '__main__': sleep(0.250) # 250ms # re-start OpenVPN process - cmd = 'start-stop-daemon' - cmd += ' --start' - cmd += ' --oknodo' - cmd += ' --quiet' - cmd += ' --pidfile ' + get_pid_file(interface) - cmd += ' --exec /usr/sbin/openvpn' + command = 'start-stop-daemon' + command += ' --start' + command += ' --oknodo' + command += ' --quiet' + command += ' --pidfile ' + get_pid_file(interface) + command += ' --exec /usr/sbin/openvpn' # now pass arguments to openvpn binary - cmd += ' --' - cmd += ' --daemon openvpn-' + interface - cmd += ' --config ' + get_config_name(interface) + command += ' --' + command += ' --daemon openvpn-' + interface + command += ' --config ' + get_config_name(interface) - subprocess_cmd(cmd) + cmd(command) else: print("OpenVPN interface {} does not exist!".format(interface)) sys.exit(1) diff --git a/src/op_mode/reset_vpn.py b/src/op_mode/reset_vpn.py index 52677b58d..b47212f88 100755 --- a/src/op_mode/reset_vpn.py +++ b/src/op_mode/reset_vpn.py @@ -16,52 +16,53 @@ # import os import sys -import subprocess import argparse #import re -pptp_cmd = ["/usr/bin/accel-cmd", "-p 2003"] -l2tp_cmd = ["/usr/bin/accel-cmd", "-p 2004"] +from vyos.util import run, DEVNULL + +pptp_base = '/usr/bin/accel-cmd -p 2003 terminate {} {}' +l2tp_base = '/usr/bin/accel-cmd -p 2004 terminate {} {}' def terminate_sessions(username='', interface='', protocol=''): if username: if username == "all_users": if protocol == "pptp": - pptp_cmd.append("terminate all") - subprocess.call(pptp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + pptp_cmd = pptp_base.format('all','') + run(pptp_cmd, stdout=DEVNULL, stderr=DEVNULL) return elif protocol == "l2tp": - l2tp_cmd.append("terminate all") - subprocess.call(l2tp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + l2tp_cmd = l2tp_base.format('all', '') + run(l2tp_cmd, stdout=DEVNULL, stderr=DEVNULL) return else: - pptp_cmd.append("terminate all") - subprocess.call(pptp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - l2tp_cmd.append("terminate all") - subprocess.call(l2tp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + pptp_cmd = pptp_base.format('all', '') + run(pptp_cmd, stdout=DEVNULL, stderr=DEVNULL) + l2tp_cmd = l2tp_base.format('all', '') + run(l2tp_cmd, stdout=DEVNULL, stderr=DEVNULL) return if protocol == "pptp": - pptp_cmd.append("terminate username {0}".format(username)) - subprocess.call(pptp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + pptp_cmd = pptp_base.format('username', username) + run(pptp_cmd, stdout=DEVNULL, stderr=DEVNULL) return elif protocol == "l2tp": - l2tp_cmd.append("terminate username {0}".format(username)) - subprocess.call(l2tp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + l2tp_cmd = l2tp_base.format('username', username) + run(l2tp_cmd, stdout=DEVNULL, stderr=DEVNULL) return else: - pptp_cmd.append("terminate username {0}".format(username)) - subprocess.call(pptp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + pptp_cmd = pptp_base.format('username', username) + run(pptp_cmd, stdout=DEVNULL, stderr=DEVNULL) l2tp_cmd.append("terminate username {0}".format(username)) - subprocess.call(l2tp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + run(l2tp_cmd, stdout=DEVNULL, stderr=DEVNULL) return # rewrite `terminate by interface` if pptp will have pptp%d interface naming if interface: - pptp_cmd.append("terminate if {0}".format(interface)) - subprocess.call(pptp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - l2tp_cmd.append("terminate if {0}".format(interface)) - subprocess.call(l2tp_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + pptp_cmd = pptp_base.format('if', interface) + run(pptp_cmd, stdout=DEVNULL, stderr=DEVNULL) + l2tp_cmd = l2tp_base.format('if', interface) + run(l2tp_cmd, stdout=DEVNULL, stderr=DEVNULL) def main(): diff --git a/src/op_mode/restart_dhcp_relay.py b/src/op_mode/restart_dhcp_relay.py index ab02d1eb3..057b4dcd8 100755 --- a/src/op_mode/restart_dhcp_relay.py +++ b/src/op_mode/restart_dhcp_relay.py @@ -23,6 +23,8 @@ import argparse import os import vyos.config +from vyos.util import run + parser = argparse.ArgumentParser() parser.add_argument("--ipv4", action="store_true", help="Restart IPv4 DHCP relay") @@ -37,7 +39,7 @@ if __name__ == '__main__': if not c.exists_effective('service dhcp-relay'): print("DHCP relay service not configured") else: - os.system('sudo systemctl restart isc-dhcp-relay.service') + run('sudo systemctl restart isc-dhcp-relay.service') sys.exit(0) elif args.ipv6: @@ -45,7 +47,7 @@ if __name__ == '__main__': if not c.exists_effective('service dhcpv6-relay'): print("DHCPv6 relay service not configured") else: - os.system('sudo systemctl restart isc-dhcpv6-relay.service') + run('sudo systemctl restart isc-dhcpv6-relay.service') sys.exit(0) else: diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py index da6407e23..6304e72db 100755 --- a/src/op_mode/restart_frr.py +++ b/src/op_mode/restart_frr.py @@ -17,12 +17,13 @@ import sys import argparse -import subprocess import logging from logging.handlers import SysLogHandler from pathlib import Path import psutil +from vyos.util import run + # some default values watchfrr = '/usr/lib/frr/watchfrr.sh' vtysh = '/usr/bin/vtysh' @@ -86,7 +87,7 @@ def _write_config(): Path(frrconfig_tmp).mkdir(parents=False, exist_ok=True) # save frr.conf to it command = "{} -n -w --config_dir {} 2> /dev/null".format(vtysh, frrconfig_tmp) - return_code = subprocess.call(command, shell=True) + return_code = run(command) if not return_code == 0: logger.error("Failed to save active config: \"{}\" returned exit code: {}".format(command, return_code)) return False @@ -108,7 +109,7 @@ def _cleanup(): # check if daemon is running def _daemon_check(daemon): command = "{} print_status {}".format(watchfrr, daemon) - return_code = subprocess.call(command, shell=True) + return_code = run(command) if not return_code == 0: logger.error("Daemon \"{}\" is not running".format(daemon)) return False @@ -119,7 +120,7 @@ def _daemon_check(daemon): # restart daemon def _daemon_restart(daemon): command = "{} restart {}".format(watchfrr, daemon) - return_code = subprocess.call(command, shell=True) + return_code = run(command) if not return_code == 0: logger.error("Failed to restart daemon \"{}\"".format(daemon)) return False @@ -135,7 +136,7 @@ def _reload_config(daemon): else: command = "{} -n -b --config_dir {} 2> /dev/null".format(vtysh, frrconfig_tmp) - return_code = subprocess.call(command, shell=True) + return_code = run(command) if not return_code == 0: logger.error("Failed to reinstall configuration") return False diff --git a/src/op_mode/show_acceleration.py b/src/op_mode/show_acceleration.py index 3ba0e85dd..05d3d8906 100755 --- a/src/op_mode/show_acceleration.py +++ b/src/op_mode/show_acceleration.py @@ -19,14 +19,15 @@ import sys import os import re import argparse -import subprocess + from vyos.config import Config +from vyos.util import popen, run + def detect_qat_dev(): - ret = subprocess.Popen(['sudo', 'lspci', '-nn'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() + output, err = popen('sudo lspci -nn', decode='utf-8') if not err: - data = re.findall('(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)', output.decode("utf-8")) + data = re.findall('(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)', output) #If QAT devices found if data: return @@ -42,15 +43,12 @@ def show_qat_status(): sys.exit(1) # Show QAT service - os.system('sudo /etc/init.d/vyos-qat-utilities status') + run('sudo /etc/init.d/vyos-qat-utilities status') # Return QAT devices def get_qat_devices(): - ret = subprocess.Popen(['sudo', '/etc/init.d/vyos-qat-utilities', 'status'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() + data_st, err = popen('sudo /etc/init.d/vyos-qat-utilities status', decode='utf-8') if not err: - #print(output) - data_st = output.decode("utf-8") elm_lst = re.findall('qat_dev\d', data_st) print('\n'.join(elm_lst)) @@ -58,11 +56,10 @@ def get_qat_devices(): def get_qat_proc_path(qat_dev): q_type = "" q_bsf = "" - ret = subprocess.Popen(['sudo', '/etc/init.d/vyos-qat-utilities', 'status'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() + output, err = popen('sudo /etc/init.d/vyos-qat-utilities status', decode='utf-8') if not err: # Parse QAT service output - data_st = output.decode("utf-8").split("\n") + data_st = output.split("\n") for elm_str in range(len(data_st)): if re.search(qat_dev, data_st[elm_str]): elm_list = data_st[elm_str].split(", ") @@ -97,22 +94,22 @@ args = parser.parse_args() if args.hw: detect_qat_dev() # Show availible Intel QAT devices - os.system('sudo lspci -nn | egrep -e \'8086:37c8|8086:19e2|8086:0435|8086:6f54\'') + run('sudo lspci -nn | egrep -e \'8086:37c8|8086:19e2|8086:0435|8086:6f54\'') elif args.flow and args.dev: check_qat_if_conf() - os.system('sudo cat '+get_qat_proc_path(args.dev)+"fw_counters") + run('sudo cat '+get_qat_proc_path(args.dev)+"fw_counters") elif args.interrupts: check_qat_if_conf() # Delete _dev from args.dev - os.system('sudo cat /proc/interrupts | grep qat') + run('sudo cat /proc/interrupts | grep qat') elif args.status: check_qat_if_conf() show_qat_status() elif args.conf and args.dev: check_qat_if_conf() - os.system('sudo cat '+get_qat_proc_path(args.dev)+"dev_cfg") + run('sudo cat '+get_qat_proc_path(args.dev)+"dev_cfg") elif args.dev_list: get_qat_devices() else: parser.print_help() - sys.exit(1)
\ No newline at end of file + sys.exit(1) diff --git a/src/op_mode/show_dhcp.py b/src/op_mode/show_dhcp.py index f801ba753..4e3e08263 100755 --- a/src/op_mode/show_dhcp.py +++ b/src/op_mode/show_dhcp.py @@ -24,9 +24,12 @@ import collections import os from datetime import datetime -from vyos.config import Config from isc_dhcp_leases import Lease, IscDhcpLeases +from vyos.config import Config +from vyos.util import run + + lease_file = "/config/dhcpd.leases" pool_key = "shared-networkname" @@ -190,7 +193,7 @@ if __name__ == '__main__': sys.exit(0) # if dhcp server is down, inactive leases may still be shown as active, so warn the user. - if os.system('systemctl -q is-active isc-dhcpv4-server.service') != 0: + if run('systemctl -q is-active isc-dhcpv4-server.service') != 0: print("WARNING: DHCP server is configured but not started. Data may be stale.") if args.leases: diff --git a/src/op_mode/show_dhcpv6.py b/src/op_mode/show_dhcpv6.py index ae63af39b..4ef4849ff 100755 --- a/src/op_mode/show_dhcpv6.py +++ b/src/op_mode/show_dhcpv6.py @@ -24,9 +24,11 @@ import collections import os from datetime import datetime -from vyos.config import Config from isc_dhcp_leases import Lease, IscDhcpLeases +from vyos.config import Config +from vyos.util import run + lease_file = "/config/dhcpdv6.leases" pool_key = "shared-networkname" @@ -177,7 +179,7 @@ if __name__ == '__main__': sys.exit(0) # if dhcp server is down, inactive leases may still be shown as active, so warn the user. - if os.system('systemctl -q is-active isc-dhcpv6-server.service') != 0: + if run('systemctl -q is-active isc-dhcpv6-server.service') != 0: print("WARNING: DHCPv6 server is configured but not started. Data may be stale.") if args.leases: diff --git a/src/op_mode/show_vpn_ra.py b/src/op_mode/show_vpn_ra.py index cf6119c2f..2323193b1 100755 --- a/src/op_mode/show_vpn_ra.py +++ b/src/op_mode/show_vpn_ra.py @@ -17,8 +17,8 @@ import os import sys import re -import subprocess -# from subprocess import Popen, PIPE + +from vyos.util import popen # chech connection to pptp and l2tp daemon def get_sessions(): @@ -31,18 +31,16 @@ def get_sessions(): len_def_header = 170 # Check pptp - ret = subprocess.Popen(pptp_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() - if not err and len(output.decode("utf-8")) > len_def_header and not re.search(err_pattern, output.decode("utf-8")): - print(output.decode("utf-8")) + output, err = popen(pptp_cmd, decode='utf-8') + if not err and len(output) > len_def_header and not re.search(err_pattern, output): + print(output) else: absent_pptp = True # Check l2tp - ret = subprocess.Popen(l2tp_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, err) = ret.communicate() - if not err and len(output.decode("utf-8")) > len_def_header and not re.search(err_pattern, output.decode("utf-8")): - print(output.decode("utf-8")) + output, err = popen(l2tp_cmd, decode='utf-8') + if not err and len(output) > len_def_header and not re.search(err_pattern, output): + print(output) else: absent_l2tp = True diff --git a/src/op_mode/show_vrf.py b/src/op_mode/show_vrf.py index 66c33e607..b6bb73d01 100755 --- a/src/op_mode/show_vrf.py +++ b/src/op_mode/show_vrf.py @@ -16,10 +16,10 @@ import argparse import jinja2 - -from subprocess import check_output from json import loads +from vyos.util import cmd + vrf_out_tmpl = """ VRF name state mac address flags interfaces -------- ----- ----------- ----- ---------- @@ -31,12 +31,12 @@ VRF name state mac address flags interfa def list_vrfs(): command = 'ip -j -br link show type vrf' - answer = loads(check_output(command.split()).decode()) + answer = loads(cmd(command)) return [_ for _ in answer if _] def list_vrf_members(vrf): command = f'ip -j -br link show master {vrf}' - answer = loads(check_output(command.split()).decode()) + answer = loads(cmd(command)) return [_ for _ in answer if _] parser = argparse.ArgumentParser() diff --git a/src/op_mode/show_wireless.py b/src/op_mode/show_wireless.py index aff882559..b5ee3aee1 100755 --- a/src/op_mode/show_wireless.py +++ b/src/op_mode/show_wireless.py @@ -19,19 +19,15 @@ import re from sys import exit from copy import deepcopy -from subprocess import Popen, PIPE, STDOUT from vyos.config import Config +from vyos.util import popen parser = argparse.ArgumentParser() parser.add_argument("-s", "--scan", help="Scan for Wireless APs on given interface, e.g. 'wlan0'") parser.add_argument("-b", "--brief", action="store_true", help="Show wireless configuration") parser.add_argument("-c", "--stations", help="Show wireless clients connected on interface, e.g. 'wlan0'") -def _cmd(command): - p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True) - tmp = p.communicate()[0].strip() - return tmp.decode() def show_brief(): config = Config() @@ -57,7 +53,8 @@ def show_brief(): return interfaces def ssid_scan(intf): - tmp = _cmd('/sbin/iw dev {} scan ap-force'.format(intf)) + # XXX: This ignores errors + tmp, _ = popen(f'/sbin/iw dev {intf} scan ap-force') networks = [] data = { 'ssid': '', @@ -89,7 +86,8 @@ def ssid_scan(intf): return networks def show_clients(intf): - tmp = _cmd('/sbin/iw dev {} station dump'.format(intf)) + # XXX: This ignores errors + tmp, _ = popen(f'/sbin/iw dev {intf} station dump') clients = [] data = { 'mac': '', diff --git a/src/op_mode/snmp.py b/src/op_mode/snmp.py index e08441f0e..b09eab97f 100755 --- a/src/op_mode/snmp.py +++ b/src/op_mode/snmp.py @@ -24,6 +24,7 @@ import sys import argparse from vyos.config import Config +from vyos.util import run config_file_daemon = r'/etc/snmp/snmpd.conf' @@ -53,7 +54,7 @@ def show_all(): def show_community(c, h): print('Status of SNMP community {0} on {1}'.format(c, h), flush=True) - os.system('/usr/bin/snmpstatus -t1 -v1 -c {0} {1}'.format(c, h)) + run('/usr/bin/snmpstatus -t1 -v1 -c {0} {1}'.format(c, h)) if __name__ == '__main__': args = parser.parse_args() diff --git a/src/op_mode/snmp_ifmib.py b/src/op_mode/snmp_ifmib.py index 3a0e0d4b2..2479936bd 100755 --- a/src/op_mode/snmp_ifmib.py +++ b/src/op_mode/snmp_ifmib.py @@ -22,36 +22,24 @@ import sys import argparse import netifaces -import subprocess from vyos.config import Config +from vyos.util import popen parser = argparse.ArgumentParser(description='Retrieve SNMP interfaces information') parser.add_argument('--ifindex', action='store', nargs='?', const='all', help='Show interface index') parser.add_argument('--ifalias', action='store', nargs='?', const='all', help='Show interface aliase') parser.add_argument('--ifdescr', action='store', nargs='?', const='all', help='Show interface description') -def show_ifindex(i): - proc = subprocess.Popen(['/bin/ip', 'link', 'show', i], stdout=subprocess.PIPE) - (out, err) = proc.communicate() - # convert output to string - string = out.decode("utf-8") - - index = 'ifIndex = ' + string.split(':')[0] +def show_ifindex(intf): + out, err = popen(f'/bin/ip link show {intf}', decode='utf-8') + index = 'ifIndex = ' + out.split(':')[0] return index.replace('\n', '') -def show_ifalias(i): - proc = subprocess.Popen(['/bin/ip', 'link', 'show', i], stdout=subprocess.PIPE) - (out, err) = proc.communicate() - # convert output to string - string = out.decode("utf-8") - - if 'alias' in string: - alias = 'ifAlias = ' + string.split('alias')[1].lstrip() - else: - alias = 'ifAlias = ' + i - - return alias.replace('\n', '') +def show_ifalias(intf): + out, err = popen(f'/bin/ip link show {intf}', decode='utf-8') + alias = out.split('alias')[1].lstrip() if 'alias' in out else intf + return 'ifAlias = ' + alias.replace('\n', '') def show_ifdescr(i): ven_id = '' @@ -74,14 +62,13 @@ def show_ifdescr(i): return ret device = str(ven_id) + ':' + str(dev_id) - proc = subprocess.Popen(['/usr/bin/lspci', '-mm', '-d', device], stdout=subprocess.PIPE) - (out, err) = proc.communicate() + out, err = popen(f'/usr/bin/lspci -mm -d {device}', decode='utf-8') vendor = "" device = "" # convert output to string - string = out.decode("utf-8").split('"') + string = out.split('"') if len(string) > 3: vendor = string[3] diff --git a/src/op_mode/system_integrity.py b/src/op_mode/system_integrity.py index 886d94f16..c0e3d1095 100755 --- a/src/op_mode/system_integrity.py +++ b/src/op_mode/system_integrity.py @@ -18,18 +18,19 @@ import sys import os -import subprocess import re import itertools from datetime import datetime, timedelta +from vyos.util import cmd + verf = r'/usr/libexec/vyos/op_mode/version.py' def get_sys_build_version(): if not os.path.exists(verf): return None - a = subprocess.check_output(['/usr/libexec/vyos/op_mode/version.py']).decode() + a = cmd('/usr/libexec/vyos/op_mode/version.py') if re.search('^Built on:.+',a, re.M) == None: return None diff --git a/src/op_mode/version.py b/src/op_mode/version.py index 5aff0f767..34eca44b1 100755 --- a/src/op_mode/version.py +++ b/src/op_mode/version.py @@ -22,7 +22,6 @@ import os import sys -import subprocess import argparse import json @@ -31,6 +30,8 @@ import pystache import vyos.version import vyos.limericks +from vyos.util import cmd, run + parser = argparse.ArgumentParser() parser.add_argument("-a", "--all", action="store_true", help="Include individual package versions") @@ -73,15 +74,15 @@ if __name__ == '__main__': version_data = vyos.version.get_version_data() # Get system architecture (well, kernel architecture rather) - version_data['system_arch'] = subprocess.check_output('uname -m', shell=True).decode().strip() + version_data['system_arch'] = cmd('uname -m') # Get hypervisor name, if any system_type = "bare metal" try: - hypervisor = subprocess.check_output('hvinfo 2>/dev/null', shell=True).decode().strip() + hypervisor = cmd('hvinfo 2>/dev/null') system_type = "{0} guest".format(hypervisor) - except subprocess.CalledProcessError: + except OSError: # hvinfo returns 1 if it cannot detect any hypervisor pass version_data['system_type'] = system_type @@ -93,9 +94,9 @@ if __name__ == '__main__': # while on livecd it's just "filesystem.squashfs", that's how we tell a livecd boot # from an installed image boot_via = "installed image" - if subprocess.call(""" grep -e '^overlay.*/filesystem.squashfs' /proc/mounts >/dev/null""", shell=True) == 0: + if run(""" grep -e '^overlay.*/filesystem.squashfs' /proc/mounts >/dev/null""") == 0: boot_via = "livecd" - elif subprocess.call(""" grep '^overlay /' /proc/mounts >/dev/null """, shell=True) != 0: + elif run(""" grep '^overlay /' /proc/mounts >/dev/null """) != 0: boot_via = "legacy non-image installation" version_data['boot_via'] = boot_via @@ -118,7 +119,7 @@ if __name__ == '__main__': if args.all: print("Package versions:") - os.system("dpkg -l") + run("dpkg -l") if args.funny: print(vyos.limericks.get_random()) diff --git a/src/op_mode/wireguard.py b/src/op_mode/wireguard.py index c684f8a47..d940d79eb 100755 --- a/src/op_mode/wireguard.py +++ b/src/op_mode/wireguard.py @@ -20,7 +20,6 @@ import argparse import os import sys import shutil -import subprocess import syslog as sl import re @@ -28,6 +27,7 @@ from vyos.ifconfig import WireGuardIf from vyos import ConfigError from vyos.config import Config +from vyos.util import run dir = r'/config/auth/wireguard' psk = dir + '/preshared.key' @@ -36,16 +36,14 @@ def check_kmod(): """ check if kmod is loaded, if not load it """ if not os.path.exists('/sys/module/wireguard'): sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") - if os.system('sudo modprobe wireguard') != 0: + if run('sudo modprobe wireguard') != 0: sl.syslog(sl.LOG_ERR, "modprobe wireguard failed") raise ConfigError("modprobe wireguard failed") def generate_keypair(pk, pub): """ generates a keypair which is stored in /config/auth/wireguard """ old_umask = os.umask(0o027) - ret = subprocess.call( - ['wg genkey | tee ' + pk + '|wg pubkey > ' + pub], shell=True) - if ret != 0: + if run(f'wg genkey | tee {pk} | wg pubkey > {pub}') != 0: raise ConfigError("wireguard key-pair generation failed") else: sl.syslog( @@ -69,9 +67,9 @@ def genkey(location): else: """ if keypair is bing executed from a running iso """ if not os.path.exists(location): - subprocess.call(['sudo mkdir -p ' + location], shell=True) - subprocess.call(['sudo chgrp vyattacfg ' + location], shell=True) - subprocess.call(['sudo chmod 750 ' + location], shell=True) + run(f'sudo mkdir -p {location}') + run(f'sudo chgrp vyattacfg {location}') + run(f'sudo chmod 750 {location}') generate_keypair(pk, pub) os.umask(old_umask) @@ -90,7 +88,7 @@ def genpsk(): it's stored only in the cli config """ - subprocess.call(['wg genpsk'], shell=True) + run('wg genpsk') def list_key_dirs(): """ lists all dirs under /config/auth/wireguard """ diff --git a/src/system/keepalived-fifo.py b/src/system/keepalived-fifo.py index 5e85da4a4..2778deaab 100755 --- a/src/system/keepalived-fifo.py +++ b/src/system/keepalived-fifo.py @@ -20,7 +20,6 @@ import time import signal import argparse import threading -import subprocess import re import json from pathlib import Path @@ -28,6 +27,8 @@ from queue import Queue import logging from logging.handlers import SysLogHandler +from vyos.util import cmd + # configure logging logger = logging.getLogger(__name__) logs_format = logging.Formatter('%(filename)s: %(message)s') @@ -84,14 +85,11 @@ class KeepalivedFifo: # run command def _run_command(self, command): + logger.debug("Running the command: {}".format(command)) try: - logger.debug("Running the command: {}".format(command)) - process = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) - stdout, stderr = process.communicate() - if process.returncode != 0: - raise Exception("The command \"{}\" returned status {}. Error: {}".format(command, process.returncode, stderr)) - except Exception as err: - logger.error("Unable to execute command \"{}\": {}".format(command, err)) + cmd(command, universal_newlines=True) + except OSError as err: + logger.error(f'Unable to execute command "{command}": {err}') # create FIFO pipe def pipe_create(self): diff --git a/src/validators/timezone b/src/validators/timezone index d7a8f64c4..ec845e755 100755 --- a/src/validators/timezone +++ b/src/validators/timezone @@ -17,12 +17,8 @@ import argparse from sys import exit -from subprocess import Popen, PIPE, STDOUT -def _cmd(cmd): - p = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True) - tmp = p.communicate()[0].strip() - return tmp.decode() +from vyos.util import cmd parser = argparse.ArgumentParser() parser.add_argument("--validate", action="store", help="Check if timezone is valid") @@ -31,7 +27,7 @@ if __name__ == '__main__': args = parser.parse_args() if args.validate: - tz_data = _cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::') + tz_data = cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::') tz_data = tz_data.split('\n') # if timezone can't be found in list it's invalid if args.validate not in tz_data: |