summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/vyos/airbag.py5
-rw-r--r--python/vyos/authutils.py2
-rw-r--r--python/vyos/debug.py182
-rw-r--r--python/vyos/ifconfig/__init__.py1
-rw-r--r--python/vyos/ifconfig/control.py13
-rw-r--r--python/vyos/ifconfig/input.py31
-rw-r--r--python/vyos/ifconfig/section.py (renamed from python/vyos/ifconfig/register.py)72
-rw-r--r--python/vyos/ifconfig/tunnel.py14
-rw-r--r--python/vyos/ifconfig/vti.py31
-rw-r--r--python/vyos/remote.py15
-rw-r--r--python/vyos/template.py22
-rw-r--r--python/vyos/util.py144
12 files changed, 445 insertions, 87 deletions
diff --git a/python/vyos/airbag.py b/python/vyos/airbag.py
index 664974d5f..b0565192d 100644
--- a/python/vyos/airbag.py
+++ b/python/vyos/airbag.py
@@ -19,10 +19,10 @@ import logging
import logging.handlers
from datetime import datetime
+from vyos import debug
from vyos.config import Config
from vyos.version import get_version
from vyos.util import run
-from vyos.util import debug
# we allow to disable the extra logging
@@ -77,8 +77,7 @@ def bug_report(dtype, value, trace):
# reach the end of __main__ and was not intercepted
def intercepter(dtype, value, trace):
bug_report(dtype, value, trace)
- # debug returns either '' or 'developer' if debuging is enabled
- if debug('developer'):
+ if debug.enabled('developer'):
import pdb
pdb.pm()
diff --git a/python/vyos/authutils.py b/python/vyos/authutils.py
index 90a46ffb4..66b5f4a74 100644
--- a/python/vyos/authutils.py
+++ b/python/vyos/authutils.py
@@ -22,7 +22,7 @@ def make_password_hash(password):
""" Makes a password hash for /etc/shadow using mkpasswd """
mkpassword = 'mkpasswd --method=sha-512 --stdin'
- return cmd(mkpassword, input=password.encode(), timeout=5)
+ return cmd(mkpassword, input=password, timeout=5)
def split_ssh_public_key(key_string, defaultname=""):
""" Splits an SSH public key into its components """
diff --git a/python/vyos/debug.py b/python/vyos/debug.py
new file mode 100644
index 000000000..20090fb85
--- /dev/null
+++ b/python/vyos/debug.py
@@ -0,0 +1,182 @@
+# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+
+def message(message, flag='', destination=sys.stdout):
+ """
+ print a debug message line on stdout if debugging is enabled for the flag
+ also log it to a file if the flag 'log' is enabled
+
+ message: the message to print
+ flag: which flag must be set for it to print
+ destination: which file like object to write to (default: sys.stdout)
+
+ returns if any message was logged or not
+ """
+ enable = enabled(flag)
+ if enable:
+ destination.write(_format(flag,message))
+
+ # the log flag is special as it logs all the commands
+ # executed to a log
+ logfile = _logfile('log', '/tmp/developer-log')
+ if not logfile:
+ return enable
+
+ try:
+ # at boot the file is created as root:vyattacfg
+ # at runtime the file is created as user:vyattacfg
+ # the default permission are 644
+ mask = os.umask(0o113)
+
+ with open(logfile, 'a') as f:
+ f.write(_format('log', message))
+ finally:
+ os.umask(mask)
+
+ return enable
+
+
+def enabled(flag):
+ """
+ a flag can be set by touching the file in /tmp or /config
+
+ The current flags are:
+ - developer: the code will drop into PBD on un-handled exception
+ - log: the code will log all command to a file
+ - ifconfig: when modifying an interface,
+ prints command with result and sysfs access on stdout for interface
+ - command: print command run with result
+
+ Having the flag setup on the filesystem is required to have
+ debuging at boot time, however, setting the flag via environment
+ does not require a seek to the filesystem and is more efficient
+ it can be done on the shell on via .bashrc for the user
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns either the file or environment name used to set it up
+ """
+
+ # this is to force all new flags to be registered here to be
+ # documented both here and a reminder to update readthedocs :-)
+ if flag not in ['developer', 'log', 'ifconfig', 'command']:
+ return ''
+
+ return _fromenv(flag) or _fromfile(flag)
+
+
+def _format(flag, message):
+ """
+ format a log message
+ """
+ return f'DEBUG/{flag.upper():<7} {message}\n'
+
+
+def _fromenv(flag):
+ """
+ check if debugging is set for this flag via environment
+
+ For a given debug flag named "test"
+ The presence of the environment VYOS_TEST_DEBUG (uppercase) enables it
+
+ return empty string if not
+ return content of env value it is
+ """
+
+ flagname = f'VYOS_{flag.upper()}_DEBUG'
+ flagenv = os.environ.get(flagname, None)
+
+ if flagenv is None:
+ return ''
+ return flagenv
+
+
+def _fromfile(flag):
+ """
+ Check if debug exist for a given debug flag name
+
+ Check is a debug flag was set by the user. the flag can be set either:
+ - in /tmp for a non-persistent presence between reboot
+ - in /config for always on (an existence at boot time)
+
+ For a given debug flag named "test"
+ The presence of the file vyos.test.debug (all lowercase) enables it
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns the full flagname
+ """
+
+ for folder in ('/tmp', '/config'):
+ flagfile = f'{folder}/vyos.{flag}.debug'
+ if os.path.isfile(flagfile):
+ return flagfile
+
+ return ''
+
+
+def _contentenv(flag):
+ return os.environ.get(f'VYOS_{flag.upper()}_DEBUG', '').strip()
+
+
+def _contentfile(flag):
+ """
+ Check if debug exist for a given debug flag name
+
+ Check is a debug flag was set by the user. the flag can be set either:
+ - in /tmp for a non-persistent presence between reboot
+ - in /config for always on (an existence at boot time)
+
+ For a given debug flag named "test"
+ The presence of the file vyos.test.debug (all lowercase) enables it
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns the full flagname
+ """
+
+ for folder in ('/tmp', '/config'):
+ flagfile = f'{folder}/vyos.{flag}.debug'
+ if not os.path.isfile(flagfile):
+ continue
+ with open(flagfile) as f:
+ return f.readline().strip()
+
+ return ''
+
+
+def _logfile(flag, default):
+ """
+ return the name of the file to use for logging when the flag 'log' is set
+ if it could not be established or the location is invalid it returns
+ an empty string
+ """
+
+ # For log we return the location of the log file
+ log_location = _contentenv(flag) or _contentfile(flag)
+
+ # it was not set
+ if not log_location:
+ return ''
+
+ # Make sure that the logs can only be in /tmp, /var/log, or /tmp
+ if not log_location.startswith('/tmp/') and \
+ not log_location.startswith('/config/') and \
+ not log_location.startswith('/var/log/'):
+ return default
+ if '..' in log_location:
+ return default
+ return log_location
diff --git a/python/vyos/ifconfig/__init__.py b/python/vyos/ifconfig/__init__.py
index 1f9956af0..cd1696ca1 100644
--- a/python/vyos/ifconfig/__init__.py
+++ b/python/vyos/ifconfig/__init__.py
@@ -14,6 +14,7 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+from vyos.ifconfig.section import Section
from vyos.ifconfig.interface import Interface
from vyos.ifconfig.bond import BondIf
diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py
index c7a2fa2d6..7bb63beed 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -16,12 +16,13 @@
import os
-from vyos.util import debug, debug_msg
-from vyos.util import popen, cmd
-from vyos.ifconfig.register import Register
+from vyos import debug
+from vyos.util import popen
+from vyos.util import cmd
+from vyos.ifconfig.section import Section
-class Control(Register):
+class Control(Section):
_command_get = {}
_command_set = {}
@@ -35,10 +36,10 @@ class Control(Register):
# if debug is not explicitely disabled the the config, enable it
self.debug = ''
if kargs.get('debug', True):
- self.debug = debug('ifconfig')
+ self.debug = debug.enabled('ifconfig')
def _debug_msg (self, message):
- return debug_msg(message, self.debug)
+ return debug.message(message, self.debug)
def _popen(self, command):
return popen(command, self.debug)
diff --git a/python/vyos/ifconfig/input.py b/python/vyos/ifconfig/input.py
new file mode 100644
index 000000000..bfab36335
--- /dev/null
+++ b/python/vyos/ifconfig/input.py
@@ -0,0 +1,31 @@
+# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+
+from vyos.ifconfig.interface import Interface
+
+
+@Interface.register
+class InputIf(Interface):
+ default = {
+ 'type': '',
+ }
+ definition = {
+ **Interface.definition,
+ **{
+ 'section': 'input',
+ 'prefixes': ['ifb', ],
+ },
+ }
diff --git a/python/vyos/ifconfig/register.py b/python/vyos/ifconfig/section.py
index c90782b70..ab340d247 100644
--- a/python/vyos/ifconfig/register.py
+++ b/python/vyos/ifconfig/section.py
@@ -16,9 +16,10 @@
import netifaces
-class Register:
+class Section:
# the known interface prefixes
_prefixes = {}
+ _classes = []
# class need to define: definition['prefixes']
# the interface prefixes declared by a class used to name interface with
@@ -26,9 +27,16 @@ class Register:
@classmethod
def register(cls, klass):
+ """
+ A function to use as decorator the interfaces classes
+ It register the prefix for the interface (eth, dum, vxlan, ...)
+ with the class which can handle it (EthernetIf, DummyIf,VXLANIf, ...)
+ """
if not klass.definition.get('prefixes',[]):
raise RuntimeError(f'valid interface prefixes not defined for {klass.__name__}')
+ cls._classes.append(klass)
+
for ifprefix in klass.definition['prefixes']:
if ifprefix in cls._prefixes:
raise RuntimeError(f'only one class can be registered for prefix "{ifprefix}" type')
@@ -38,7 +46,11 @@ class Register:
@classmethod
def _basename (cls, name, vlan):
- # remove number from interface name
+ """
+ remove the number at the end of interface name
+ name: name of the interface
+ vlan: if vlan is True, do not stop at the vlan number
+ """
name = name.rstrip('0123456789')
name = name.rstrip('.')
if vlan:
@@ -47,15 +59,13 @@ class Register:
@classmethod
def section(cls, name, vlan=True):
- # return the name of a section an interface should be under
+ """
+ return the name of a section an interface should be under
+ name: name of the interface (eth0, dum1, ...)
+ vlan: should we try try to remove the VLAN from the number
+ """
name = cls._basename(name, vlan)
- # XXX: To leave as long as vti and input are not moved to vyos
- if name == 'vti':
- return 'vti'
- if name == 'ifb':
- return 'input'
-
if name in cls._prefixes:
return cls._prefixes[name].definition['section']
return ''
@@ -68,15 +78,13 @@ class Register:
raise ValueError(f'No type found for interface name: {name}')
@classmethod
- def _listing (cls,section=''):
+ def _intf_under_section (cls,section=''):
+ """
+ return a generator with the name of the interface which are under a section
+ """
interfaces = netifaces.interfaces()
for ifname in interfaces:
- # XXX: Temporary hack as vti and input are not yet moved from vyatta to vyos
- if ifname.startswith('vti') or ifname.startswith('input'):
- yield ifname
- continue
-
ifsection = cls.section(ifname)
if not ifsection:
continue
@@ -87,9 +95,37 @@ class Register:
yield ifname
@classmethod
- def listing(cls, section=''):
- return list(cls._listing(section))
+ def interfaces(cls, section=''):
+ """
+ return a list of the name of the interface which are under a section
+ if no section is provided, then it returns all configured interfaces
+ """
+ return list(cls._intf_under_section(section))
+ @classmethod
+ def _intf_with_feature(cls, feature=''):
+ """
+ return a generator with the name of the interface which have
+ a particular feature set in their definition such as:
+ bondable, broadcast, bridgeable, ...
+ """
+ for klass in cls._classes:
+ if klass.definition[feature]:
+ yield klass.definition['section']
-# XXX: TODO - limit name for VRF interfaces
+ @classmethod
+ def feature(cls, feature=''):
+ """
+ return list with the name of the interface which have
+ a particular feature set in their definition such as:
+ bondable, broadcast, bridgeable, ...
+ """
+ return list(cls._intf_with_feature(feature))
+ @classmethod
+ def reserved(cls):
+ """
+ return list with the interface name prefixes
+ eth, lo, vxlan, dum, ...
+ """
+ return list(cls._prefixes.keys())
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index 05060669a..009a53a82 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -143,7 +143,7 @@ class GREIf(_Tunnel):
options = ['local', 'remote', 'ttl', 'tos', 'key']
updates = ['local', 'remote', 'ttl', 'tos',
- 'multicast', 'allmulticast']
+ 'mtu', 'multicast', 'allmulticast']
create = 'ip tunnel add {ifname} mode {type}'
change = 'ip tunnel cha {ifname}'
@@ -167,7 +167,7 @@ class GRETapIf(_Tunnel):
required = ['local', ]
options = ['local', 'remote', ]
- updates = []
+ updates = ['mtu', ]
create = 'ip link add {ifname} type {type}'
change = ''
@@ -193,7 +193,7 @@ class IP6GREIf(_Tunnel):
'hoplimit', 'tclass', 'flowlabel']
updates = ['local', 'remote', 'encaplimit',
'hoplimit', 'tclass', 'flowlabel',
- 'multicast', 'allmulticast']
+ 'mtu', 'multicast', 'allmulticast']
create = 'ip tunnel add {ifname} mode {type}'
change = 'ip tunnel cha {ifname} mode {type}'
@@ -227,7 +227,7 @@ class IPIPIf(_Tunnel):
options = ['local', 'remote', 'ttl', 'tos', 'key']
updates = ['local', 'remote', 'ttl', 'tos',
- 'multicast', 'allmulticast']
+ 'mtu', 'multicast', 'allmulticast']
create = 'ip tunnel add {ifname} mode {type}'
change = 'ip tunnel cha {ifname}'
@@ -252,7 +252,7 @@ class IPIP6If(_Tunnel):
'hoplimit', 'tclass', 'flowlabel']
updates = ['local', 'remote', 'encaplimit',
'hoplimit', 'tclass', 'flowlabel',
- 'multicast', 'allmulticast']
+ 'mtu', 'multicast', 'allmulticast']
create = 'ip -6 tunnel add {ifname} mode {type}'
change = 'ip -6 tunnel cha {ifname}'
@@ -288,7 +288,7 @@ class SitIf(_Tunnel):
options = ['local', 'remote', 'ttl', 'tos', 'key']
updates = ['local', 'remote', 'ttl', 'tos',
- 'multicast', 'allmulticast']
+ 'mtu', 'multicast', 'allmulticast']
create = 'ip tunnel add {ifname} mode {type}'
change = 'ip tunnel cha {ifname}'
@@ -309,7 +309,7 @@ class Sit6RDIf(SitIf):
# TODO: check if key can really be used with 6RD
options = ['remote', 'ttl', 'tos', 'key', '6rd-prefix', '6rd-relay-prefix']
updates = ['remote', 'ttl', 'tos',
- 'multicast', 'allmulticast']
+ 'mtu', 'multicast', 'allmulticast']
def _create(self):
# do not call _Tunnel.create, building fully here
diff --git a/python/vyos/ifconfig/vti.py b/python/vyos/ifconfig/vti.py
new file mode 100644
index 000000000..56ebe01d1
--- /dev/null
+++ b/python/vyos/ifconfig/vti.py
@@ -0,0 +1,31 @@
+# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+
+from vyos.ifconfig.interface import Interface
+
+
+@Interface.register
+class VTIIf(Interface):
+ default = {
+ 'type': 'vti',
+ }
+ definition = {
+ **Interface.definition,
+ **{
+ 'section': 'vti',
+ 'prefixes': ['vti', ],
+ },
+ }
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index f918461d1..1b4d3876e 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -18,7 +18,8 @@ import os
import re
import fileinput
-from vyos.util import cmd, DEVNULL
+from vyos.util import cmd
+from vyos.util import DEVNULL
def check_and_add_host_key(host_name):
@@ -31,10 +32,10 @@ def check_and_add_host_key(host_name):
mode = 0o600
os.mknod(known_hosts, 0o600)
- keyscan_cmd = 'ssh-keyscan -t rsa {} 2>/dev/null'.format(host_name)
+ keyscan_cmd = 'ssh-keyscan -t rsa {}'.format(host_name)
try:
- host_key = cmd(keyscan_cmd, shell=True, stderr=DEVNULL)
+ host_key = cmd(keyscan_cmd, stderr=DEVNULL)
except OSError:
sys.exit("Can not get RSA host key")
@@ -61,9 +62,9 @@ def check_and_add_host_key(host_name):
print("Host key has changed!")
print("If you trust the host key fingerprint below, continue.")
- fingerprint_cmd = 'ssh-keygen -lf /dev/stdin <<< "{}"'.format(host_key)
+ fingerprint_cmd = 'ssh-keygen -lf /dev/stdin'
try:
- fingerprint = cmd(fingerprint_cmd, shell=True, stderr=DEVNULL)
+ fingerprint = cmd(fingerprint_cmd, stderr=DEVNULL, input=host_key)
except OSError:
sys.exit("Can not get RSA host key fingerprint.")
@@ -125,7 +126,7 @@ 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 = cmd(curl_cmd, shell=True)
+ curl_output = cmd(curl_cmd)
except OSError:
sys.exit(1)
@@ -142,6 +143,6 @@ def get_remote_config(remote_file):
curl_cmd = 'curl {0} -# {1}'.format(redirect_opt, remote_file)
try:
- return cmd(curl_cmd, shell=True, stderr=None)
+ return cmd(curl_cmd, stderr=None)
except OSError:
return None
diff --git a/python/vyos/template.py b/python/vyos/template.py
index e559120c0..6c73ce753 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -22,10 +22,17 @@ from vyos.defaults import directories
# reuse the same Environment to improve performance
-_templates_env = Environment(loader=FileSystemLoader(directories['templates']))
-_templates_mem = {}
+_templates_env = {
+ False: Environment(loader=FileSystemLoader(directories['templates'])),
+ True: Environment(loader=FileSystemLoader(directories['templates']), trim_blocks=True),
+}
+_templates_mem = {
+ False: {},
+ True: {},
+}
-def render(destination, template, content):
+
+def render(destination, template, content, trim_blocks=False, formater=None):
"""
render a template from the template directory, it will raise on any errors
destination: the file where the rendered template must be saved
@@ -41,15 +48,18 @@ def render(destination, template, content):
# Setup a renderer for the given template
# This is cached and re-used for performance
- if template not in _templates_mem:
- _templates_mem[template] = _templates_env.get_template(template)
- template = _templates_mem[template]
+ if template not in _templates_mem[trim_blocks]:
+ _templates_mem[trim_blocks][template] = _templates_env[trim_blocks].get_template(template)
+ template = _templates_mem[trim_blocks][template]
# As we are opening the file with 'w', we are performing the rendering
# before calling open() to not accidentally erase the file if the
# templating fails
content = template.render(content)
+ if formater:
+ content = formater(content)
+
# Write client config file
with open(destination, 'w') as f:
f.write(content)
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 291ce64ea..49c47cd85 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -16,67 +16,121 @@
import os
import re
import sys
-from subprocess import Popen, PIPE, STDOUT, DEVNULL
+from subprocess import Popen
+from subprocess import PIPE
+from subprocess import STDOUT
+from subprocess import DEVNULL
-def debug(flag):
- # this is to force all new flags to be registered here so that
- # they can be documented:
- # - developer: the code will drop into PBD on un-handled exception
- # - ifconfig: prints command and sysfs access on stdout for interface
- if flag not in ['developer', 'ifconfig']:
- return ''
- return flag if os.path.isfile(f'/tmp/vyos.{flag}.debug') else ''
+from vyos import debug
+# There is many (too many) ways to run command with python
+# os.system, subprocess.Popen, subproces.{run,call,check_output}
+# which all have slighty different behaviour
-def debug_msg(message, section=''):
- if debug(section):
- print(f'DEBUG/{section:<6} {message}')
+def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=PIPE, stderr=None, decode=None):
+ """
+ popen is a wrapper helper aound subprocess.Popen
+ with it default setting it will return a tuple (out, err)
+ out: the output of the program run
+ err: the error code returned by the program
+
+ it can be affected by the following flags:
+ shell: do not try to auto-detect if a shell is required
+ for example if a pipe (|) or redirection (>, >>) is used
+ input: data to sent to the child process via STDIN
+ the data should be bytes but string will be converted
+ timeout: time after which the command will be considered to have failed
+ env: mapping that defines the environment variables for the new process
+ stdout: define how the output of the program should be handled
+ - PIPE (default), sends stdout to the output
+ - DEVNULL, discard the output
+ stderr: define how the output of the program should be handled
+ - None (default), send/merge the data to/with stderr
+ - PIPE, popen will append it to output
+ - STDOUT, send the data to be merged with stdout
+ - DEVNULL, discard the output
+ decode: specify the expected text encoding (utf-8, ascii, ...)
+
+ usage:
+ to get both stdout, and stderr: popen('command', stdout=PIPE, stderr=STDOUT)
+ to discard stdout and get stderr: popen('command', stdout=DEVNUL, stderr=PIPE)
+ """
+
+ # log if the flag is set, otherwise log if command is set
+ if not debug.enabled(flag):
+ flag = 'command'
+
+ cmd_msg = f"cmd '{command}'"
+ debug.message(cmd_msg, flag)
-def popen(command, section='', shell=None, input=None, timeout=None, env=None,
- universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None):
- """ popen does not raise, returns the output and error code of command """
use_shell = shell
+ stdin = None
if shell is None:
- use_shell = True if ' ' in command else False
+ use_shell = False
+ if ' ' in command:
+ use_shell = True
+ if env:
+ use_shell = True
+ if input:
+ stdin = PIPE
+ input = input.encode() if type(input) is str else input
p = Popen(
command,
- stdout=stdout, stderr=stderr,
+ stdin=stdin, 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(decode) if decode else tmp.decode()
+ tmp = p.communicate(input, timeout)
+ out1 = b''
+ out2 = b''
+ if stdout == PIPE:
+ out1 = tmp[0]
+ if stderr == PIPE:
+ out2 += tmp[1]
+ decoded1 = out1.decode(decode) if decode else out1.decode()
+ decoded2 = out2.decode(decode) if decode else out2.decode()
+ decoded1 = decoded1.replace('\r\n', '\n').strip()
+ decoded2 = decoded2.replace('\r\n', '\n').strip()
+ nl = '\n' if decoded1 and decoded2 else ''
+ decoded = decoded1 + nl + decoded2
if decoded:
- debug_msg(f"returned:\n{decoded}", section)
+ ret_msg = f"returned:\n{decoded}"
+ debug.message(ret_msg, flag)
return decoded, p.returncode
-def run(command, section='', shell=None, input=None, timeout=None, env=None,
- universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None):
- """ does not raise exception on error, returns error code """
+def run(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=DEVNULL, stderr=None, decode=None):
+ """
+ A wrapper around vyos.util.popen, which discard the stdout and
+ will return the error code of a command
+ """
_, code = popen(
- command, section,
+ command, flag,
stdout=stdout, stderr=stderr,
input=input, timeout=timeout,
env=env, shell=shell,
- universal_newlines=universal_newlines,
decode=decode,
)
return code
-def cmd(command, section='', shell=None, input=None, timeout=None, env=None,
- universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None,
+def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=PIPE, stderr=None, decode=None,
raising=None, message=''):
- """ does raise exception, returns output of command """
+ """
+ A wrapper around vyos.util.popen, which returns the stdout and
+ will raise the error code of a command
+
+ raising: specify which call should be used when raising (default is OSError)
+ the class should only require a string as parameter
+ """
decoded, code = popen(
- command, section,
+ command, flag,
stdout=stdout, stderr=stderr,
input=input, timeout=timeout,
env=env, shell=shell,
- universal_newlines=universal_newlines,
decode=decode,
)
if code != 0:
@@ -92,15 +146,17 @@ def cmd(command, section='', shell=None, input=None, timeout=None, env=None,
return decoded
-def call(command, section='', shell=None, input=None, timeout=None, env=None,
- universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None):
- """ does not raise exception on error, returns error code, print output """
+def call(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=PIPE, stderr=None, decode=None):
+ """
+ A wrapper around vyos.util.popen, which print the stdout and
+ will return the error code of a command
+ """
out, code = popen(
- command, section,
+ command, flag,
stdout=stdout, stderr=stderr,
input=input, timeout=timeout,
env=env, shell=shell,
- universal_newlines=universal_newlines,
decode=decode,
)
print(out)
@@ -124,6 +180,16 @@ def chown(path, user, group):
gid = getgrnam(group).gr_gid
os.chown(path, uid, gid)
+
+def chmod_600(path):
+ """ make file only read/writable by owner """
+ from stat import S_IRUSR, S_IWUSR
+
+ if os.path.exists(path):
+ bitmask = S_IRUSR | S_IWUSR
+ os.chmod(path, bitmask)
+
+
def chmod_750(path):
""" make file/directory only executable to user and group """
from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP
@@ -133,8 +199,8 @@ def chmod_750(path):
os.chmod(path, bitmask)
-def chmod_x(path):
- """ make file executable """
+def chmod_755(path):
+ """ make file executable by all """
from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
if os.path.exists(path):