diff options
| author | Daniil Baturin <daniil@vyos.io> | 2023-01-10 15:24:42 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-10 15:24:42 +0000 | 
| commit | c86ed3df1e309b6a98de3ce88b508ea79453f9b1 (patch) | |
| tree | 4f24bc1da40b457309989e4a9ab55933de471070 | |
| parent | de686b3bee8ffb410142c9eeb2a7bf788316e513 (diff) | |
| parent | ba4aebaaa816d810727ded073d3b09c1723cded7 (diff) | |
| download | vyos-1x-c86ed3df1e309b6a98de3ce88b508ea79453f9b1.tar.gz vyos-1x-c86ed3df1e309b6a98de3ce88b508ea79453f9b1.zip | |
Merge pull request #1744 from jestabro/container-op-mode
container: T4880: expose 'add/delete container image' in HTTP-API
| -rw-r--r-- | data/templates/https/nginx.default.j2 | 2 | ||||
| -rw-r--r-- | op-mode-definitions/container.xml.in | 4 | ||||
| -rw-r--r-- | python/vyos/configsession.py | 14 | ||||
| -rw-r--r-- | python/vyos/opmode.py | 2 | ||||
| -rwxr-xr-x | src/op_mode/container.py | 13 | ||||
| -rw-r--r-- | src/services/api/graphql/libs/op_mode.py | 2 | ||||
| -rwxr-xr-x | src/services/vyos-http-api-server | 46 | 
7 files changed, 77 insertions, 6 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/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/python/vyos/configsession.py b/python/vyos/configsession.py index 3a60f6d92..9864aa5c5 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'] +ADD = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'add'] +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(ADD + ['container', 'image'] + [name]) +        return out + +    def delete_container_image(self, name): +        out = self.__run_command(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..17a9ab581 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -66,7 +66,7 @@ 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)", name):          return True      else:          return False 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/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/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 | 
