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.py191
1 files changed, 191 insertions, 0 deletions
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
new file mode 100644
index 000000000..0994fd974
--- /dev/null
+++ b/python/vyos/configsession.py
@@ -0,0 +1,191 @@
+# configsession -- the write API for the VyOS running config
+# Copyright (C) 2019 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import os
+import re
+import sys
+import subprocess
+
+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'
+DISCARD = '/opt/vyatta/sbin/my_discard'
+SHOW_CONFIG = ['/bin/cli-shell-api', 'showConfig']
+LOAD_CONFIG = ['/bin/cli-shell-api', 'loadFile']
+SAVE_CONFIG = ['/opt/vyatta/sbin/vyatta-save-config.pl']
+INSTALL_IMAGE = ['/opt/vyatta/sbin/install-image', '--url']
+REMOVE_IMAGE = ['/opt/vyatta/bin/vyatta-boot-image.pl', '--del']
+GENERATE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'generate']
+SHOW = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'show']
+
+# Default "commit via" string
+APP = "vyos-http-api"
+
+# When started as a service rather than from a user shell,
+# the process lacks the VyOS-specific environment that comes
+# from bash configs, so we have to inject it
+# XXX: maybe it's better to do via a systemd environment file
+def inject_vyos_env(env):
+ env['VYATTA_CFG_GROUP_NAME'] = 'vyattacfg'
+ 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_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates'
+ env['vyatta_configdir'] = '/opt/vyatta/config'
+ env['vyatta_datadir'] = '/opt/vyatta/share'
+ env['vyatta_datarootdir'] = '/opt/vyatta/share'
+ env['vyatta_libdir'] = '/opt/vyatta/lib'
+ env['vyatta_libexecdir'] = '/opt/vyatta/libexec'
+ env['vyatta_op_templates'] = '/opt/vyatta/share/vyatta-op/templates'
+ env['vyatta_prefix'] = '/opt/vyatta'
+ env['vyatta_sbindir'] = '/opt/vyatta/sbin'
+ env['vyatta_sysconfdir'] = '/opt/vyatta/etc'
+ env['vyos_bin_dir'] = '/usr/bin'
+ env['vyos_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates'
+ env['vyos_completion_dir'] = '/usr/libexec/vyos/completion'
+ env['vyos_configdir'] = '/opt/vyatta/config'
+ env['vyos_conf_scripts_dir'] = '/usr/libexec/vyos/conf_mode'
+ env['vyos_datadir'] = '/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'
+ env['vyos_op_templates'] = '/opt/vyatta/share/vyatta-op/templates'
+ env['vyos_prefix'] = '/opt/vyatta'
+ env['vyos_sbin_dir'] = '/usr/sbin'
+ env['vyos_validators_dir'] = '/usr/libexec/vyos/validators'
+
+ return env
+
+
+class ConfigSessionError(Exception):
+ pass
+
+
+class ConfigSession(object):
+ """
+ The write API of VyOS.
+ """
+ def __init__(self, session_id, app=APP):
+ """
+ Creates a new config session.
+
+ Args:
+ session_id (str): Session identifier
+ app (str): Application name, purely informational
+
+ Note:
+ The session identifier MUST be globally unique within the system.
+ The best practice is to only have one ConfigSession object per process
+ and used the PID for the session identifier.
+ """
+
+ 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
+ # XXX: it's better to extend cli-shell-api to provide easily readable output
+ env_list = re.findall(r'([A-Z_]+)=([^;\s]+)', env_str.decode())
+
+ session_env = os.environ
+ session_env = inject_vyos_env(session_env)
+ for k, v in env_list:
+ session_env[k] = v
+
+ self.__session_env = session_env
+ self.__session_env["COMMIT_VIA"] = app
+
+ self.__run_command([CLI_SHELL_API, 'setupSession'])
+
+ 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)
+
+ def __run_command(self, cmd_list):
+ p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=self.__session_env)
+ result = p.wait()
+ output = p.stdout.read().decode()
+ p.communicate()
+ if result != 0:
+ raise ConfigSessionError(output)
+ return output
+
+ def get_session_env(self):
+ return self.__session_env
+
+ def set(self, path, value=None):
+ if not value:
+ value = []
+ else:
+ value = [value]
+ self.__run_command([SET] + path + value)
+
+ def delete(self, path, value=None):
+ if not value:
+ value = []
+ else:
+ value = [value]
+ self.__run_command([DELETE] + path + value)
+
+ def comment(self, path, value=None):
+ if not value:
+ value = [""]
+ else:
+ value = [value]
+ self.__run_command([COMMENT] + path + value)
+
+ def commit(self):
+ out = self.__run_command([COMMIT])
+ return out
+
+ def discard(self):
+ self.__run_command([DISCARD])
+
+ def show_config(self, path, format='raw'):
+ config_data = self.__run_command(SHOW_CONFIG + path)
+
+ if format == 'raw':
+ return config_data
+
+ def load_config(self, file_path):
+ out = self.__run_command(LOAD_CONFIG + [file_path])
+ return out
+
+ def save_config(self, file_path):
+ out = self.__run_command(SAVE_CONFIG + [file_path])
+ return out
+
+ def install_image(self, url):
+ out = self.__run_command(INSTALL_IMAGE + [url])
+ return out
+
+ def remove_image(self, name):
+ out = self.__run_command(REMOVE_IMAGE + [name])
+ return out
+
+ def generate(self, path):
+ out = self.__run_command(GENERATE + path)
+ return out
+
+ def show(self, path):
+ out = self.__run_command(SHOW + path)
+ return out
+