Feature 11009 Ns config template as first class citizens in OSM - CLI 45/14445/3
authorkayal2001 <kayalvizhi.v@tataelxsi.co.in>
Tue, 25 Jun 2024 12:08:48 +0000 (17:38 +0530)
committerkayal2001 <kayalvizhi.v@tataelxsi.co.in>
Wed, 17 Jul 2024 08:57:17 +0000 (14:27 +0530)
Change-Id: Iea1eddfc0df28a71a990a84feaa8fc6ab80ab786
Signed-off-by: kayal2001 <kayalvizhi.v@tataelxsi.co.in>
osmclient/cli_commands/ns.py
osmclient/cli_commands/nspkg.py
osmclient/scripts/osm.py
osmclient/sol005/client.py
osmclient/sol005/nct.py [new file with mode: 0644]
osmclient/sol005/ns.py

index a21c492..ad07a11 100755 (executable)
@@ -334,6 +334,11 @@ def ns_show(ctx, name, literal, filter, output):
     help="do not return the control immediately, but keep it "
     "until the operation is completed, or timeout",
 )
+@click.option(
+    "--ns-config-template",
+    default=None,
+    help="specify the ns config template name or id",
+)
 @click.option("--timeout", default=None, help="ns deployment timeout")
 @click.pass_context
 def ns_create(
@@ -345,6 +350,7 @@ def ns_create(
     ssh_keys,
     config,
     config_file,
+    ns_config_template,
     wait,
     timeout,
 ):
@@ -362,6 +368,7 @@ def ns_create(
         nsd_name,
         ns_name,
         config=config,
+        ns_config_template=ns_config_template,
         ssh_keys=ssh_keys,
         account=vim_account,
         wait=wait,
index be0304d..5195d81 100755 (executable)
@@ -15,6 +15,7 @@
 
 import click
 from osmclient.cli_commands import utils
+from osmclient.common.exceptions import NotFound
 from prettytable import PrettyTable
 import yaml
 import json
@@ -330,3 +331,162 @@ def nsd_delete2(ctx, name, force):
     """
     logger.debug("")
     nsd_delete(ctx, name, force)
+
+
+@click.command(name="nsconfig-list", short_help="list all NS Config Templates")
+@click.option(
+    "--nsd",
+    default=None,
+    help="list all the NS Config Template based on the nsd",
+)
+@click.option("--long", is_flag=True, help="for more details")
+@click.pass_context
+def nsconfig_list(ctx, nsd, long):
+    """list all Ns config template in the system"""
+    logger.debug("")
+    if nsd:
+        utils.check_client_version(ctx.obj, "--nsd")
+        filter = nsd
+        resp = []
+        resp_id = ctx.obj.nct.list(filter)
+        resp_list = ctx.obj.nct.list()
+        for rep in resp_list:
+            if resp_id == rep.get("nsdId"):
+                resp.append(rep)
+        if not resp:
+            raise NotFound("ns config template not present for the nsd")
+    else:
+        resp = ctx.obj.nct.list()
+
+    if long:
+        table = PrettyTable(
+            [
+                "ns config template name",
+                "id",
+                "nsd id",
+                "onboarding state",
+                "operational state",
+                "usage state",
+                "date",
+                "last update",
+            ]
+        )
+    else:
+        table = PrettyTable(["ns config template name", "id", "nsd id"])
+    for nct in resp:
+        name = nct.get("name")
+        nsd_id = nct.get("nsdId")
+        if long:
+            onb_state = nct["_admin"].get("onboardingState", "-")
+            op_state = nct["_admin"].get("operationalState", "-")
+            usage_state = nct["_admin"].get("usageState", "-")
+            date = datetime.fromtimestamp(nct["_admin"]["created"]).strftime(
+                "%Y-%m-%dT%H:%M:%S"
+            )
+            last_update = datetime.fromtimestamp(nct["_admin"]["modified"]).strftime(
+                "%Y-%m-%dT%H:%M:%S"
+            )
+            table.add_row(
+                [
+                    name,
+                    nct["_id"],
+                    nsd_id,
+                    onb_state,
+                    op_state,
+                    usage_state,
+                    date,
+                    last_update,
+                ]
+            )
+        else:
+            table.add_row([name, nct["_id"], nsd_id])
+    table.align = "l"
+    print(table)
+
+
+@click.command(
+    name="nsconfig-show", short_help="shows the details of a Ns config template"
+)
+@click.option("--literal", is_flag=True, help="print literally, no pretty table")
+@click.argument("name")
+@click.pass_context
+def nsconfig_show(ctx, name, literal):
+    """shows the content of a Ns config template
+
+    NAME: name or ID of the Ns config template
+    """
+    logger.debug("")
+    resp = ctx.obj.nct.get(name)
+
+    if literal:
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
+        return
+
+    table = PrettyTable(["field", "value"])
+    for k, v in list(resp.items()):
+        table.add_row([k, utils.wrap_text(text=json.dumps(v, indent=2), width=100)])
+    table.align = "l"
+    print(table)
+
+
+@click.command(name="nsconfig-delete", short_help="deletes a Ns config template")
+@click.argument("name")
+@click.option(
+    "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
+)
+@click.pass_context
+def nsconfig_delete(ctx, name, force):
+    """deletes a Ns config template
+
+    NAME: name or ID of the Ns config template to be deleted
+    """
+    logger.debug("")
+    if not force:
+        ctx.obj.nct.delete(name)
+    else:
+        utils.check_client_version(ctx.obj, "--force")
+        ctx.obj.nct.delete(name, force)
+
+
+@click.command(name="nsconfig-create", short_help="creates a Ns config template")
+@click.argument("name")
+@click.option(
+    "--config_file",
+    prompt=True,
+    help="ns config template specific yaml configuration file",
+)
+@click.option(
+    "--nsd",
+    prompt=True,
+    help=" specify the nsd for the template",
+)
+@click.pass_context
+def nsconfig_create(ctx, name, config_file, nsd):
+    """creates a new Ns Config Template"""
+    logger.debug("")
+    if config_file:
+        utils.check_client_version(ctx.obj, "--config_file")
+        with open(config_file, "r") as cf:
+            config = cf.read()
+        ctx.obj.nct.create(name, config, nsd)
+
+
+@click.command(
+    name="nsconfig-update", short_help="update content of Ns config template"
+)
+@click.argument("name")
+@click.option("--newname", help="New name for the Ns config template")
+@click.option(
+    "--config_file", help="Upload config file to edit the instantiation parameters"
+)
+@click.pass_context
+def nsconfig_update(ctx, name, newname, config_file):
+    """update the name and config parameters in the template"""
+    logger.debug("")
+    config = None
+    if config_file:
+        utils.check_client_version(ctx.obj, "--config_file")
+        with open(config_file, "r") as cf:
+            config = cf.read()
+
+    ctx.obj.nct.update(name, newname, config)
index 20cddc6..f67f550 100755 (executable)
@@ -206,6 +206,12 @@ def cli():
         cli_osm.add_command(nspkg.nsd_update1)
         cli_osm.add_command(nspkg.nsd_update2)
 
+        cli_osm.add_command(nspkg.nsconfig_list)
+        cli_osm.add_command(nspkg.nsconfig_show)
+        cli_osm.add_command(nspkg.nsconfig_delete)
+        cli_osm.add_command(nspkg.nsconfig_create)
+        cli_osm.add_command(nspkg.nsconfig_update)
+
         cli_osm.add_command(other.get_version)
 
         cli_osm.add_command(packages.descriptor_translate)
index be11dd1..e0254c3 100644 (file)
@@ -23,6 +23,7 @@ from osmclient.sol005 import nsd
 from osmclient.sol005 import nst
 from osmclient.sol005 import nsi
 from osmclient.sol005 import ns
+from osmclient.sol005 import nct
 from osmclient.sol005 import vnf
 from osmclient.sol005 import vim
 from osmclient.sol005 import wim
@@ -83,6 +84,7 @@ class Client(object):
         self.nst = nst.Nst(self._http_client, client=self)
         self.package = package.Package(self._http_client, client=self)
         self.ns = ns.Ns(self._http_client, client=self)
+        self.nct = nct.Nct(self._http_client, client=self)
         self.nsi = nsi.Nsi(self._http_client, client=self)
         self.vim = vim.Vim(self._http_client, client=self)
         self.wim = wim.Wim(self._http_client, client=self)
diff --git a/osmclient/sol005/nct.py b/osmclient/sol005/nct.py
new file mode 100644 (file)
index 0000000..6836239
--- /dev/null
@@ -0,0 +1,198 @@
+# Copyright 2018 Telefonica
+#
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""
+OSM nct API handling
+"""
+
+from osmclient.common.exceptions import NotFound
+from osmclient.common.exceptions import ClientException
+from osmclient.common import utils
+import yaml
+import json
+import logging
+
+
+class Nct(object):
+    def __init__(self, http=None, client=None):
+        self._http = http
+        self._client = client
+        self._logger = logging.getLogger("osmclient")
+        self._apiName = "/nsd"
+        self._apiVersion = "/v1"
+        self._apiResource = "/ns_config_template"
+        self._apiBase = "{}{}{}".format(
+            self._apiName, self._apiVersion, self._apiResource
+        )
+
+    def list(self, nsd=None):
+        self._logger.debug("")
+        self._client.get_token()
+        filter_string = ""
+        if nsd:
+            filter_string = "?{}".format(nsd)
+            _, resp = self._http.get2_cmd(
+                "{}{}/ns_descriptors{}".format(
+                    self._apiName, self._apiVersion, filter_string
+                )
+            )
+            resp = json.loads(resp)
+            for rep in resp:
+                if nsd == rep.get("_id"):
+                    resp_id = rep.get("_id")
+                    return resp_id
+                if nsd == rep.get("name"):
+                    resp_id = rep.get("_id")
+                    return resp_id
+            raise NotFound("nsd {} not found".format(nsd))
+        _, resp = self._http.get2_cmd("{}".format(self._apiBase))
+        if resp:
+            return json.loads(resp)
+        return list()
+
+    def get(self, name):
+        self._logger.debug("")
+        self._client.get_token()
+        if utils.validate_uuid4(name):
+            for nct in self.list():
+                if name == nct["_id"]:
+                    return nct
+        else:
+            for nct in self.list():
+                if "name" in nct and name == nct["name"]:
+                    return nct
+        raise NotFound("ns config template {} not found".format(name))
+
+    def delete(self, name, force=False):
+        self._logger.debug("")
+        nct = self.get(name)
+        querystring = ""
+        if force:
+            querystring = "?FORCE=True"
+        http_code, resp = self._http.delete_cmd(
+            "{}/{}{}".format(self._apiBase, nct["_id"], querystring)
+        )
+
+        if http_code == 202:
+            print("Deletion in progress")
+        elif http_code == 204:
+            print("Deleted")
+        else:
+            msg = resp or ""
+            raise ClientException(
+                "failed to delete ns config template {} - {}".format(name, msg)
+            )
+
+    def create(self, name, config, nsd):
+        template = {}
+        vim_account_id = {}
+        config_param = yaml.safe_load(config)
+
+        def get_vim_account_id(vim_account):
+            self._logger.debug("")
+            if vim_account_id.get(vim_account):
+                return vim_account_id[vim_account]
+            vim = self._client.vim.get(vim_account)
+            if vim is None:
+                raise NotFound("cannot find vim account '{}'".format(vim_account))
+            vim_account_id[vim_account] = vim["_id"]
+            return vim["_id"]
+
+        if "vim-network-name" in config_param:
+            config_param["vld"] = config_param.pop("vim-network-name")
+        if "vld" in config_param:
+            if not isinstance(config_param["vld"], list):
+                raise ClientException(
+                    "Error at --config 'vld' must be a list of dictionaries"
+                )
+            for vld in config_param["vld"]:
+                if not isinstance(vld, dict):
+                    raise ClientException(
+                        "Error at --config 'vld' must be a list of dictionaries"
+                    )
+                if vld.get("vim-network-name"):
+                    if isinstance(vld["vim-network-name"], dict):
+                        vim_network_name_dict = {}
+                        for vim_account, vim_net in vld["vim-network-name"].items():
+                            vim_network_name_dict[get_vim_account_id(vim_account)] = (
+                                vim_net
+                            )
+                        vld["vim-network-name"] = vim_network_name_dict
+        if "vnf" in config_param:
+            for vnf in config_param["vnf"]:
+                if vnf.get("vim_account"):
+                    vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account"))
+
+        if "additionalParamsForNs" in config_param:
+            if not isinstance(config_param["additionalParamsForNs"], dict):
+                raise ClientException(
+                    "Error at --config 'additionalParamsForNs' must be a dictionary"
+                )
+        if "additionalParamsForVnf" in config_param:
+            if not isinstance(config_param["additionalParamsForVnf"], list):
+                raise ClientException(
+                    "Error at --config 'additionalParamsForVnf' must be a list"
+                )
+            for additional_param_vnf in config_param["additionalParamsForVnf"]:
+                if not isinstance(additional_param_vnf, dict):
+                    raise ClientException(
+                        "Error at --config 'additionalParamsForVnf' items must be dictionaries"
+                    )
+                if not additional_param_vnf.get("member-vnf-index"):
+                    raise ClientException(
+                        "Error at --config 'additionalParamsForVnf' items must contain "
+                        "'member-vnf-index'"
+                    )
+        template["name"] = name
+        template["config"] = config_param
+        template["nsdId"] = self.list(nsd)
+
+        try:
+            headers = self._client._headers
+            headers["Content-Type"] = "application/json"
+            self._http.set_http_header(headers)
+            http_code, resp = self._http.post_cmd(
+                endpoint=self._apiBase, postfields_dict=template
+            )
+
+            if resp:
+                resp = json.loads(resp)
+                print(resp.get("id"))
+            if not resp or "id" not in resp:
+                raise ClientException(
+                    "unexpected response from server - {} ".format(resp)
+                )
+        except ClientException as exc:
+            message = "failed to create ns config template: {}:\nerror:\n{}".format(
+                name, str(exc)
+            )
+            raise ClientException(message)
+
+    def update(self, name, newname=None, config=None):
+        self._logger.debug("")
+        template_edit = {}
+        config_name = self.get(name)
+        if newname:
+            template_edit["name"] = newname
+        if config:
+            config_content = yaml.safe_load(config)
+            template_edit["config"] = config_content
+        http_code, resp = self._http.put_cmd(
+            endpoint="{}/{}/template_content".format(self._apiBase, config_name["_id"]),
+            postfields_dict=template_edit,
+        )
+        if http_code == 204:
+            print("Updated")
index 8328dba..236674f 100644 (file)
@@ -159,6 +159,7 @@ class Ns(object):
         nsr_name,
         account,
         config=None,
+        ns_config_template=None,
         ssh_keys=None,
         description="default description",
         admin_status="ENABLED",
@@ -212,6 +213,16 @@ class Ns(object):
                     ns["ssh_keys"].append(f.read())
         if timeout:
             ns["timeout_ns_deploy"] = timeout
+        if ns_config_template:
+            _, resp = self._http.get2_cmd(
+                "/nsd{}/ns_config_template".format(self._apiVersion)
+            )
+            resp = json.loads(resp)
+            for rep in resp:
+                if ns_config_template == rep.get("name"):
+                    ns["nsConfigTemplateId"] = rep.get("_id")
+                if ns_config_template == rep.get("_id"):
+                    ns["nsConfigTemplateId"] = rep.get("_id")
         if config:
             ns_config = yaml.safe_load(config)
             if "vim-network-name" in ns_config: