summaryrefslogtreecommitdiff
path: root/python/vyos/configsession.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/vyos/configsession.py')
-rw-r--r--python/vyos/configsession.py221
1 files changed, 190 insertions, 31 deletions
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index 7d51b94e4..50f93f890 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2019-2024 VyOS maintainers and contributors
+# Copyright 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;
@@ -21,36 +21,75 @@ import subprocess
from vyos.defaults import directories
from vyos.utils.process import is_systemd_service_running
from vyos.utils.dict import dict_to_paths
+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'
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',
- '--action', 'add', '--no-prompt', '--image-path']
+INSTALL_IMAGE = [
+ '/usr/libexec/vyos/op_mode/image_installer.py',
+ '--action',
+ 'add',
+ '--no-prompt',
+ '--image-path',
+]
IMPORT_PKI = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'import']
-IMPORT_PKI_NO_PROMPT = ['/usr/libexec/vyos/op_mode/pki.py',
- '--action', 'import', '--no-prompt']
-REMOVE_IMAGE = ['/usr/libexec/vyos/op_mode/image_manager.py',
- '--action', 'delete', '--no-prompt', '--image-name']
-SET_DEFAULT_IMAGE = ['/usr/libexec/vyos/op_mode/image_manager.py',
- '--action', 'set', '--no-prompt', '--image-name']
+IMPORT_PKI_NO_PROMPT = [
+ '/usr/libexec/vyos/op_mode/pki.py',
+ 'import_pki',
+ '--no-prompt',
+]
+REMOVE_IMAGE = [
+ '/usr/libexec/vyos/op_mode/image_manager.py',
+ '--action',
+ 'delete',
+ '--no-prompt',
+ '--image-name',
+]
+SET_DEFAULT_IMAGE = [
+ '/usr/libexec/vyos/op_mode/image_manager.py',
+ '--action',
+ 'set',
+ '--no-prompt',
+ '--image-name',
+]
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']
+TRACEROUTE = [
+ '/usr/libexec/vyos/op_mode/mtr_execute.py',
+ 'mtr',
+ '--for-api',
+ '--report-mode',
+ '--report-cycles',
+ '1',
+ '--json',
+ '--host',
+]
# Default "commit via" string
-APP = "vyos-http-api"
+APP = 'vyos-http-api'
+
# When started as a service rather than from a user shell,
# the process lacks the VyOS-specific environment that comes
@@ -61,7 +100,7 @@ def inject_vyos_env(env):
env['VYATTA_USER_LEVEL_DIR'] = '/opt/vyatta/etc/shell/level/admin'
env['VYATTA_PROCESS_CLIENT'] = 'gui2_rest'
env['VYOS_HEADLESS_CLIENT'] = 'vyos_http_api'
- env['vyatta_bindir']= '/opt/vyatta/bin'
+ env['vyatta_bindir'] = '/opt/vyatta/bin'
env['vyatta_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates'
env['vyatta_configdir'] = directories['vyos_configdir']
env['vyatta_datadir'] = '/opt/vyatta/share'
@@ -78,7 +117,7 @@ def inject_vyos_env(env):
env['vyos_configdir'] = directories['vyos_configdir']
env['vyos_conf_scripts_dir'] = '/usr/libexec/vyos/conf_mode'
env['vyos_datadir'] = '/opt/vyatta/share'
- env['vyos_datarootdir']= '/opt/vyatta/share'
+ env['vyos_datarootdir'] = '/opt/vyatta/share'
env['vyos_libdir'] = '/opt/vyatta/lib'
env['vyos_libexec_dir'] = '/usr/libexec/vyos'
env['vyos_op_scripts_dir'] = '/usr/libexec/vyos/op_mode'
@@ -87,6 +126,10 @@ def inject_vyos_env(env):
env['vyos_sbin_dir'] = '/usr/sbin'
env['vyos_validators_dir'] = '/usr/libexec/vyos/validators'
+ # with the retirement of the Cstore backend, this will remain as the
+ # sole indication of legacy CLI config mode, as checked by VyconfSession
+ env['_OFR_CONFIGURE'] = 'ok'
+
# if running the vyos-configd daemon, inject the vyshim env var
if is_systemd_service_running('vyos-configd.service'):
env['vyshim'] = '/usr/sbin/vyshim'
@@ -102,7 +145,8 @@ class ConfigSession(object):
"""
The write API of VyOS.
"""
- def __init__(self, session_id, app=APP):
+
+ def __init__(self, session_id, app=APP, shared=False):
"""
Creates a new config session.
@@ -116,7 +160,9 @@ class ConfigSession(object):
and used the PID for the session identifier.
"""
- env_str = subprocess.check_output([CLI_SHELL_API, 'getSessionEnv', str(session_id)])
+ env_str = subprocess.check_output(
+ [CLI_SHELL_API, 'getSessionEnv', str(session_id)]
+ )
self.__session_id = session_id
# Extract actual variables from the chunk of shell it outputs
@@ -128,21 +174,60 @@ class ConfigSession(object):
for k, v in env_list:
session_env[k] = v
+ session_env['CONFIGSESSION_PID'] = str(session_id)
+
self.__session_env = session_env
- self.__session_env["COMMIT_VIA"] = app
+ self.__session_env['COMMIT_VIA'] = app
self.__run_command([CLI_SHELL_API, 'setupSession'])
+ if vyconf_backend() and boot_configuration_complete():
+ self._vyconf_session = VyconfSession(
+ pid=session_id, on_error=ConfigSessionError
+ )
+ else:
+ self._vyconf_session = None
+
+ self.shared = shared
+
def __del__(self):
- try:
- output = subprocess.check_output([CLI_SHELL_API, 'teardownSession'], env=self.__session_env).decode().strip()
- if output:
- print("cli-shell-api teardownSession output for sesion {0}: {1}".format(self.__session_id, output), file=sys.stderr)
- except Exception as e:
- print("Could not tear down session {0}: {1}".format(self.__session_id, e), file=sys.stderr)
+ if self.shared:
+ return
+ if self._vyconf_session is None:
+ try:
+ output = (
+ subprocess.check_output(
+ [CLI_SHELL_API, 'teardownSession'], env=self.__session_env
+ )
+ .decode()
+ .strip()
+ )
+ if output:
+ print(
+ 'cli-shell-api teardownSession output for sesion {0}: {1}'.format(
+ self.__session_id, output
+ ),
+ file=sys.stderr,
+ )
+ except Exception as e:
+ print(
+ 'Could not tear down session {0}: {1}'.format(self.__session_id, e),
+ file=sys.stderr,
+ )
+ else:
+ if self._vyconf_session.session_changed():
+ Warn('Exiting with uncommitted changes')
+ self._vyconf_session.discard()
+ self._vyconf_session.exit_config_mode()
+ self._vyconf_session.teardown()
def __run_command(self, cmd_list):
- p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=self.__session_env)
+ p = subprocess.Popen(
+ cmd_list,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ env=self.__session_env,
+ )
(stdout_data, stderr_data) = p.communicate()
output = stdout_data.decode()
result = p.wait()
@@ -158,7 +243,10 @@ class ConfigSession(object):
value = []
else:
value = [value]
- self.__run_command([SET] + path + value)
+ if self._vyconf_session is None:
+ self.__run_command([SET] + path + value)
+ else:
+ self._vyconf_session.set(path + value)
def set_section(self, path: list, d: dict):
try:
@@ -172,7 +260,10 @@ class ConfigSession(object):
value = []
else:
value = [value]
- self.__run_command([DELETE] + path + value)
+ if self._vyconf_session is None:
+ self.__run_command([DELETE] + path + value)
+ else:
+ self._vyconf_session.delete(path + value)
def load_section(self, path: list, d: dict):
try:
@@ -204,34 +295,94 @@ class ConfigSession(object):
def comment(self, path, value=None):
if not value:
- value = [""]
+ value = ['']
else:
value = [value]
self.__run_command([COMMENT] + path + value)
def commit(self):
- out = self.__run_command([COMMIT])
+ if self._vyconf_session is None:
+ out = self.__run_command([COMMIT])
+ else:
+ out, _ = self._vyconf_session.commit()
+
+ 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):
- self.__run_command([DISCARD])
+ if self._vyconf_session is None:
+ self.__run_command([DISCARD])
+ else:
+ out, _ = self._vyconf_session.discard()
def show_config(self, path, format='raw'):
- config_data = self.__run_command(SHOW_CONFIG + path)
+ if self._vyconf_session is None:
+ config_data = self.__run_command(SHOW_CONFIG + path)
+ else:
+ config_data, _ = self._vyconf_session.show_config(path)
if format == 'raw':
return config_data
def load_config(self, file_path):
- out = self.__run_command(LOAD_CONFIG + [file_path])
+ if self._vyconf_session is None:
+ out = self.__run_command(LOAD_CONFIG + [file_path])
+ else:
+ out, _ = self._vyconf_session.load_config(file_name=file_path)
+
return out
+ def load_explicit(self, file_path):
+ from vyos.load_config import load
+ from vyos.load_config import LoadConfigError
+
+ try:
+ load(file_path, switch='explicit')
+ except LoadConfigError as e:
+ raise ConfigSessionError(e) from e
+
def migrate_and_load_config(self, file_path):
- out = self.__run_command(MIGRATE_LOAD_CONFIG + [file_path])
+ if self._vyconf_session is None:
+ out = self.__run_command(MIGRATE_LOAD_CONFIG + [file_path])
+ else:
+ out, _ = self._vyconf_session.load_config(file_name=file_path, migrate=True)
+
+ return out
+
+ def merge_config(self, file_path, destructive=False):
+ if self._vyconf_session is None:
+ destr = ['--destructive'] if destructive else []
+ out = self.__run_command(MERGE_CONFIG + [file_path] + destr)
+ else:
+ out, _ = self._vyconf_session.merge_config(
+ file_name=file_path, destructive=destructive
+ )
+
return out
def save_config(self, file_path):
- out = self.__run_command(SAVE_CONFIG + [file_path])
+ if self._vyconf_session is None:
+ out = self.__run_command(SAVE_CONFIG + [file_path])
+ else:
+ out, _ = self._vyconf_session.save_config(
+ file=file_path, append_version=True
+ )
+
return out
def install_image(self, url):
@@ -270,6 +421,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
@@ -285,3 +440,7 @@ class ConfigSession(object):
def show_container_image(self):
out = self.__run_command(SHOW + ['container', 'image'])
return out
+
+ def traceroute(self, host):
+ out = self.__run_command(TRACEROUTE + [host])
+ return out