From 8de29b3c35e4dc81ff5e4f92e666d760d251f6f0 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 16 Apr 2025 11:16:36 -0500 Subject: T7363: add check for config mode that is independent from Cstore The environment variable _OFR_CONFIGURE is used by bash completion to setup the config mode environment. We check this setting to coordinate vyconf config mode and CLI config mode, independent of the legacy backend Cstore check. --- python/vyos/utils/session.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 python/vyos/utils/session.py (limited to 'python') diff --git a/python/vyos/utils/session.py b/python/vyos/utils/session.py new file mode 100644 index 000000000..28559dc59 --- /dev/null +++ b/python/vyos/utils/session.py @@ -0,0 +1,25 @@ +# Copyright 2025 VyOS maintainers and contributors +# +# 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 . + +# pylint: disable=import-outside-toplevel + + +def in_config_session(): + """Vyatta bash completion uses the following environment variable for + indication of the config mode environment, independent of legacy backend + initialization of Cstore""" + from os import environ + + return '_OFR_CONFIGURE' in environ -- cgit v1.2.3 From 0cf87061b4216af395651aa5b935a5c320737de3 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 16 Apr 2025 11:21:59 -0500 Subject: T7363: use legacy environment variable to indicate config mode In the absence of Cstore, the env var remains as the sole indication of config mode for the legacy CLI, and its emulation here. --- python/vyos/configsession.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'python') diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index a3be29881..6490e6feb 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -1,4 +1,4 @@ -# Copyright (C) 2019-2024 VyOS maintainers and contributors +# Copyright (C) 2019-2025 VyOS maintainers and contributors # # 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; @@ -120,6 +120,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' -- cgit v1.2.3 From 553450438afedd12b6c44fe21eb4372c62d1a5a2 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 16 Apr 2025 15:02:45 -0500 Subject: T7363: add pid aware initialization --- python/vyos/configsession.py | 35 +++++++++++++++++++---------------- python/vyos/vyconf_session.py | 32 +++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 23 deletions(-) (limited to 'python') diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index 6490e6feb..a070736cd 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -179,26 +179,29 @@ class ConfigSession(object): self._vyconf_session = None def __del__(self): - try: - output = ( - subprocess.check_output( - [CLI_SHELL_API, 'teardownSession'], env=self.__session_env + if self._vyconf_session is None: + try: + output = ( + subprocess.check_output( + [CLI_SHELL_API, 'teardownSession'], env=self.__session_env + ) + .decode() + .strip() ) - .decode() - .strip() - ) - if output: + 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( - 'cli-shell-api teardownSession output for sesion {0}: {1}'.format( - self.__session_id, output - ), + 'Could not tear down session {0}: {1}'.format(self.__session_id, e), file=sys.stderr, ) - except Exception as e: - print( - 'Could not tear down session {0}: {1}'.format(self.__session_id, e), - file=sys.stderr, - ) + else: + self._vyconf_session.teardown() def __run_command(self, cmd_list): p = subprocess.Popen( diff --git a/python/vyos/vyconf_session.py b/python/vyos/vyconf_session.py index 506095625..cbf2fc873 100644 --- a/python/vyos/vyconf_session.py +++ b/python/vyos/vyconf_session.py @@ -15,6 +15,7 @@ # # +import os import tempfile import shutil from functools import wraps @@ -24,6 +25,7 @@ from vyos.proto import vyconf_client from vyos.migrate import ConfigMigrate from vyos.migrate import ConfigMigrateError from vyos.component_version import append_system_version +from vyos.utils.session import in_config_session def output(o): @@ -35,14 +37,35 @@ def output(o): class VyconfSession: - def __init__(self, token: str = None, on_error: Type[Exception] = None): + def __init__( + self, token: str = None, pid: int = None, on_error: Type[Exception] = None + ): + self.pid = os.getpid() if pid is None else pid if token is None: - out = vyconf_client.send_request('setup_session') + # CLI applications with arg pid=getppid() allow coordination + # with the ambient session; other uses (such as ConfigSession) + # may default to self pid + out = vyconf_client.send_request('session_of_pid', client_pid=self.pid) + if out.output is None: + out = vyconf_client.send_request('setup_session', client_pid=self.pid) self.__token = out.output else: + out = vyconf_client.send_request( + 'session_update_pid', token=token, client_pid=self.pid + ) + if out.status: + raise ValueError(f'No existing session for token: {token}') self.__token = token self.on_error = on_error + self.in_config_session = in_config_session() + + def __del__(self): + if not self.in_config_session: + self.teardown() + + def teardown(self): + vyconf_client.send_request('teardown', token=self.__token) @staticmethod def raise_exception(f): @@ -116,8 +139,3 @@ class VyconfSession: path = [] out = vyconf_client.send_request('show_config', token=self.__token, path=path) return output(out), out.status - - def __del__(self): - out = vyconf_client.send_request('teardown', token=self.__token) - if out.status: - print(f'Could not tear down session {self.__token}: {output(out)}') -- cgit v1.2.3 From 48f9544c72386bccb177ae213c13d4f68053bc06 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 2 May 2025 16:31:21 -0500 Subject: T7352: add util for enabling vyconf backend for smoketests --- python/vyos/configsession.py | 4 +- python/vyos/utils/backend.py | 88 +++++++++++++++++++++++++++++++++++++++ src/helpers/set_vyconf_backend.py | 39 +++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 python/vyos/utils/backend.py create mode 100755 src/helpers/set_vyconf_backend.py (limited to 'python') diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index a070736cd..8131e0ea3 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -22,9 +22,9 @@ 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 -vyconf_backend = False CLI_SHELL_API = '/bin/cli-shell-api' SET = '/opt/vyatta/sbin/my_set' @@ -173,7 +173,7 @@ class ConfigSession(object): self.__run_command([CLI_SHELL_API, 'setupSession']) - if vyconf_backend and boot_configuration_complete(): + if vyconf_backend() and boot_configuration_complete(): self._vyconf_session = VyconfSession(on_error=ConfigSessionError) else: self._vyconf_session = None diff --git a/python/vyos/utils/backend.py b/python/vyos/utils/backend.py new file mode 100644 index 000000000..400ea9b69 --- /dev/null +++ b/python/vyos/utils/backend.py @@ -0,0 +1,88 @@ +# Copyright 2025 VyOS maintainers and contributors +# +# 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 . + +# N.B. the following is a temporary addition for running smoketests under +# vyconf and is not to be called explicitly, at the risk of catastophe. + +# pylint: disable=wrong-import-position + +from pathlib import Path + +from vyos.utils.io import ask_yes_no +from vyos.utils.process import call + +VYCONF_SENTINEL = '/run/vyconf_backend' + +MSG_ENABLE_VYCONF = 'This will enable the vyconf backend for testing. Proceed?' +MSG_DISABLE_VYCONF = ( + 'This will restore the legacy backend; it requires a reboot. Proceed?' +) + +# read/set immutable file attribute without popen: +# https://www.geeklab.info/2021/04/chattr-and-lsattr-in-python/ +import fcntl # pylint: disable=C0411 # noqa: E402 +from array import array # pylint: disable=C0411 # noqa: E402 + +# FS constants - see /uapi/linux/fs.h in kernel source +# or +FS_IOC_GETFLAGS = 0x80086601 +FS_IOC_SETFLAGS = 0x40086602 +FS_IMMUTABLE_FL = 0x010 + + +def chattri(filename: str, value: bool): + with open(filename, 'r') as f: + arg = array('L', [0]) + fcntl.ioctl(f.fileno(), FS_IOC_GETFLAGS, arg, True) + if value: + arg[0] = arg[0] | FS_IMMUTABLE_FL + else: + arg[0] = arg[0] & ~FS_IMMUTABLE_FL + fcntl.ioctl(f.fileno(), FS_IOC_SETFLAGS, arg, True) + + +def lsattri(filename: str) -> bool: + with open(filename, 'r') as f: + arg = array('L', [0]) + fcntl.ioctl(f.fileno(), FS_IOC_GETFLAGS, arg, True) + return bool(arg[0] & FS_IMMUTABLE_FL) + + +# End: read/set immutable file attribute without popen + + +def vyconf_backend() -> bool: + return Path(VYCONF_SENTINEL).exists() and lsattri(VYCONF_SENTINEL) + + +def set_vyconf_backend(value: bool, no_prompt: bool = False): + vyconfd_service = 'vyconfd.service' + match value: + case True: + if vyconf_backend(): + return + if not no_prompt and not ask_yes_no(MSG_ENABLE_VYCONF): + return + Path(VYCONF_SENTINEL).touch() + chattri(VYCONF_SENTINEL, True) + call(f'systemctl restart {vyconfd_service}') + case False: + if not vyconf_backend(): + return + if not no_prompt and not ask_yes_no(MSG_DISABLE_VYCONF): + return + chattri(VYCONF_SENTINEL, False) + Path(VYCONF_SENTINEL).unlink() + call('/sbin/shutdown -r now') diff --git a/src/helpers/set_vyconf_backend.py b/src/helpers/set_vyconf_backend.py new file mode 100755 index 000000000..6747e51c3 --- /dev/null +++ b/src/helpers/set_vyconf_backend.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2025 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# + +# N.B. only for use within testing framework; explicit invocation will leave +# system in inconsistent state. + +from argparse import ArgumentParser + +from vyos.utils.backend import set_vyconf_backend + + +parser = ArgumentParser() +parser.add_argument('--disable', action='store_true', + help='enable/disable vyconf backend') +parser.add_argument('--no-prompt', action='store_true', + help='confirm without prompt') + +args = parser.parse_args() + +match args.disable: + case False: + set_vyconf_backend(True, no_prompt=args.no_prompt) + case True: + set_vyconf_backend(False, no_prompt=args.no_prompt) -- cgit v1.2.3 From bbd3871fbb4cb72771bfae3800d73176ec7b1c77 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 9 May 2025 11:18:57 -0500 Subject: T7363: distinguish config mode from op mode --- python/vyos/configsession.py | 5 +++ python/vyos/vyconf_session.py | 72 +++++++++++++++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 17 deletions(-) (limited to 'python') diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index 8131e0ea3..6b4d71e76 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -24,6 +24,7 @@ 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 CLI_SHELL_API = '/bin/cli-shell-api' @@ -201,6 +202,10 @@ class ConfigSession(object): 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): diff --git a/python/vyos/vyconf_session.py b/python/vyos/vyconf_session.py index cbf2fc873..47d9e1445 100644 --- a/python/vyos/vyconf_session.py +++ b/python/vyos/vyconf_session.py @@ -28,12 +28,8 @@ from vyos.component_version import append_system_version from vyos.utils.session import in_config_session -def output(o): - out = '' - for res in (o.output, o.error, o.warning): - if res is not None: - out = out + res - return out +class VyconfSessionError(Exception): + pass class VyconfSession: @@ -57,8 +53,15 @@ class VyconfSession: raise ValueError(f'No existing session for token: {token}') self.__token = token - self.on_error = on_error self.in_config_session = in_config_session() + if self.in_config_session: + out = vyconf_client.send_request( + 'enter_configuration_mode', token=self.__token + ) + if out.status: + raise VyconfSessionError(self.output(out)) + + self.on_error = on_error def __del__(self): if not self.in_config_session: @@ -67,6 +70,32 @@ class VyconfSession: def teardown(self): vyconf_client.send_request('teardown', token=self.__token) + def exit_config_mode(self): + if self.session_changed(): + return 'Uncommited changes', Errnum.UNCOMMITED_CHANGES + out = vyconf_client.send_request('exit_configuration_mode', token=self.__token) + return self.output(out), out.status + + def in_session(self) -> bool: + return self.in_config_session + + def session_changed(self) -> bool: + out = vyconf_client.send_request('session_changed', token=self.__token) + return not bool(out.status) + + @staticmethod + def config_mode(f): + @wraps(f) + def wrapped(self, *args, **kwargs): + msg = 'operation not available outside of config mode' + if not self.in_config_session: + if self.on_error is None: + raise VyconfSessionError(msg) + raise self.on_error(msg) + return f(self, *args, **kwargs) + + return wrapped + @staticmethod def raise_exception(f): @wraps(f) @@ -80,31 +109,40 @@ class VyconfSession: return wrapped + @staticmethod + def output(o): + out = '' + for res in (o.output, o.error, o.warning): + if res is not None: + out = out + res + return out + @raise_exception + @config_mode def set(self, path: list[str]) -> tuple[str, int]: out = vyconf_client.send_request('set', token=self.__token, path=path) - return output(out), out.status + return self.output(out), out.status @raise_exception + @config_mode def delete(self, path: list[str]) -> tuple[str, int]: out = vyconf_client.send_request('delete', token=self.__token, path=path) - return output(out), out.status + return self.output(out), out.status @raise_exception + @config_mode def commit(self) -> tuple[str, int]: out = vyconf_client.send_request('commit', token=self.__token) return output(out), out.status @raise_exception + @config_mode def discard(self) -> tuple[str, int]: out = vyconf_client.send_request('discard', token=self.__token) - return output(out), out.status - - def session_changed(self) -> bool: - out = vyconf_client.send_request('session_changed', token=self.__token) - return not bool(out.status) + return self.output(out), out.status @raise_exception + @config_mode def load_config(self, file: str, migrate: bool = False) -> tuple[str, int]: # pylint: disable=consider-using-with if migrate: @@ -124,18 +162,18 @@ class VyconfSession: if tmp: tmp.close() - return output(out), out.status + return self.output(out), out.status @raise_exception def save_config(self, file: str, append_version: bool = False) -> tuple[str, int]: out = vyconf_client.send_request('save', token=self.__token, location=file) if append_version: append_system_version(file) - return output(out), out.status + return self.output(out), out.status @raise_exception def show_config(self, path: list[str] = None) -> tuple[str, int]: if path is None: path = [] out = vyconf_client.send_request('show_config', token=self.__token, path=path) - return output(out), out.status + return self.output(out), out.status -- cgit v1.2.3 From d6e286a7567e61f235e2170e3da2dc55fe8c928d Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sun, 11 May 2025 19:12:48 -0500 Subject: T7121: add missing default version string on init from internal cache --- python/vyos/configtree.py | 1 + 1 file changed, 1 insertion(+) (limited to 'python') diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index ff40fbad0..faf124480 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -194,6 +194,7 @@ class ConfigTree(object): raise ValueError('Failed to read internal rep: {0}'.format(msg)) else: self.__config = config + self.__version = '' elif config_string is not None: config_section, version_section = extract_version(config_string) config_section = escape_backslash(config_section) -- cgit v1.2.3 From 5071d8cb0e66dc2663beadde24ed7f56cee8d147 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sun, 4 May 2025 17:33:50 -0500 Subject: T7363: add initialization of Config from VyconfSession --- python/vyos/config.py | 11 +++++++++-- python/vyos/configsession.py | 5 ++++- python/vyos/configsource.py | 33 ++++++++++++++++++++++++++++++++- python/vyos/defaults.py | 3 ++- python/vyos/vyconf_session.py | 6 ++++++ 5 files changed, 53 insertions(+), 5 deletions(-) (limited to 'python') diff --git a/python/vyos/config.py b/python/vyos/config.py index 546eeceab..9ae0467d4 100644 --- a/python/vyos/config.py +++ b/python/vyos/config.py @@ -1,4 +1,4 @@ -# Copyright 2017-2024 VyOS maintainers and contributors +# Copyright 2017-2025 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -62,6 +62,7 @@ while functions prefixed "effective" return values from the running config. In operational mode, all functions return values from the running config. """ +import os import re import json from typing import Union @@ -73,8 +74,11 @@ from vyos.xml_ref import ext_dict_merge from vyos.xml_ref import relative_defaults from vyos.utils.dict import get_sub_dict from vyos.utils.dict import mangle_dict_keys +from vyos.utils.boot import boot_configuration_complete +from vyos.utils.backend import vyconf_backend from vyos.configsource import ConfigSource from vyos.configsource import ConfigSourceSession +from vyos.configsource import ConfigSourceVyconfSession class ConfigDict(dict): _from_defaults = {} @@ -132,7 +136,10 @@ class Config(object): """ def __init__(self, session_env=None, config_source=None): if config_source is None: - self._config_source = ConfigSourceSession(session_env) + if vyconf_backend() and boot_configuration_complete(): + self._config_source = ConfigSourceVyconfSession(session_env) + else: + self._config_source = ConfigSourceSession(session_env) else: if not isinstance(config_source, ConfigSource): raise TypeError("config_source not of type ConfigSource") diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index 6b4d71e76..1b19c68b4 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -169,13 +169,16 @@ 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.__run_command([CLI_SHELL_API, 'setupSession']) if vyconf_backend() and boot_configuration_complete(): - self._vyconf_session = VyconfSession(on_error=ConfigSessionError) + self._vyconf_session = VyconfSession(pid=session_id, + on_error=ConfigSessionError) else: self._vyconf_session = None diff --git a/python/vyos/configsource.py b/python/vyos/configsource.py index 65cef5333..6c121a202 100644 --- a/python/vyos/configsource.py +++ b/python/vyos/configsource.py @@ -1,5 +1,5 @@ -# Copyright 2020-2023 VyOS maintainers and contributors +# Copyright 2020-2025 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,6 +20,9 @@ import subprocess from vyos.configtree import ConfigTree from vyos.utils.boot import boot_configuration_complete +from vyos.vyconf_session import VyconfSession +from vyos.vyconf_session import VyconfSessionError +from vyos.defaults import directories class VyOSError(Exception): """ @@ -310,6 +313,34 @@ class ConfigSourceSession(ConfigSource): except VyOSError: return False +class ConfigSourceVyconfSession(ConfigSource): + def __init__(self, session_env=None): + super().__init__() + + if session_env: + self.__session_env = session_env + else: + self.__session_env = None + + if session_env and 'CONFIGSESSION_PID' in session_env: + self.pid = int(session_env['CONFIGSESSION_PID']) + else: + self.pid = os.getppid() + + self._vyconf_session = VyconfSession(pid=self.pid) + try: + out = self._vyconf_session.get_config() + except VyconfSessionError as e: + raise ConfigSourceError(f'Init error in {type(self)}: {e}') + + session_dir = directories['vyconf_session_dir'] + + self.running_cache_path = os.path.join(session_dir, f'running_cache_{out}') + self.session_cache_path = os.path.join(session_dir, f'session_cache_{out}') + + self._running_config = ConfigTree(internal=self.running_cache_path) + self._session_config = ConfigTree(internal=self.session_cache_path) + class ConfigSourceString(ConfigSource): def __init__(self, running_config_text=None, session_config_text=None): super().__init__() diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index c1e5ddc04..fbde0298b 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -39,7 +39,8 @@ directories = { 'completion_dir' : f'{base_dir}/completion', 'ca_certificates' : '/usr/local/share/ca-certificates/vyos', 'ppp_nexthop_dir' : '/run/ppp_nexthop', - 'proto_path' : '/usr/share/vyos/vyconf' + 'proto_path' : '/usr/share/vyos/vyconf', + 'vyconf_session_dir' : f'{base_dir}/vyconf/session' } systemd_services = { diff --git a/python/vyos/vyconf_session.py b/python/vyos/vyconf_session.py index 47d9e1445..077952a9f 100644 --- a/python/vyos/vyconf_session.py +++ b/python/vyos/vyconf_session.py @@ -83,6 +83,12 @@ class VyconfSession: out = vyconf_client.send_request('session_changed', token=self.__token) return not bool(out.status) + def get_config(self): + out = vyconf_client.send_request('get_config', token=self.__token) + if out.status: + raise VyconfSessionError(self.output(out)) + return out.output + @staticmethod def config_mode(f): @wraps(f) -- cgit v1.2.3 From eefae2ae49a5f7719542afcfceb07ff15a2fc0fd Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sun, 11 May 2025 15:53:23 -0500 Subject: T7363: populate ConfigSourceVyconfSession methods --- python/vyos/configsource.py | 74 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'python') diff --git a/python/vyos/configsource.py b/python/vyos/configsource.py index 6c121a202..e4ced6305 100644 --- a/python/vyos/configsource.py +++ b/python/vyos/configsource.py @@ -17,12 +17,16 @@ import os import re import subprocess +from typing import Union from vyos.configtree import ConfigTree from vyos.utils.boot import boot_configuration_complete from vyos.vyconf_session import VyconfSession from vyos.vyconf_session import VyconfSessionError from vyos.defaults import directories +from vyos.xml_ref import is_tag +from vyos.xml_ref import is_leaf +from vyos.xml_ref import is_multi class VyOSError(Exception): """ @@ -341,6 +345,76 @@ class ConfigSourceVyconfSession(ConfigSource): self._running_config = ConfigTree(internal=self.running_cache_path) self._session_config = ConfigTree(internal=self.session_cache_path) + # N.B. level not yet implemented pending integration with legacy CLI + # cf. T7374 + self._level = [] + + def get_level(self): + return self._level + + def set_level(self): + pass + + def session_changed(self): + """ + Returns: + True if the config session has uncommited changes, False otherwise. + """ + try: + return self._vyconf_session.session_changed() + except VyconfSessionError: + # no actionable session info on error + return False + + def in_session(self): + """ + Returns: + True if called from a configuration session, False otherwise. + """ + return self._vyconf_session.in_session() + + def show_config(self, path: Union[str,list] = None, default: str = None, + effective: bool = False): + """ + Args: + path (str|list): Configuration tree path, or empty + default (str): Default value to return + + Returns: + str: working configuration + """ + + if path is None: + path = [] + if isinstance(path, str): + path = path.split() + + ct = self._running_config if effective else self._session_config + with_node = True if self.is_tag(path) else False + ct_at_path = ct.get_subtree(path, with_node=with_node) if path else ct + + res = ct_at_path.to_string().strip() + + return res if res else default + + def is_tag(self, path): + try: + return is_tag(path) + except ValueError: + return False + + def is_leaf(self, path): + try: + return is_leaf(path) + except ValueError: + return False + + def is_multi(self, path): + try: + return is_multi(path) + except ValueError: + return False + class ConfigSourceString(ConfigSource): def __init__(self, running_config_text=None, session_config_text=None): super().__init__() -- cgit v1.2.3 From 5328e901c1384a9ee0ea95f4d7bcb048bfa94316 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Tue, 15 Apr 2025 12:44:28 -0500 Subject: T7365: normalize formatting --- python/vyos/utils/commit.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'python') diff --git a/python/vyos/utils/commit.py b/python/vyos/utils/commit.py index 105aed8c2..2b014c939 100644 --- a/python/vyos/utils/commit.py +++ b/python/vyos/utils/commit.py @@ -1,4 +1,4 @@ -# Copyright 2023 VyOS maintainers and contributors +# Copyright 2023-2025 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -13,8 +13,11 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +# pylint: disable=import-outside-toplevel + + def commit_in_progress(): - """ Not to be used in normal op mode scripts! """ + """Not to be used in normal op mode scripts!""" # The CStore backend locks the config by opening a file # The file is not removed after commit, so just checking @@ -36,7 +39,9 @@ def commit_in_progress(): from vyos.defaults import commit_lock if getuser() != 'root': - raise OSError('This functions needs to be run as root to return correct results!') + raise OSError( + 'This functions needs to be run as root to return correct results!' + ) for proc in process_iter(): try: @@ -45,7 +50,7 @@ def commit_in_progress(): for f in files: if f.path == commit_lock: return True - except NoSuchProcess as err: + except NoSuchProcess: # Process died before we could examine it pass # Default case @@ -53,8 +58,9 @@ def commit_in_progress(): def wait_for_commit_lock(): - """ Not to be used in normal op mode scripts! """ + """Not to be used in normal op mode scripts!""" from time import sleep + # Very synchronous approach to multiprocessing while commit_in_progress(): sleep(1) -- cgit v1.2.3 From 23b67254ed4a3a7d382a95f409338db431cc3556 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Mon, 14 Apr 2025 17:58:49 -0500 Subject: T7365: add POSIX-type lock to vyconf_session.commit for compatibility We maintain compatibility with the legacy commit lock file until all other references are resolved; this requires a POSIX-type lock instead of the BSD-type lock of vyos.utils.locking. --- python/vyos/utils/commit.py | 37 +++++++++++++++++++++++++++++++++++++ python/vyos/vyconf_session.py | 11 ++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'python') diff --git a/python/vyos/utils/commit.py b/python/vyos/utils/commit.py index 2b014c939..9167c78d2 100644 --- a/python/vyos/utils/commit.py +++ b/python/vyos/utils/commit.py @@ -15,6 +15,8 @@ # pylint: disable=import-outside-toplevel +from typing import IO + def commit_in_progress(): """Not to be used in normal op mode scripts!""" @@ -64,3 +66,38 @@ def wait_for_commit_lock(): # Very synchronous approach to multiprocessing while commit_in_progress(): sleep(1) + + +# For transitional compatibility with the legacy commit locking mechanism, +# we require a lockf/fcntl (POSIX-type) lock, hence the following in place +# of vyos.utils.locking + + +def acquire_commit_lock_file() -> tuple[IO, str]: + import fcntl + from pathlib import Path + from vyos.defaults import commit_lock + + try: + # pylint: disable=consider-using-with + lock_fd = Path(commit_lock).open('w') + except IOError as e: + out = f'Critical error opening commit lock file {e}' + return None, out + + try: + fcntl.lockf(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + return lock_fd, '' + except IOError: + out = 'Configuration system locked by another commit in progress' + lock_fd.close() + return None, out + + +def release_commit_lock_file(file_descr): + import fcntl + + if file_descr is None: + return + fcntl.lockf(file_descr, fcntl.LOCK_UN) + file_descr.close() diff --git a/python/vyos/vyconf_session.py b/python/vyos/vyconf_session.py index 077952a9f..4250f0cfb 100644 --- a/python/vyos/vyconf_session.py +++ b/python/vyos/vyconf_session.py @@ -26,6 +26,9 @@ from vyos.migrate import ConfigMigrate from vyos.migrate import ConfigMigrateError from vyos.component_version import append_system_version 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 class VyconfSessionError(Exception): @@ -138,8 +141,14 @@ class VyconfSession: @raise_exception @config_mode def commit(self) -> tuple[str, int]: + lock_fd, out = acquire_commit_lock_file() + if lock_fd is None: + return out, Errnum.COMMIT_IN_PROGRESS + out = vyconf_client.send_request('commit', token=self.__token) - return output(out), out.status + release_commit_lock_file(lock_fd) + + return self.output(out), out.status @raise_exception @config_mode -- cgit v1.2.3 From c6c3fff4f59b99a3bedeb382546aac4ebc2718ca Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Thu, 22 May 2025 20:02:18 -0500 Subject: T7363: retain generated files as imports for nosetests --- .gitignore | 2 - python/vyos/proto/vycall_pb2.py | 29 +++ python/vyos/proto/vyconf_pb2.py | 93 ++++++++++ python/vyos/proto/vyconf_proto.py | 377 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 499 insertions(+), 2 deletions(-) create mode 100644 python/vyos/proto/vycall_pb2.py create mode 100644 python/vyos/proto/vyconf_pb2.py create mode 100644 python/vyos/proto/vyconf_proto.py (limited to 'python') diff --git a/.gitignore b/.gitignore index 839d2afff..7084332a8 100644 --- a/.gitignore +++ b/.gitignore @@ -152,9 +152,7 @@ data/reftree.cache data/configd-include.json # autogenerated vyos-commitd protobuf files -python/vyos/proto/*pb2.py python/vyos/proto/*.desc -python/vyos/proto/vyconf_proto.py # We do not use pip Pipfile diff --git a/python/vyos/proto/vycall_pb2.py b/python/vyos/proto/vycall_pb2.py new file mode 100644 index 000000000..95214d2a6 --- /dev/null +++ b/python/vyos/proto/vycall_pb2.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: vycall.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cvycall.proto\"&\n\x06Status\x12\x0f\n\x07success\x18\x01 \x02(\x08\x12\x0b\n\x03out\x18\x02 \x02(\t\"Y\n\x04\x43\x61ll\x12\x13\n\x0bscript_name\x18\x01 \x02(\t\x12\x11\n\ttag_value\x18\x02 \x01(\t\x12\x11\n\targ_value\x18\x03 \x01(\t\x12\x16\n\x05reply\x18\x04 \x01(\x0b\x32\x07.Status\"~\n\x06\x43ommit\x12\x12\n\nsession_id\x18\x01 \x02(\t\x12\x0f\n\x07\x64ry_run\x18\x04 \x02(\x08\x12\x0e\n\x06\x61tomic\x18\x05 \x02(\x08\x12\x12\n\nbackground\x18\x06 \x02(\x08\x12\x15\n\x04init\x18\x07 \x01(\x0b\x32\x07.Status\x12\x14\n\x05\x63\x61lls\x18\x08 \x03(\x0b\x32\x05.Call') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'vycall_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _STATUS._serialized_start=16 + _STATUS._serialized_end=54 + _CALL._serialized_start=56 + _CALL._serialized_end=145 + _COMMIT._serialized_start=147 + _COMMIT._serialized_end=273 +# @@protoc_insertion_point(module_scope) diff --git a/python/vyos/proto/vyconf_pb2.py b/python/vyos/proto/vyconf_pb2.py new file mode 100644 index 000000000..3d5042888 --- /dev/null +++ b/python/vyos/proto/vyconf_pb2.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: vyconf.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cvyconf.proto\"\x89\x15\n\x07Request\x12!\n\x06prompt\x18\x01 \x01(\x0b\x32\x0f.Request.PromptH\x00\x12.\n\rsetup_session\x18\x02 \x01(\x0b\x32\x15.Request.SetupSessionH\x00\x12\x1b\n\x03set\x18\x03 \x01(\x0b\x32\x0c.Request.SetH\x00\x12!\n\x06\x64\x65lete\x18\x04 \x01(\x0b\x32\x0f.Request.DeleteH\x00\x12!\n\x06rename\x18\x05 \x01(\x0b\x32\x0f.Request.RenameH\x00\x12\x1d\n\x04\x63opy\x18\x06 \x01(\x0b\x32\r.Request.CopyH\x00\x12#\n\x07\x63omment\x18\x07 \x01(\x0b\x32\x10.Request.CommentH\x00\x12!\n\x06\x63ommit\x18\x08 \x01(\x0b\x32\x0f.Request.CommitH\x00\x12%\n\x08rollback\x18\t \x01(\x0b\x32\x11.Request.RollbackH\x00\x12\x1f\n\x05merge\x18\n \x01(\x0b\x32\x0e.Request.MergeH\x00\x12\x1d\n\x04save\x18\x0b \x01(\x0b\x32\r.Request.SaveH\x00\x12*\n\x0bshow_config\x18\x0c \x01(\x0b\x32\x13.Request.ShowConfigH\x00\x12!\n\x06\x65xists\x18\r \x01(\x0b\x32\x0f.Request.ExistsH\x00\x12&\n\tget_value\x18\x0e \x01(\x0b\x32\x11.Request.GetValueH\x00\x12(\n\nget_values\x18\x0f \x01(\x0b\x32\x12.Request.GetValuesH\x00\x12.\n\rlist_children\x18\x10 \x01(\x0b\x32\x15.Request.ListChildrenH\x00\x12)\n\x0brun_op_mode\x18\x11 \x01(\x0b\x32\x12.Request.RunOpModeH\x00\x12#\n\x07\x63onfirm\x18\x12 \x01(\x0b\x32\x10.Request.ConfirmH\x00\x12\x43\n\x18\x65nter_configuration_mode\x18\x13 \x01(\x0b\x32\x1f.Request.EnterConfigurationModeH\x00\x12\x41\n\x17\x65xit_configuration_mode\x18\x14 \x01(\x0b\x32\x1e.Request.ExitConfigurationModeH\x00\x12%\n\x08validate\x18\x15 \x01(\x0b\x32\x11.Request.ValidateH\x00\x12%\n\x08teardown\x18\x16 \x01(\x0b\x32\x11.Request.TeardownH\x00\x12\x30\n\x0ereload_reftree\x18\x17 \x01(\x0b\x32\x16.Request.ReloadReftreeH\x00\x12\x1d\n\x04load\x18\x18 \x01(\x0b\x32\r.Request.LoadH\x00\x12#\n\x07\x64iscard\x18\x19 \x01(\x0b\x32\x10.Request.DiscardH\x00\x12\x32\n\x0fsession_changed\x18\x1a \x01(\x0b\x32\x17.Request.SessionChangedH\x00\x12/\n\x0esession_of_pid\x18\x1b \x01(\x0b\x32\x15.Request.SessionOfPidH\x00\x12\x37\n\x12session_update_pid\x18\x1c \x01(\x0b\x32\x19.Request.SessionUpdatePidH\x00\x12(\n\nget_config\x18\x1d \x01(\x0b\x32\x12.Request.GetConfigH\x00\x1a\x08\n\x06Prompt\x1aP\n\x0cSetupSession\x12\x11\n\tClientPid\x18\x01 \x02(\x05\x12\x19\n\x11\x43lientApplication\x18\x02 \x01(\t\x12\x12\n\nOnBehalfOf\x18\x03 \x01(\x05\x1a!\n\x0cSessionOfPid\x12\x11\n\tClientPid\x18\x01 \x02(\x05\x1a%\n\x10SessionUpdatePid\x12\x11\n\tClientPid\x18\x01 \x02(\x05\x1a\x1a\n\tGetConfig\x12\r\n\x05\x64ummy\x18\x01 \x01(\x05\x1a\x1e\n\x08Teardown\x12\x12\n\nOnBehalfOf\x18\x01 \x01(\x05\x1a\x46\n\x08Validate\x12\x0c\n\x04Path\x18\x01 \x03(\t\x12,\n\routput_format\x18\x02 \x01(\x0e\x32\x15.Request.OutputFormat\x1a\x13\n\x03Set\x12\x0c\n\x04Path\x18\x01 \x03(\t\x1a\x16\n\x06\x44\x65lete\x12\x0c\n\x04Path\x18\x01 \x03(\t\x1a\x18\n\x07\x44iscard\x12\r\n\x05\x64ummy\x18\x01 \x01(\x05\x1a\x1f\n\x0eSessionChanged\x12\r\n\x05\x64ummy\x18\x01 \x01(\x05\x1a\x35\n\x06Rename\x12\x11\n\tEditLevel\x18\x01 \x03(\t\x12\x0c\n\x04\x46rom\x18\x02 \x02(\t\x12\n\n\x02To\x18\x03 \x02(\t\x1a\x33\n\x04\x43opy\x12\x11\n\tEditLevel\x18\x01 \x03(\t\x12\x0c\n\x04\x46rom\x18\x02 \x02(\t\x12\n\n\x02To\x18\x03 \x02(\t\x1a(\n\x07\x43omment\x12\x0c\n\x04Path\x18\x01 \x03(\t\x12\x0f\n\x07\x43omment\x18\x02 \x02(\t\x1aR\n\x06\x43ommit\x12\x0f\n\x07\x43onfirm\x18\x01 \x01(\x08\x12\x16\n\x0e\x43onfirmTimeout\x18\x02 \x01(\x05\x12\x0f\n\x07\x43omment\x18\x03 \x01(\t\x12\x0e\n\x06\x44ryRun\x18\x04 \x01(\x08\x1a\x1c\n\x08Rollback\x12\x10\n\x08Revision\x18\x01 \x02(\x05\x1a?\n\x04Load\x12\x10\n\x08Location\x18\x01 \x02(\t\x12%\n\x06\x66ormat\x18\x02 \x01(\x0e\x32\x15.Request.ConfigFormat\x1a@\n\x05Merge\x12\x10\n\x08Location\x18\x01 \x02(\t\x12%\n\x06\x66ormat\x18\x02 \x01(\x0e\x32\x15.Request.ConfigFormat\x1a?\n\x04Save\x12\x10\n\x08Location\x18\x01 \x02(\t\x12%\n\x06\x66ormat\x18\x02 \x01(\x0e\x32\x15.Request.ConfigFormat\x1a\x41\n\nShowConfig\x12\x0c\n\x04Path\x18\x01 \x03(\t\x12%\n\x06\x66ormat\x18\x02 \x01(\x0e\x32\x15.Request.ConfigFormat\x1a\x16\n\x06\x45xists\x12\x0c\n\x04Path\x18\x01 \x03(\t\x1a\x46\n\x08GetValue\x12\x0c\n\x04Path\x18\x01 \x03(\t\x12,\n\routput_format\x18\x02 \x01(\x0e\x32\x15.Request.OutputFormat\x1aG\n\tGetValues\x12\x0c\n\x04Path\x18\x01 \x03(\t\x12,\n\routput_format\x18\x02 \x01(\x0e\x32\x15.Request.OutputFormat\x1aJ\n\x0cListChildren\x12\x0c\n\x04Path\x18\x01 \x03(\t\x12,\n\routput_format\x18\x02 \x01(\x0e\x32\x15.Request.OutputFormat\x1aG\n\tRunOpMode\x12\x0c\n\x04Path\x18\x01 \x03(\t\x12,\n\routput_format\x18\x02 \x01(\x0e\x32\x15.Request.OutputFormat\x1a\t\n\x07\x43onfirm\x1a\x46\n\x16\x45nterConfigurationMode\x12\x11\n\tExclusive\x18\x01 \x02(\x08\x12\x19\n\x11OverrideExclusive\x18\x02 \x02(\x08\x1a\x17\n\x15\x45xitConfigurationMode\x1a#\n\rReloadReftree\x12\x12\n\nOnBehalfOf\x18\x01 \x01(\x05\"#\n\x0c\x43onfigFormat\x12\t\n\x05\x43URLY\x10\x00\x12\x08\n\x04JSON\x10\x01\")\n\x0cOutputFormat\x12\x0c\n\x08OutPlain\x10\x00\x12\x0b\n\x07OutJSON\x10\x01\x42\x05\n\x03msg\";\n\x0fRequestEnvelope\x12\r\n\x05token\x18\x01 \x01(\t\x12\x19\n\x07request\x18\x02 \x02(\x0b\x32\x08.Request\"S\n\x08Response\x12\x17\n\x06status\x18\x01 \x02(\x0e\x32\x07.Errnum\x12\x0e\n\x06output\x18\x02 \x01(\t\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12\x0f\n\x07warning\x18\x04 \x01(\t*\xd2\x01\n\x06\x45rrnum\x12\x0b\n\x07SUCCESS\x10\x00\x12\x08\n\x04\x46\x41IL\x10\x01\x12\x10\n\x0cINVALID_PATH\x10\x02\x12\x11\n\rINVALID_VALUE\x10\x03\x12\x16\n\x12\x43OMMIT_IN_PROGRESS\x10\x04\x12\x18\n\x14\x43ONFIGURATION_LOCKED\x10\x05\x12\x12\n\x0eINTERNAL_ERROR\x10\x06\x12\x15\n\x11PERMISSION_DENIED\x10\x07\x12\x17\n\x13PATH_ALREADY_EXISTS\x10\x08\x12\x16\n\x12UNCOMMITED_CHANGES\x10\t') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'vyconf_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _ERRNUM._serialized_start=2863 + _ERRNUM._serialized_end=3073 + _REQUEST._serialized_start=17 + _REQUEST._serialized_end=2714 + _REQUEST_PROMPT._serialized_start=1237 + _REQUEST_PROMPT._serialized_end=1245 + _REQUEST_SETUPSESSION._serialized_start=1247 + _REQUEST_SETUPSESSION._serialized_end=1327 + _REQUEST_SESSIONOFPID._serialized_start=1329 + _REQUEST_SESSIONOFPID._serialized_end=1362 + _REQUEST_SESSIONUPDATEPID._serialized_start=1364 + _REQUEST_SESSIONUPDATEPID._serialized_end=1401 + _REQUEST_GETCONFIG._serialized_start=1403 + _REQUEST_GETCONFIG._serialized_end=1429 + _REQUEST_TEARDOWN._serialized_start=1431 + _REQUEST_TEARDOWN._serialized_end=1461 + _REQUEST_VALIDATE._serialized_start=1463 + _REQUEST_VALIDATE._serialized_end=1533 + _REQUEST_SET._serialized_start=1535 + _REQUEST_SET._serialized_end=1554 + _REQUEST_DELETE._serialized_start=1556 + _REQUEST_DELETE._serialized_end=1578 + _REQUEST_DISCARD._serialized_start=1580 + _REQUEST_DISCARD._serialized_end=1604 + _REQUEST_SESSIONCHANGED._serialized_start=1606 + _REQUEST_SESSIONCHANGED._serialized_end=1637 + _REQUEST_RENAME._serialized_start=1639 + _REQUEST_RENAME._serialized_end=1692 + _REQUEST_COPY._serialized_start=1694 + _REQUEST_COPY._serialized_end=1745 + _REQUEST_COMMENT._serialized_start=1747 + _REQUEST_COMMENT._serialized_end=1787 + _REQUEST_COMMIT._serialized_start=1789 + _REQUEST_COMMIT._serialized_end=1871 + _REQUEST_ROLLBACK._serialized_start=1873 + _REQUEST_ROLLBACK._serialized_end=1901 + _REQUEST_LOAD._serialized_start=1903 + _REQUEST_LOAD._serialized_end=1966 + _REQUEST_MERGE._serialized_start=1968 + _REQUEST_MERGE._serialized_end=2032 + _REQUEST_SAVE._serialized_start=2034 + _REQUEST_SAVE._serialized_end=2097 + _REQUEST_SHOWCONFIG._serialized_start=2099 + _REQUEST_SHOWCONFIG._serialized_end=2164 + _REQUEST_EXISTS._serialized_start=2166 + _REQUEST_EXISTS._serialized_end=2188 + _REQUEST_GETVALUE._serialized_start=2190 + _REQUEST_GETVALUE._serialized_end=2260 + _REQUEST_GETVALUES._serialized_start=2262 + _REQUEST_GETVALUES._serialized_end=2333 + _REQUEST_LISTCHILDREN._serialized_start=2335 + _REQUEST_LISTCHILDREN._serialized_end=2409 + _REQUEST_RUNOPMODE._serialized_start=2411 + _REQUEST_RUNOPMODE._serialized_end=2482 + _REQUEST_CONFIRM._serialized_start=1799 + _REQUEST_CONFIRM._serialized_end=1808 + _REQUEST_ENTERCONFIGURATIONMODE._serialized_start=2495 + _REQUEST_ENTERCONFIGURATIONMODE._serialized_end=2565 + _REQUEST_EXITCONFIGURATIONMODE._serialized_start=2567 + _REQUEST_EXITCONFIGURATIONMODE._serialized_end=2590 + _REQUEST_RELOADREFTREE._serialized_start=2592 + _REQUEST_RELOADREFTREE._serialized_end=2627 + _REQUEST_CONFIGFORMAT._serialized_start=2629 + _REQUEST_CONFIGFORMAT._serialized_end=2664 + _REQUEST_OUTPUTFORMAT._serialized_start=2666 + _REQUEST_OUTPUTFORMAT._serialized_end=2707 + _REQUESTENVELOPE._serialized_start=2716 + _REQUESTENVELOPE._serialized_end=2775 + _RESPONSE._serialized_start=2777 + _RESPONSE._serialized_end=2860 +# @@protoc_insertion_point(module_scope) diff --git a/python/vyos/proto/vyconf_proto.py b/python/vyos/proto/vyconf_proto.py new file mode 100644 index 000000000..404ef2f27 --- /dev/null +++ b/python/vyos/proto/vyconf_proto.py @@ -0,0 +1,377 @@ +from enum import IntEnum +from dataclasses import dataclass +from dataclasses import field + +class Errnum(IntEnum): + SUCCESS = 0 + FAIL = 1 + INVALID_PATH = 2 + INVALID_VALUE = 3 + COMMIT_IN_PROGRESS = 4 + CONFIGURATION_LOCKED = 5 + INTERNAL_ERROR = 6 + PERMISSION_DENIED = 7 + PATH_ALREADY_EXISTS = 8 + UNCOMMITED_CHANGES = 9 + +class ConfigFormat(IntEnum): + CURLY = 0 + JSON = 1 + +class OutputFormat(IntEnum): + OutPlain = 0 + OutJSON = 1 + +@dataclass +class Prompt: + pass + +@dataclass +class SetupSession: + ClientPid: int = 0 + ClientApplication: str = None + OnBehalfOf: int = None + +@dataclass +class SessionOfPid: + ClientPid: int = 0 + +@dataclass +class SessionUpdatePid: + ClientPid: int = 0 + +@dataclass +class GetConfig: + dummy: int = None + +@dataclass +class Teardown: + OnBehalfOf: int = None + +@dataclass +class Validate: + Path: list[str] = field(default_factory=list) + output_format: OutputFormat = None + +@dataclass +class Set: + Path: list[str] = field(default_factory=list) + +@dataclass +class Delete: + Path: list[str] = field(default_factory=list) + +@dataclass +class Discard: + dummy: int = None + +@dataclass +class SessionChanged: + dummy: int = None + +@dataclass +class Rename: + EditLevel: list[str] = field(default_factory=list) + From: str = "" + To: str = "" + +@dataclass +class Copy: + EditLevel: list[str] = field(default_factory=list) + From: str = "" + To: str = "" + +@dataclass +class Comment: + Path: list[str] = field(default_factory=list) + Comment: str = "" + +@dataclass +class Commit: + Confirm: bool = None + ConfirmTimeout: int = None + Comment: str = None + DryRun: bool = None + +@dataclass +class Rollback: + Revision: int = 0 + +@dataclass +class Load: + Location: str = "" + format: ConfigFormat = None + +@dataclass +class Merge: + Location: str = "" + format: ConfigFormat = None + +@dataclass +class Save: + Location: str = "" + format: ConfigFormat = None + +@dataclass +class ShowConfig: + Path: list[str] = field(default_factory=list) + format: ConfigFormat = None + +@dataclass +class Exists: + Path: list[str] = field(default_factory=list) + +@dataclass +class GetValue: + Path: list[str] = field(default_factory=list) + output_format: OutputFormat = None + +@dataclass +class GetValues: + Path: list[str] = field(default_factory=list) + output_format: OutputFormat = None + +@dataclass +class ListChildren: + Path: list[str] = field(default_factory=list) + output_format: OutputFormat = None + +@dataclass +class RunOpMode: + Path: list[str] = field(default_factory=list) + output_format: OutputFormat = None + +@dataclass +class Confirm: + pass + +@dataclass +class EnterConfigurationMode: + Exclusive: bool = False + OverrideExclusive: bool = False + +@dataclass +class ExitConfigurationMode: + pass + +@dataclass +class ReloadReftree: + OnBehalfOf: int = None + +@dataclass +class Request: + prompt: Prompt = None + setup_session: SetupSession = None + set: Set = None + delete: Delete = None + rename: Rename = None + copy: Copy = None + comment: Comment = None + commit: Commit = None + rollback: Rollback = None + merge: Merge = None + save: Save = None + show_config: ShowConfig = None + exists: Exists = None + get_value: GetValue = None + get_values: GetValues = None + list_children: ListChildren = None + run_op_mode: RunOpMode = None + confirm: Confirm = None + enter_configuration_mode: EnterConfigurationMode = None + exit_configuration_mode: ExitConfigurationMode = None + validate: Validate = None + teardown: Teardown = None + reload_reftree: ReloadReftree = None + load: Load = None + discard: Discard = None + session_changed: SessionChanged = None + session_of_pid: SessionOfPid = None + session_update_pid: SessionUpdatePid = None + get_config: GetConfig = None + +@dataclass +class RequestEnvelope: + token: str = None + request: Request = None + +@dataclass +class Response: + status: Errnum = None + output: str = None + error: str = None + warning: str = None + +def set_request_prompt(token: str = None): + reqi = Prompt () + req = Request(prompt=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_setup_session(token: str = None, client_pid: int = 0, client_application: str = None, on_behalf_of: int = None): + reqi = SetupSession (client_pid, client_application, on_behalf_of) + req = Request(setup_session=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_session_of_pid(token: str = None, client_pid: int = 0): + reqi = SessionOfPid (client_pid) + req = Request(session_of_pid=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_session_update_pid(token: str = None, client_pid: int = 0): + reqi = SessionUpdatePid (client_pid) + req = Request(session_update_pid=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_get_config(token: str = None, dummy: int = None): + reqi = GetConfig (dummy) + req = Request(get_config=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_teardown(token: str = None, on_behalf_of: int = None): + reqi = Teardown (on_behalf_of) + req = Request(teardown=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_validate(token: str = None, path: list[str] = [], output_format: OutputFormat = None): + reqi = Validate (path, output_format) + req = Request(validate=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_set(token: str = None, path: list[str] = []): + reqi = Set (path) + req = Request(set=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_delete(token: str = None, path: list[str] = []): + reqi = Delete (path) + req = Request(delete=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_discard(token: str = None, dummy: int = None): + reqi = Discard (dummy) + req = Request(discard=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_session_changed(token: str = None, dummy: int = None): + reqi = SessionChanged (dummy) + req = Request(session_changed=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_rename(token: str = None, edit_level: list[str] = [], from_: str = "", to: str = ""): + reqi = Rename (edit_level, from_, to) + req = Request(rename=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_copy(token: str = None, edit_level: list[str] = [], from_: str = "", to: str = ""): + reqi = Copy (edit_level, from_, to) + req = Request(copy=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_comment(token: str = None, path: list[str] = [], comment: str = ""): + reqi = Comment (path, comment) + req = Request(comment=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_commit(token: str = None, confirm: bool = None, confirm_timeout: int = None, comment: str = None, dry_run: bool = None): + reqi = Commit (confirm, confirm_timeout, comment, dry_run) + req = Request(commit=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_rollback(token: str = None, revision: int = 0): + reqi = Rollback (revision) + req = Request(rollback=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_load(token: str = None, location: str = "", format: ConfigFormat = None): + reqi = Load (location, format) + req = Request(load=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_merge(token: str = None, location: str = "", format: ConfigFormat = None): + reqi = Merge (location, format) + req = Request(merge=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_save(token: str = None, location: str = "", format: ConfigFormat = None): + reqi = Save (location, format) + req = Request(save=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_show_config(token: str = None, path: list[str] = [], format: ConfigFormat = None): + reqi = ShowConfig (path, format) + req = Request(show_config=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_exists(token: str = None, path: list[str] = []): + reqi = Exists (path) + req = Request(exists=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_get_value(token: str = None, path: list[str] = [], output_format: OutputFormat = None): + reqi = GetValue (path, output_format) + req = Request(get_value=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_get_values(token: str = None, path: list[str] = [], output_format: OutputFormat = None): + reqi = GetValues (path, output_format) + req = Request(get_values=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_list_children(token: str = None, path: list[str] = [], output_format: OutputFormat = None): + reqi = ListChildren (path, output_format) + req = Request(list_children=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_run_op_mode(token: str = None, path: list[str] = [], output_format: OutputFormat = None): + reqi = RunOpMode (path, output_format) + req = Request(run_op_mode=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_confirm(token: str = None): + reqi = Confirm () + req = Request(confirm=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_enter_configuration_mode(token: str = None, exclusive: bool = False, override_exclusive: bool = False): + reqi = EnterConfigurationMode (exclusive, override_exclusive) + req = Request(enter_configuration_mode=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_exit_configuration_mode(token: str = None): + reqi = ExitConfigurationMode () + req = Request(exit_configuration_mode=reqi) + req_env = RequestEnvelope(token, req) + return req_env + +def set_request_reload_reftree(token: str = None, on_behalf_of: int = None): + reqi = ReloadReftree (on_behalf_of) + req = Request(reload_reftree=reqi) + req_env = RequestEnvelope(token, req) + return req_env -- cgit v1.2.3