From 66efce28d45f35ad661fb789b5d88a4c2c8b48e4 Mon Sep 17 00:00:00 2001 From: Shubham Potale <shubham.potale@nttdata.com> Date: Tue, 10 Dec 2019 15:39:20 +0530 Subject: [PATCH] OSC support to create vnf using vnflcm API Added a new command ``openstack vnflcm create`` to create a new vnf. Blueprint: support-etsi-nfv-specs Change-Id: Ia90955df6ac141661c3d58e4de4e098c4cb51aab --- lower-constraints.txt | 2 +- requirements.txt | 2 +- setup.cfg | 2 +- tackerclient/osc/v1/vnflcm/__init__.py | 0 tackerclient/osc/v1/vnflcm/vnflcm.py | 82 ++++++++++++++++++ tackerclient/tests/unit/osc/v1/test_vnflcm.py | 84 +++++++++++++++++++ .../tests/unit/osc/v1/vnflcm_fakes.py | 53 ++++++++++++ tackerclient/v1_0/client.py | 23 +++++ 8 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 tackerclient/osc/v1/vnflcm/__init__.py create mode 100644 tackerclient/osc/v1/vnflcm/vnflcm.py create mode 100644 tackerclient/tests/unit/osc/v1/test_vnflcm.py create mode 100644 tackerclient/tests/unit/osc/v1/vnflcm_fakes.py diff --git a/lower-constraints.txt b/lower-constraints.txt index da5af261..9d8a4655 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -34,7 +34,7 @@ oslo.context==2.19.2 oslo.i18n==3.15.3 oslo.log==3.36.0 oslo.serialization==2.18.0 -oslo.utils==3.33.0 +oslo.utils==3.40.0 pbr==2.0.0 pep8==1.5.7 positional==1.2.1 diff --git a/requirements.txt b/requirements.txt index 621b07f6..d5d0aced 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,5 +14,5 @@ Babel!=2.4.0,>=2.3.4 # BSD oslo.i18n>=3.15.3 # Apache-2.0 osc-lib>=1.8.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 -oslo.utils>=3.33.0 # Apache-2.0 +oslo.utils>=3.40.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index e6ac5fcd..cb9121b0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -83,7 +83,7 @@ openstack.tackerclient.v1 = vnf_package_show = tackerclient.osc.v1.vnfpkgm.vnf_package:ShowVnfPackage vnf_package_upload = tackerclient.osc.v1.vnfpkgm.vnf_package:UploadVnfPackage vnf_package_delete = tackerclient.osc.v1.vnfpkgm.vnf_package:DeleteVnfPackage - + vnflcm_create = tackerclient.osc.v1.vnflcm.vnflcm:CreateVnfLcm [build_releasenotes] all_files = 1 diff --git a/tackerclient/osc/v1/vnflcm/__init__.py b/tackerclient/osc/v1/vnflcm/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tackerclient/osc/v1/vnflcm/vnflcm.py b/tackerclient/osc/v1/vnflcm/vnflcm.py new file mode 100644 index 00000000..efc20682 --- /dev/null +++ b/tackerclient/osc/v1/vnflcm/vnflcm.py @@ -0,0 +1,82 @@ +# Copyright (C) 2020 NTT DATA +# 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. + +from osc_lib.command import command +from osc_lib import utils + +from tackerclient.i18n import _ +from tackerclient.osc import sdk_utils + +_mixed_case_fields = ('vnfInstanceName', 'vnfInstanceDescription', 'vnfdId', + 'vnfProvider', 'vnfProductName', 'vnfSoftwareVersion', + 'vnfdVersion', 'instantiationState') + + +def _get_columns(item): + column_map = { + 'id': 'ID', + 'vnfInstanceName': 'VNF Instance Name', + 'vnfInstanceDescription': 'VNF Instance Description', + 'vnfdId': 'VNFD ID', + 'vnfProvider': 'VNF Provider', + 'vnfProductName': 'VNF Product Name', + 'vnfSoftwareVersion': 'VNF Software Version', + 'vnfdVersion': 'VNFD Version', + 'instantiationState': 'Instantiation State', + 'links': 'Links', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + + +class CreateVnfLcm(command.ShowOne): + _description = _("Create a new VNF Instance") + + def get_parser(self, prog_name): + parser = super(CreateVnfLcm, self).get_parser(prog_name) + parser.add_argument( + 'vnfd_id', + metavar="<vnfd-id>", + help=_('Identifier that identifies the VNFD which defines the ' + 'VNF instance to be created.')) + parser.add_argument( + '--name', + metavar="<vnf-instance-name>", + help=_('Name of the VNF instance to be created.')) + parser.add_argument( + '--description', + metavar="<vnf-instance-description>", + help=_('Description of the VNF instance to be created.')) + return parser + + def args2body(self, parsed_args): + body = {} + body['vnfdId'] = parsed_args.vnfd_id + + if parsed_args.description: + body['vnfInstanceDescription'] = parsed_args.description + + if parsed_args.name: + body['vnfInstanceName'] = parsed_args.name + + return body + + def take_action(self, parsed_args): + client = self.app.client_manager.tackerclient + vnf = client.create_vnf_instance(self.args2body(parsed_args)) + display_columns, columns = _get_columns(vnf) + data = utils.get_item_properties( + sdk_utils.DictModel(vnf), + columns, mixed_case_fields=_mixed_case_fields) + return (display_columns, data) diff --git a/tackerclient/tests/unit/osc/v1/test_vnflcm.py b/tackerclient/tests/unit/osc/v1/test_vnflcm.py new file mode 100644 index 00000000..25670483 --- /dev/null +++ b/tackerclient/tests/unit/osc/v1/test_vnflcm.py @@ -0,0 +1,84 @@ +# Copyright (C) 2020 NTT DATA +# 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. + +import ddt +import mock +import os + +from oslo_utils.fixture import uuidsentinel + +from tackerclient.osc.v1.vnflcm import vnflcm +from tackerclient.tests.unit.osc import base +from tackerclient.tests.unit.osc.v1.fixture_data import client +from tackerclient.tests.unit.osc.v1 import vnflcm_fakes + + +class TestVnfLcm(base.FixturedTestCase): + client_fixture_class = client.ClientFixture + + def setUp(self): + super(TestVnfLcm, self).setUp() + self.url = client.TACKER_URL + self.header = {'content-type': 'application/json'} + self.app = mock.Mock() + self.app_args = mock.Mock() + self.client_manager = self.cs + self.app.client_manager.tackerclient = self.client_manager + + +def _get_columns_vnflcm(): + columns = ['ID', 'Instantiation State', 'VNF Instance Description', + 'VNF Instance Name', 'VNF Product Name', 'VNF Provider', + 'VNF Software Version', 'VNFD ID', 'VNFD Version', 'Links'] + return columns + + +@ddt.ddt +class TestCreateVnfLcm(TestVnfLcm): + + def setUp(self): + super(TestCreateVnfLcm, self).setUp() + self.create_vnf_lcm = vnflcm.CreateVnfLcm( + self.app, self.app_args, cmd_name='vnflcm create') + + def test_create_no_args(self): + self.assertRaises(base.ParserException, self.check_parser, + self.create_vnf_lcm, [], []) + + @ddt.data(True, False) + def test_take_action(self, optional_arguments): + arglist = [uuidsentinel.vnf_package_vnfd_id] + verifylist = [('vnfd_id', uuidsentinel.vnf_package_vnfd_id)] + + if optional_arguments: + arglist.extend(['--name', 'test', + '--description', 'test']) + verifylist.extend([('name', 'test'), + ('description', 'test')]) + + # command param + parsed_args = self.check_parser(self.create_vnf_lcm, arglist, + verifylist) + + json = vnflcm_fakes.vnf_instance_response() + self.requests_mock.register_uri( + 'POST', os.path.join(self.url, 'vnflcm/v1/vnf_instances'), + json=json, headers=self.header) + + columns, data = (self.create_vnf_lcm.take_action(parsed_args)) + self.assertItemsEqual(_get_columns_vnflcm(), + columns) + self.assertItemsEqual(vnflcm_fakes.get_vnflcm_data(json), + data) diff --git a/tackerclient/tests/unit/osc/v1/vnflcm_fakes.py b/tackerclient/tests/unit/osc/v1/vnflcm_fakes.py new file mode 100644 index 00000000..5f560626 --- /dev/null +++ b/tackerclient/tests/unit/osc/v1/vnflcm_fakes.py @@ -0,0 +1,53 @@ +# Copyright (C) 2020 NTT DATA +# 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. + +from oslo_utils.fixture import uuidsentinel + + +def vnf_instance_response(attrs=None): + """Create a fake vnf instance. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A vnf instance dict + """ + attrs = attrs or {} + + # Set default attributes. + dummy_vnf_instance = { + "id": uuidsentinel.vnf_instance_id, + "vnfInstanceName": "Fake-VNF-Instance", + "vnfInstanceDescription": "Fake VNF", + "vnfdId": uuidsentinel.vnf_package_vnfd_id, + "vnfProvider": "NTT NS lab", + "vnfProductName": "Sample VNF", + "vnfSoftwareVersion": "1.0", + "vnfdVersion": "1.0", + "instantiationState": "NOT_INSTANTIATED", + "links": "vnflcm/v1/vnf_instances/" + uuidsentinel.vnf_instance_id + + "/instantiate" + } + return dummy_vnf_instance + + +def get_vnflcm_data(vnf_instance): + """Get the vnf instance data. + + :return: + A tuple object sorted based on the name of the columns. + """ + # return the list of data as per column order + return tuple([vnf_instance[key] for key in sorted(vnf_instance.keys())]) diff --git a/tackerclient/v1_0/client.py b/tackerclient/v1_0/client.py index bc2fd30f..15c6d155 100644 --- a/tackerclient/v1_0/client.py +++ b/tackerclient/v1_0/client.py @@ -774,6 +774,23 @@ class VnfPackageClient(ClientBase): body=file_data) +class VnfLCMClient(ClientBase): + """Client for vnflcm APIs. + + Purpose of this class is to create required request url for vnflcm + APIs. + """ + + vnf_instances_path = '/vnflcm/v1/vnf_instances' + + def build_action(self, action): + return action + + @APIParamsCall + def create_vnf_instance(self, body): + return self.post(self.vnf_instances_path, body=body) + + class Client(object): """Unified interface to interact with multiple applications of tacker service. @@ -794,6 +811,7 @@ class Client(object): """ def __init__(self, **kwargs): + self.vnf_lcm_client = VnfLCMClient(**kwargs) self.vnf_package_client = VnfPackageClient(**kwargs) self.legacy_client = LegacyClient(**kwargs) @@ -1018,3 +1036,8 @@ class Client(object): def delete_vnf_package(self, vnf_package): return self.vnf_package_client.delete_vnf_package(vnf_package) + + # VnfLCMClient methods. + + def create_vnf_instance(self, body): + return self.vnf_lcm_client.create_vnf_instance(body)