From b60e9a9a9140782b4e8144e322775aa6c03a6e49 Mon Sep 17 00:00:00 2001 From: kayal2001 Date: Tue, 25 Jun 2024 17:38:48 +0530 Subject: [PATCH] Feature 11009 Ns config template as first class citizens in OSM - CLI Change-Id: Iea1eddfc0df28a71a990a84feaa8fc6ab80ab786 Signed-off-by: kayal2001 --- osmclient/cli_commands/ns.py | 7 ++ osmclient/cli_commands/nspkg.py | 160 ++++++++++++++++++++++++++ osmclient/scripts/osm.py | 6 + osmclient/sol005/client.py | 2 + osmclient/sol005/nct.py | 198 ++++++++++++++++++++++++++++++++ osmclient/sol005/ns.py | 11 ++ 6 files changed, 384 insertions(+) create mode 100644 osmclient/sol005/nct.py diff --git a/osmclient/cli_commands/ns.py b/osmclient/cli_commands/ns.py index a21c492..ad07a11 100755 --- a/osmclient/cli_commands/ns.py +++ b/osmclient/cli_commands/ns.py @@ -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, diff --git a/osmclient/cli_commands/nspkg.py b/osmclient/cli_commands/nspkg.py index be0304d..5195d81 100755 --- a/osmclient/cli_commands/nspkg.py +++ b/osmclient/cli_commands/nspkg.py @@ -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) diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index 20cddc6..f67f550 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -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) diff --git a/osmclient/sol005/client.py b/osmclient/sol005/client.py index be11dd1..e0254c3 100644 --- a/osmclient/sol005/client.py +++ b/osmclient/sol005/client.py @@ -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 index 0000000..6836239 --- /dev/null +++ b/osmclient/sol005/nct.py @@ -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") diff --git a/osmclient/sol005/ns.py b/osmclient/sol005/ns.py index 8328dba..236674f 100644 --- a/osmclient/sol005/ns.py +++ b/osmclient/sol005/ns.py @@ -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: -- 2.25.1