summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/https/nginx.default.j22
-rw-r--r--debian/control1
-rw-r--r--op-mode-definitions/container.xml.in4
-rw-r--r--op-mode-definitions/monitor-log.xml.in2
-rw-r--r--op-mode-definitions/show-log.xml.in4
-rw-r--r--python/vyos/configsession.py14
-rw-r--r--python/vyos/opmode.py20
-rwxr-xr-xsrc/conf_mode/service_webproxy.py2
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py2
-rwxr-xr-xsrc/op_mode/container.py13
-rwxr-xr-xsrc/op_mode/ipsec.py28
-rw-r--r--src/services/api/graphql/libs/op_mode.py2
-rw-r--r--src/services/api/graphql/session/errors/op_mode_errors.py6
-rwxr-xr-xsrc/services/vyos-http-api-server46
14 files changed, 112 insertions, 34 deletions
diff --git a/data/templates/https/nginx.default.j2 b/data/templates/https/nginx.default.j2
index dbb08e187..753c3a5c9 100644
--- a/data/templates/https/nginx.default.j2
+++ b/data/templates/https/nginx.default.j2
@@ -34,7 +34,7 @@ server {
ssl_protocols TLSv1.2 TLSv1.3;
# proxy settings for HTTP API, if enabled; 503, if not
- location ~ /(retrieve|configure|config-file|image|generate|show|reset|docs|openapi.json|redoc|graphql) {
+ location ~ /(retrieve|configure|config-file|image|container-image|generate|show|reset|docs|openapi.json|redoc|graphql) {
{% if server.api %}
{% if server.api.socket %}
proxy_pass http://unix:/run/api.sock;
diff --git a/debian/control b/debian/control
index 696f8902d..a477bffec 100644
--- a/debian/control
+++ b/debian/control
@@ -39,7 +39,6 @@ Depends:
beep,
bmon,
bsdmainutils,
- charon-systemd,
conntrack,
conntrackd,
conserver-client,
diff --git a/op-mode-definitions/container.xml.in b/op-mode-definitions/container.xml.in
index 786bd66d3..ada9a4d59 100644
--- a/op-mode-definitions/container.xml.in
+++ b/op-mode-definitions/container.xml.in
@@ -11,7 +11,7 @@
<properties>
<help>Pull a new image for container</help>
</properties>
- <command>sudo podman image pull "${4}"</command>
+ <command>sudo ${vyos_op_scripts_dir}/container.py add_image --name "${4}"</command>
</tagNode>
</children>
</node>
@@ -44,7 +44,7 @@
<script>sudo podman image ls -q</script>
</completionHelp>
</properties>
- <command>sudo podman image rm --force "${4}"</command>
+ <command>sudo ${vyos_op_scripts_dir}/container.py delete_image --name "${4}"</command>
</tagNode>
</children>
</node>
diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in
index c14f45d75..8abc5f4db 100644
--- a/op-mode-definitions/monitor-log.xml.in
+++ b/op-mode-definitions/monitor-log.xml.in
@@ -262,7 +262,7 @@
<properties>
<help>Monitor last lines of IPsec</help>
</properties>
- <command>journalctl --no-hostname --boot --follow --unit strongswan.service</command>
+ <command>journalctl --no-hostname --boot --follow --unit strongswan-starter.service</command>
</leafNode>
<leafNode name="l2tp">
<properties>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 70afaaf6e..977ece453 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -403,13 +403,13 @@
<properties>
<help>Show log for ALL</help>
</properties>
- <command>journalctl --no-hostname --boot --unit strongswan.service --unit accel-ppp@*.service</command>
+ <command>journalctl --no-hostname --boot --unit strongswan-starter.service --unit accel-ppp@*.service</command>
</leafNode>
<leafNode name="ipsec">
<properties>
<help>Show log for IPsec</help>
</properties>
- <command>journalctl --no-hostname --boot --unit strongswan.service</command>
+ <command>journalctl --no-hostname --boot --unit strongswan-starter.service</command>
</leafNode>
<leafNode name="l2tp">
<properties>
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index 3a60f6d92..df44fd8d6 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -34,6 +34,8 @@ 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']
RESET = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'reset']
+OP_CMD_ADD = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'add']
+OP_CMD_DELETE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'delete']
# Default "commit via" string
APP = "vyos-http-api"
@@ -204,3 +206,15 @@ class ConfigSession(object):
def reset(self, path):
out = self.__run_command(RESET + path)
return out
+
+ def add_container_image(self, name):
+ out = self.__run_command(OP_CMD_ADD + ['container', 'image'] + [name])
+ return out
+
+ def delete_container_image(self, name):
+ out = self.__run_command(OP_CMD_DELETE + ['container', 'image'] + [name])
+ return out
+
+ def show_container_image(self):
+ out = self.__run_command(SHOW + ['container', 'image'])
+ return out
diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py
index 5ff768859..30e893d74 100644
--- a/python/vyos/opmode.py
+++ b/python/vyos/opmode.py
@@ -45,6 +45,10 @@ class PermissionDenied(Error):
"""
pass
+class UnsupportedOperation(Error):
+ """ Requested operation is technically valid but is not implemented yet. """
+ pass
+
class IncorrectValue(Error):
""" Requested operation is valid, but an argument provided has an
incorrect value, preventing successful completion.
@@ -66,13 +70,13 @@ class InternalError(Error):
def _is_op_mode_function_name(name):
- if re.match(r"^(show|clear|reset|restart)", name):
+ if re.match(r"^(show|clear|reset|restart|add|delete|generate)", name):
return True
else:
return False
-def _is_show(name):
- if re.match(r"^show", name):
+def _capture_output(name):
+ if re.match(r"^(show|generate)", name):
return True
else:
return False
@@ -199,14 +203,14 @@ def run(module):
# it would cause an extra argument error when we pass the dict to a function
del args["subcommand"]
- # Show commands must always get the "raw" argument,
- # but other commands (clear/reset/restart) should not,
+ # Show and generate commands must always get the "raw" argument,
+ # but other commands (clear/reset/restart/add/delete) should not,
# because they produce no output and it makes no sense for them.
- if ("raw" not in args) and _is_show(function_name):
+ if ("raw" not in args) and _capture_output(function_name):
args["raw"] = False
- if re.match(r"^show", function_name):
- # Show commands are slightly special:
+ if _capture_output(function_name):
+ # Show and generate commands are slightly special:
# they may return human-formatted output
# or a raw dict that we need to serialize in JSON for printing
res = func(**args)
diff --git a/src/conf_mode/service_webproxy.py b/src/conf_mode/service_webproxy.py
index 41a1deaa3..658e496a6 100755
--- a/src/conf_mode/service_webproxy.py
+++ b/src/conf_mode/service_webproxy.py
@@ -246,7 +246,7 @@ def apply(proxy):
if os.path.exists(squidguard_db_dir):
chmod_755(squidguard_db_dir)
- call('systemctl restart squid.service')
+ call('systemctl reload-or-restart squid.service')
return None
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index 04e2f2939..b79e9847a 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -622,7 +622,7 @@ def wait_for_vici_socket(timeout=5, sleep_interval=0.1):
sleep(sleep_interval)
def apply(ipsec):
- systemd_service = 'strongswan.service'
+ systemd_service = 'strongswan-starter.service'
if not ipsec:
call(f'systemctl stop {systemd_service}')
else:
diff --git a/src/op_mode/container.py b/src/op_mode/container.py
index ecefc556e..d48766a0c 100755
--- a/src/op_mode/container.py
+++ b/src/op_mode/container.py
@@ -35,6 +35,19 @@ def _get_raw_data(command: str) -> list:
data = json.loads(json_data)
return data
+def add_image(name: str):
+ from vyos.util import rc_cmd
+
+ rc, output = rc_cmd(f'podman image pull {name}')
+ if rc != 0:
+ raise vyos.opmode.InternalError(output)
+
+def delete_image(name: str):
+ from vyos.util import rc_cmd
+
+ rc, output = rc_cmd(f'podman image rm --force {name}')
+ if rc != 0:
+ raise vyos.opmode.InternalError(output)
def show_container(raw: bool):
command = 'podman ps --all'
diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py
index e0d204a0a..f6417764a 100755
--- a/src/op_mode/ipsec.py
+++ b/src/op_mode/ipsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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
@@ -173,7 +173,7 @@ def _get_parent_sa_proposal(connection_name: str, data: list) -> dict:
for sa in data:
# check if parent SA exist
if connection_name not in sa.keys():
- return {}
+ continue
if 'encr-alg' in sa[connection_name]:
encr_alg = sa.get(connection_name, '').get('encr-alg')
cipher = encr_alg.split('_')[0]
@@ -203,16 +203,17 @@ def _get_parent_sa_state(connection_name: str, data: list) -> str:
Returns:
Parent SA connection state
"""
+ ike_state = 'down'
if not data:
- return 'down'
+ return ike_state
for sa in data:
# check if parent SA exist
- if connection_name not in sa.keys():
- return 'down'
- if sa[connection_name]['state'].lower() == 'established':
- return 'up'
- else:
- return 'down'
+ for connection, connection_conf in sa.items():
+ if connection_name != connection:
+ continue
+ if connection_conf['state'].lower() == 'established':
+ ike_state = 'up'
+ return ike_state
def _get_child_sa_state(connection_name: str, tunnel_name: str,
@@ -227,19 +228,20 @@ def _get_child_sa_state(connection_name: str, tunnel_name: str,
Returns:
str: `up` if child SA state is 'installed' otherwise `down`
"""
+ child_sa = 'down'
if not data:
- return 'down'
+ return child_sa
for sa in data:
# check if parent SA exist
if connection_name not in sa.keys():
- return 'down'
+ continue
child_sas = sa[connection_name]['child-sas']
# Get all child SA states
# there can be multiple SAs per tunnel
child_sa_states = [
v['state'] for k, v in child_sas.items() if v['name'] == tunnel_name
]
- return 'up' if 'INSTALLED' in child_sa_states else 'down'
+ return 'up' if 'INSTALLED' in child_sa_states else child_sa
def _get_child_sa_info(connection_name: str, tunnel_name: str,
@@ -257,7 +259,7 @@ def _get_child_sa_info(connection_name: str, tunnel_name: str,
for sa in data:
# check if parent SA exist
if connection_name not in sa.keys():
- return {}
+ continue
child_sas = sa[connection_name]['child-sas']
# Get all child SA data
# Skip temp SA name (first key), get only SA values as dict
diff --git a/src/services/api/graphql/libs/op_mode.py b/src/services/api/graphql/libs/op_mode.py
index 211f8ce19..c1eb493db 100644
--- a/src/services/api/graphql/libs/op_mode.py
+++ b/src/services/api/graphql/libs/op_mode.py
@@ -30,7 +30,7 @@ def load_op_mode_as_module(name: str):
return load_as_module(name, path)
def is_op_mode_function_name(name):
- if re.match(r"^(show|clear|reset|restart)", name):
+ if re.match(r"^(show|clear|reset|restart|add|delete)", name):
return True
return False
diff --git a/src/services/api/graphql/session/errors/op_mode_errors.py b/src/services/api/graphql/session/errors/op_mode_errors.py
index 7bc1d1d81..4029fd0a1 100644
--- a/src/services/api/graphql/session/errors/op_mode_errors.py
+++ b/src/services/api/graphql/session/errors/op_mode_errors.py
@@ -4,12 +4,14 @@ op_mode_err_msg = {
"UnconfiguredSubsystem": "subsystem is not configured or not running",
"DataUnavailable": "data currently unavailable",
"PermissionDenied": "client does not have permission",
- "IncorrectValue": "argument value is incorrect"
+ "IncorrectValue": "argument value is incorrect",
+ "UnsupportedOperation": "operation is not supported (yet)",
}
op_mode_err_code = {
"UnconfiguredSubsystem": 2000,
"DataUnavailable": 2001,
"PermissionDenied": 1003,
- "IncorrectValue": 1002
+ "IncorrectValue": 1002,
+ "UnsupportedOperation": 1004,
}
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index 60ea9a5ee..f59e089ae 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -175,6 +175,19 @@ class ImageModel(ApiModel):
}
}
+class ContainerImageModel(ApiModel):
+ op: StrictStr
+ name: StrictStr = None
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "add | delete | show",
+ "name": "imagename",
+ }
+ }
+
class GenerateModel(ApiModel):
op: StrictStr
path: List[StrictStr]
@@ -389,7 +402,7 @@ class MultipartRoute(APIRoute):
if endpoint in ('/retrieve','/generate','/show','/reset'):
if request.ERR_NO_OP or request.ERR_NO_PATH:
return error(400, "Missing required field. \"op\" and \"path\" fields are required")
- if endpoint in ('/config-file', '/image'):
+ if endpoint in ('/config-file', '/image', '/container-image'):
if request.ERR_NO_OP:
return error(400, "Missing required field \"op\"")
@@ -581,6 +594,37 @@ def image_op(data: ImageModel):
return success(res)
+@app.post('/container-image')
+def image_op(data: ContainerImageModel):
+ session = app.state.vyos_session
+
+ op = data.op
+
+ try:
+ if op == 'add':
+ if data.name:
+ name = data.name
+ else:
+ return error(400, "Missing required field \"name\"")
+ res = session.add_container_image(name)
+ elif op == 'delete':
+ if data.name:
+ name = data.name
+ else:
+ return error(400, "Missing required field \"name\"")
+ res = session.delete_container_image(name)
+ elif op == 'show':
+ res = session.show_container_image()
+ else:
+ return error(400, "\"{0}\" is not a valid operation".format(op))
+ except ConfigSessionError as e:
+ return error(400, str(e))
+ except Exception as e:
+ logger.critical(traceback.format_exc())
+ return error(500, "An internal error occured. Check the logs for details.")
+
+ return success(res)
+
@app.post('/generate')
def generate_op(data: GenerateModel):
session = app.state.vyos_session