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/debug.py182
-rw-r--r--python/vyos/ifconfig/control.py9
-rw-r--r--python/vyos/util.py86
4 files changed, 221 insertions, 61 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/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/control.py b/python/vyos/ifconfig/control.py
index 464cd585e..7bb63beed 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -16,8 +16,9 @@
import os
-from vyos.util import debug, debug_msg
-from vyos.util import popen, cmd
+from vyos import debug
+from vyos.util import popen
+from vyos.util import cmd
from vyos.ifconfig.section import Section
@@ -35,10 +36,10 @@ class Control(Section):
# 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/util.py b/python/vyos/util.py
index 14020e2d9..eb78c4a26 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -14,61 +14,16 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
-import sys
-from subprocess import Popen
-from subprocess import PIPE
-from subprocess import STDOUT
-from subprocess import DEVNULL
-
-
-def debug(flag):
- """
- Check is a debug flag was set by the user.
- a flag can be set by touching the file /tmp/vyos.flag.debug
- with flag being the flag name, the current flags are:
- - developer: the code will drop into PBD on un-handled exception
- - ifconfig: prints command and sysfs access on stdout for interface
- The function returns an empty string if the flag was not set,
- """
-
- # this is to force all new flags to be registered here to be documented:
- if flag not in ['developer', 'ifconfig']:
- return ''
- for folder in ('/tmp', '/config'):
- if os.path.isfile(f'{folder}/vyos.{flag}.debug'):
- return flag
- return ''
-
-
-def debug_msg(message, flag=''):
- """
- print a debug message line on stdout if debugging is enabled for the flag
- """
-
- if debug(flag):
- print(f'DEBUG/{flag:<6} {message}')
-
- if not debug('developer'):
- return
-
- logfile = '/tmp/full-log'
- existed = os.path.exists(logfile)
-
- with open(logfile, 'a') as f:
- f.write(f'DEBUG/{flag:<6} {message}\n')
- if not existed:
- # at boot the file is created as root:vyattacfg
- # at runtime the file is created as user:vyattacfg
- # do not use run/cmd to not have a recursive call to this code
- os.system(f'chmod g+w {logfile}')
+#
+# NOTE: Do not import full classes here, move your import to the function
+# where it is used so it is as local as possible to the execution
+#
# 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
-
-
+from subprocess import Popen, PIPE, STDOUT, DEVNULL
def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
stdout=PIPE, stderr=None, decode=None):
"""
@@ -98,7 +53,14 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
to get both stdout, and stderr: popen('command', stdout=PIPE, stderr=STDOUT)
to discard stdout and get stderr: popen('command', stdout=DEVNUL, stderr=PIPE)
"""
- debug_msg(f"cmd '{command}'", flag)
+ from vyos import debug
+ # 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)
+
use_shell = shell
stdin = None
if shell is None:
@@ -129,7 +91,8 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
nl = '\n' if decoded1 and decoded2 else ''
decoded = decoded1 + nl + decoded2
if decoded:
- debug_msg(f"returned:\n{decoded}", flag)
+ ret_msg = f"returned:\n{decoded}"
+ debug.message(ret_msg, flag)
return decoded, p.returncode
@@ -262,6 +225,7 @@ def colon_separated_to_dict(data_string, uniquekeys=False):
If uniquekeys=True, then dict entries are always strings,
otherwise they are always lists of strings.
"""
+ import re
key_value_re = re.compile('([^:]+)\s*\:\s*(.*)')
data_raw = re.split('\n', data_string)
@@ -301,6 +265,17 @@ def process_running(pid_file):
return pid_exists(int(pid))
+def process_named_running(name):
+ """ Checks if process with given name is running and returns its PID.
+ If Process is not running, return None
+ """
+ from psutil import process_iter
+ for p in process_iter():
+ if name in p.name():
+ return p.pid
+ return None
+
+
def seconds_to_human(s, separator=""):
""" Converts number of seconds passed to a human-readable
interval such as 1w4d18h35m59s
@@ -350,6 +325,7 @@ def get_cfg_group_id():
def file_is_persistent(path):
+ import re
if not re.match(r'^(/config|/opt/vyatta/etc/config)', os.path.dirname(path)):
warning = "Warning: file {0} is outside the /config directory\n".format(path)
warning += "It will not be automatically migrated to a new image on system update"
@@ -406,9 +382,10 @@ def wait_for_commit_lock():
def ask_yes_no(question, default=False) -> bool:
"""Ask a yes/no question via input() and return their answer."""
+ from sys import stdout
default_msg = "[Y/n]" if default else "[y/N]"
while True:
- sys.stdout.write("%s %s " % (question, default_msg))
+ stdout.write("%s %s " % (question, default_msg))
c = input().lower()
if c == '':
return default
@@ -417,7 +394,7 @@ def ask_yes_no(question, default=False) -> bool:
elif c in ("n", "no"):
return False
else:
- sys.stdout.write("Please respond with yes/y or no/n\n")
+ stdout.write("Please respond with yes/y or no/n\n")
def is_admin() -> bool:
@@ -435,6 +412,7 @@ def mac2eui64(mac, prefix=None):
IPv6 address.
Thankfully copied from https://gist.github.com/wido/f5e32576bb57b5cc6f934e177a37a0d3
"""
+ import re
from ipaddress import ip_network
# http://tools.ietf.org/html/rfc4291#section-2.5.1
eui64 = re.sub(r'[.:-]', '', mac).lower()