summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/vyos/config_mgmt.py12
-rw-r--r--python/vyos/configsession.py33
-rw-r--r--python/vyos/configverify.py19
-rw-r--r--python/vyos/defaults.py14
-rwxr-xr-xpython/vyos/template.py20
-rw-r--r--python/vyos/utils/commit.py27
-rw-r--r--python/vyos/utils/file.py21
-rw-r--r--python/vyos/vyconf_session.py7
8 files changed, 138 insertions, 15 deletions
diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py
index dd8910afb..23eb3666e 100644
--- a/python/vyos/config_mgmt.py
+++ b/python/vyos/config_mgmt.py
@@ -44,6 +44,7 @@ from vyos.utils.io import ask_yes_no
from vyos.utils.boot import boot_configuration_complete
from vyos.utils.process import is_systemd_service_active
from vyos.utils.process import rc_cmd
+from vyos.defaults import DEFAULT_COMMIT_CONFIRM_MINUTES
SAVE_CONFIG = '/usr/libexec/vyos/vyos-save-config.py'
config_json = '/run/vyatta/config/config.json'
@@ -56,7 +57,6 @@ commit_hooks = {
'commit_archive': '02vyos-commit-archive',
}
-DEFAULT_TIME_MINUTES = 10
timer_name = 'commit-confirm'
config_file = os.path.join(directories['config'], 'config.boot')
@@ -144,14 +144,16 @@ class ConfigMgmt:
['system', 'config-management'],
key_mangling=('-', '_'),
get_first_key=True,
- with_defaults=True,
+ with_recursive_defaults=True,
)
self.max_revisions = int(d.get('commit_revisions', 0))
self.num_revisions = 0
self.locations = d.get('commit_archive', {}).get('location', [])
self.source_address = d.get('commit_archive', {}).get('source_address', '')
- self.reboot_unconfirmed = bool(d.get('commit_confirm') == 'reboot')
+ self.reboot_unconfirmed = bool(
+ d.get('commit_confirm', {}).get('action') == 'reboot'
+ )
self.config_dict = d
if config.exists(['system', 'host-name']):
@@ -181,7 +183,7 @@ class ConfigMgmt:
# Console script functions
#
def commit_confirm(
- self, minutes: int = DEFAULT_TIME_MINUTES, no_prompt: bool = False
+ self, minutes: int = DEFAULT_COMMIT_CONFIRM_MINUTES, no_prompt: bool = False
) -> Tuple[str, int]:
"""Commit with reload/reboot to saved config in 'minutes' minutes if
'confirm' call is not issued.
@@ -805,7 +807,7 @@ def run():
'-t',
dest='minutes',
type=int,
- default=DEFAULT_TIME_MINUTES,
+ default=DEFAULT_COMMIT_CONFIRM_MINUTES,
help="Minutes until reboot, unless 'confirm'",
)
commit_confirm.add_argument(
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index 1b19c68b4..f0d636b89 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -25,6 +25,7 @@ from vyos.utils.boot import boot_configuration_complete
from vyos.utils.backend import vyconf_backend
from vyos.vyconf_session import VyconfSession
from vyos.base import Warning as Warn
+from vyos.defaults import DEFAULT_COMMIT_CONFIRM_MINUTES
CLI_SHELL_API = '/bin/cli-shell-api'
@@ -32,10 +33,13 @@ SET = '/opt/vyatta/sbin/my_set'
DELETE = '/opt/vyatta/sbin/my_delete'
COMMENT = '/opt/vyatta/sbin/my_comment'
COMMIT = '/opt/vyatta/sbin/my_commit'
+COMMIT_CONFIRM = ['/usr/bin/config-mgmt', 'commit_confirm', '-y']
+CONFIRM = ['/usr/bin/config-mgmt', 'confirm']
DISCARD = '/opt/vyatta/sbin/my_discard'
SHOW_CONFIG = ['/bin/cli-shell-api', 'showConfig']
LOAD_CONFIG = ['/bin/cli-shell-api', 'loadFile']
MIGRATE_LOAD_CONFIG = ['/usr/libexec/vyos/vyos-load-config.py']
+MERGE_CONFIG = ['/usr/libexec/vyos/vyos-merge-config.py']
SAVE_CONFIG = ['/usr/libexec/vyos/vyos-save-config.py']
INSTALL_IMAGE = [
'/usr/libexec/vyos/op_mode/image_installer.py',
@@ -68,6 +72,7 @@ GENERATE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'generate']
SHOW = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'show']
RESET = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'reset']
REBOOT = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'reboot']
+RENEW = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'renew']
POWEROFF = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'poweroff']
OP_CMD_ADD = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'add']
OP_CMD_DELETE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'delete']
@@ -298,6 +303,22 @@ class ConfigSession(object):
return out
+ def commit_confirm(self, minutes: int = DEFAULT_COMMIT_CONFIRM_MINUTES):
+ if self._vyconf_session is None:
+ out = self.__run_command(COMMIT_CONFIRM + [f'-t {minutes}'])
+ else:
+ out = 'unimplemented'
+
+ return out
+
+ def confirm(self):
+ if self._vyconf_session is None:
+ out = self.__run_command(CONFIRM)
+ else:
+ out = 'unimplemented'
+
+ return out
+
def discard(self):
if self._vyconf_session is None:
self.__run_command([DISCARD])
@@ -338,6 +359,14 @@ class ConfigSession(object):
return out
+ def merge_config(self, file_path):
+ if self._vyconf_session is None:
+ out = self.__run_command(MERGE_CONFIG + [file_path])
+ else:
+ out = 'unimplemented'
+
+ return out
+
def save_config(self, file_path):
if self._vyconf_session is None:
out = self.__run_command(SAVE_CONFIG + [file_path])
@@ -384,6 +413,10 @@ class ConfigSession(object):
out = self.__run_command(RESET + path)
return out
+ def renew(self, path):
+ out = self.__run_command(RENEW + path)
+ return out
+
def poweroff(self, path):
out = self.__run_command(POWEROFF + path)
return out
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index d5f443f15..07eb29a68 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -527,6 +527,25 @@ def verify_pki_dh_parameters(config: dict, dh_name: str, min_key_size: int=0):
if dh_bits < min_key_size:
raise ConfigError(f'Minimum DH key-size is {min_key_size} bits!')
+def verify_pki_openssh_key(config: dict, key_name: str):
+ """
+ Common helper function user by PKI consumers to perform recurring
+ validation functions on OpenSSH keys
+ """
+ if 'pki' not in config:
+ raise ConfigError('PKI is not configured!')
+
+ if 'openssh' not in config['pki']:
+ raise ConfigError('PKI does not contain any OpenSSH keys!')
+
+ if key_name not in config['pki']['openssh']:
+ raise ConfigError(f'OpenSSH key "{key_name}" not found in configuration!')
+
+ if 'public' in config['pki']['openssh'][key_name]:
+ if not {'key', 'type'} <= set(config['pki']['openssh'][key_name]['public']):
+ raise ConfigError('Both public key and type must be defined for '\
+ f'OpenSSH public key "{key_name}"!')
+
def verify_eapol(config: dict):
"""
Common helper function used by interface implementations to perform
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index fbde0298b..f84b14040 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -53,6 +53,10 @@ internal_ports = {
'certbot_haproxy' : 65080, # Certbot running behing haproxy
}
+config_files = {
+ 'sshd_user_ca' : '/run/sshd/trusted_user_ca',
+}
+
config_status = '/tmp/vyos-config-status'
api_config_state = '/run/http-api-state'
frr_debug_enable = '/tmp/vyos.frr.debug'
@@ -69,8 +73,8 @@ config_default = os.path.join(directories['data'], 'config.boot.default')
rt_symbolic_names = {
# Standard routing tables for Linux & reserved IDs for VyOS
- 'default': 253, # Confusingly, a final fallthru, not the default.
- 'main': 254, # The actual global table used by iproute2 unless told otherwise.
+ 'default': 253, # Confusingly, a final fallthru, not the default.
+ 'main': 254, # The actual global table used by iproute2 unless told otherwise.
'local': 255, # Special kernel loopback table.
}
@@ -78,3 +82,9 @@ rt_global_vrf = rt_symbolic_names['main']
rt_global_table = rt_symbolic_names['main']
vyconfd_conf = '/etc/vyos/vyconfd.conf'
+
+DEFAULT_COMMIT_CONFIRM_MINUTES = 10
+
+commit_hooks = {'pre': '/etc/commit/pre-hooks.d',
+ 'post': '/etc/commit/post-hooks.d'
+ }
diff --git a/python/vyos/template.py b/python/vyos/template.py
index aa215db95..bf7928914 100755
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -1079,7 +1079,7 @@ def vyos_defined(value, test_value=None, var_type=None):
def get_default_port(service):
"""
Jinja2 plugin to retrieve common service port number from vyos.defaults
- class form a Jinja2 template. This removes the need to hardcode, or pass in
+ class from a Jinja2 template. This removes the need to hardcode, or pass in
the data using the general dictionary.
Added to remove code complexity and make it easier to read.
@@ -1092,3 +1092,21 @@ def get_default_port(service):
raise RuntimeError(f'Service "{service}" not found in internal ' \
'vyos.defaults.internal_ports dict!')
return internal_ports[service]
+
+@register_clever_function('get_default_config_file')
+def get_default_config_file(filename):
+ """
+ Jinja2 plugin to retrieve a common configuration file path from
+ vyos.defaults class from a Jinja2 template. This removes the need to
+ hardcode, or pass in the data using the general dictionary.
+
+ Added to remove code complexity and make it easier to read.
+
+ Example:
+ {{ get_default_config_file('certbot_haproxy') }}
+ """
+ from vyos.defaults import config_files
+ if filename not in config_files:
+ raise RuntimeError(f'Configuration file "{filename}" not found in '\
+ 'internal vyos.defaults.config_files dict!')
+ return config_files[filename]
diff --git a/python/vyos/utils/commit.py b/python/vyos/utils/commit.py
index 9167c78d2..fc259dadb 100644
--- a/python/vyos/utils/commit.py
+++ b/python/vyos/utils/commit.py
@@ -101,3 +101,30 @@ def release_commit_lock_file(file_descr):
return
fcntl.lockf(file_descr, fcntl.LOCK_UN)
file_descr.close()
+
+
+def call_commit_hooks(which: str):
+ import re
+ import os
+ from pathlib import Path
+ from vyos.defaults import commit_hooks
+ from vyos.utils.process import rc_cmd
+
+ if which not in list(commit_hooks):
+ raise ValueError(f'no entry {which} in commit_hooks')
+
+ hook_dir = commit_hooks[which]
+ file_list = list(Path(hook_dir).glob('*'))
+ regex = re.compile('^[a-zA-Z0-9._-]+$')
+ hook_list = sorted([str(f) for f in file_list if regex.match(f.name)])
+ err = False
+ out = ''
+ for runf in hook_list:
+ try:
+ e, o = rc_cmd(runf)
+ except FileNotFoundError:
+ continue
+ err = err | bool(e)
+ out = out + o
+
+ return out, int(err)
diff --git a/python/vyos/utils/file.py b/python/vyos/utils/file.py
index eaebb57a3..cc46d77d1 100644
--- a/python/vyos/utils/file.py
+++ b/python/vyos/utils/file.py
@@ -28,22 +28,28 @@ def file_is_persistent(path):
absolute = os.path.abspath(os.path.dirname(path))
return re.match(location,absolute)
-def read_file(fname, defaultonfailure=None):
+def read_file(fname, defaultonfailure=None, sudo=False):
"""
read the content of a file, stripping any end characters (space, newlines)
should defaultonfailure be not None, it is returned on failure to read
"""
try:
- """ Read a file to string """
- with open(fname, 'r') as f:
- data = f.read().strip()
- return data
+ # Some files can only be read by root - emulate sudo cat call
+ if sudo:
+ from vyos.utils.process import cmd
+ data = cmd(['sudo', 'cat', fname])
+ else:
+ # If not sudo, just read the file
+ with open(fname, 'r') as f:
+ data = f.read()
+ return data.strip()
except Exception as e:
if defaultonfailure is not None:
return defaultonfailure
raise e
-def write_file(fname, data, defaultonfailure=None, user=None, group=None, mode=None, append=False):
+def write_file(fname, data, defaultonfailure=None, user=None, group=None,
+ mode=None, append=False, trailing_newline=False):
"""
Write content of data to given fname, should defaultonfailure be not None,
it is returned on failure to read.
@@ -60,6 +66,9 @@ def write_file(fname, data, defaultonfailure=None, user=None, group=None, mode=N
bytes = 0
with open(fname, 'w' if not append else 'a') as f:
bytes = f.write(data)
+ if trailing_newline and not data.endswith('\n'):
+ f.write('\n')
+ bytes += 1
chown(fname, user, group)
chmod(fname, mode)
return bytes
diff --git a/python/vyos/vyconf_session.py b/python/vyos/vyconf_session.py
index 4250f0cfb..3cf847b6c 100644
--- a/python/vyos/vyconf_session.py
+++ b/python/vyos/vyconf_session.py
@@ -29,6 +29,7 @@ from vyos.utils.session import in_config_session
from vyos.proto.vyconf_proto import Errnum
from vyos.utils.commit import acquire_commit_lock_file
from vyos.utils.commit import release_commit_lock_file
+from vyos.utils.commit import call_commit_hooks
class VyconfSessionError(Exception):
@@ -145,10 +146,14 @@ class VyconfSession:
if lock_fd is None:
return out, Errnum.COMMIT_IN_PROGRESS
+ pre_out, _ = call_commit_hooks('pre')
out = vyconf_client.send_request('commit', token=self.__token)
+ os.environ['COMMIT_STATUS'] = 'FAILURE' if out.status else 'SUCCESS'
+ post_out, _ = call_commit_hooks('post')
+
release_commit_lock_file(lock_fd)
- return self.output(out), out.status
+ return pre_out + self.output(out) + post_out, out.status
@raise_exception
@config_mode