diff --git a/doc/source/devref/vnffg_usage_guide.rst b/doc/source/devref/vnffg_usage_guide.rst index a8846a2b8..2b7052246 100644 --- a/doc/source/devref/vnffg_usage_guide.rst +++ b/doc/source/devref/vnffg_usage_guide.rst @@ -51,22 +51,28 @@ deploy a sample VNFFGD template such as the one `here `_. -*Note: A current constraint of the Forwarding Path policy match criteria is -to include the network_src_port_id, such as:* +.. note:: -:: + A current constraint of the Forwarding Path policy match criteria is + to include the network_src_port_id, such as: + + .. code-block:: yaml + + policy: + type: ACL + criteria: + - network_src_port_id: 640dfd77-c92b-45a3-b8fc-22712de480e1 - policy: - type: ACL - criteria: - - network_src_port_id: 640dfd77-c92b-45a3-b8fc-22712de480e1 This is required due to a limitation of Neutron networking-sfc and only applies to an OpenStack VIM. Tacker provides the following CLI to create a VNFFGD: -**tacker vnffgd-create --vnffgd-file ** +.. code-block:: console + + tacker vnffgd-create --vnffgd-file + Creating the VNFFG ~~~~~~~~~~~~~~~~~~ @@ -82,14 +88,23 @@ http://docs.openstack.org/developer/tacker/install/getting_started.html Tacker provides the following CLI to create VNFFG: - **tacker vnffg-create --vnffgd-id ** - **--vnffgd-name ** - **--vnf-mapping ** - **--symmetrical ** +.. code-block:: console + + tacker vnffg-create --vnffgd-name \ + --vnf-mapping --symmetrical + +If you use a parameterized vnffg template: + +.. code-block:: console + + tacker vnffg-create --vnffgd-name \ + --param-file --vnf-mapping \ + --symmetrical Here, -* vnffgd-id/name - VNFFGD to use to instantiate this VNFFG +* vnffgd-name - VNFFGD to use to instantiate this VNFFG +* param-file - Parameter file in Yaml. * vnf-mapping - Allows a list of logical VNFD to VNF instance mapping * symmetrical - True/False @@ -101,9 +116,11 @@ each VNF in the Forwarding Path. For example, imagine a Forwarding Path map each VNFD defined in the VNFFGD Forwarding Path to the desired VNF instance: - **tacker vnffg-create --vnffgd-name myvnffgd** - **--vnf-mapping VNF1:'91e32c20-6d1f-47a4-9ba7-08f5e5effe07',** - **VNF2:'7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'** +.. code-block:: console + + tacker vnffg-create --vnffgd-name myvnffgd \ + --vnf-mapping VNF1:'91e32c20-6d1f-47a4-9ba7-08f5e5effe07', \ + VNF2:'7168062e-9fa1-4203-8cb7-f5c99ff3ee1b' Alternatively, if no vnf-mapping is provided then Tacker VNFFG will attempt to search for VNF instances derived from the given VNFDs in the VNFFGD. If @@ -115,8 +132,10 @@ flow through the path. This creates an extra classifier to ensure return traffic flows through the chain in a reverse path, otherwise this traffic routed normally and does not enter the VNFFG. -*Note: Enabling symmetrical is not currently supported by the OpenStack VIM -driver* +.. note:: + + Enabling symmetrical is not currently supported by the OpenStack VIM + driver Viewing a VNFFG ~~~~~~~~~~~~~~~ @@ -128,12 +147,14 @@ a Classifier. The main command to view a VNFFG is 'tacker vnffg-show, however there are several commands available in order to view the sub-components for a rendered VNFFG: -- **tacker nfp-list** -- **tacker nfp-show ** -- **tacker chain-list** -- **tacker chain-show ** -- **tacker classifier-list** -- **tacker classifier-show ** +.. code-block:: console + + tacker nfp-list + tacker nfp-show + tacker chain-list + tacker chain-show + tacker classifier-list + tacker classifier-show Known Issues and Limitations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/releasenotes/notes/add-parameter-support-vnffg-b6b790b2f4590a91.yaml b/releasenotes/notes/add-parameter-support-vnffg-b6b790b2f4590a91.yaml new file mode 100644 index 000000000..93250a0b4 --- /dev/null +++ b/releasenotes/notes/add-parameter-support-vnffg-b6b790b2f4590a91.yaml @@ -0,0 +1,8 @@ +--- +features: + - Added parameter support for deploying VNF Forwarding Graph + Descriptor templates. With this addition the user can deploy + a VNFFG using an Onboarded parameterized template with + different parameter values using parameter yaml + file at each time without onboarding new template. + diff --git a/samples/tosca-templates/vnffgd/tosca-vnffgd-param-sample.yaml b/samples/tosca-templates/vnffgd/tosca-vnffgd-param-sample.yaml new file mode 100644 index 000000000..407f5dff8 --- /dev/null +++ b/samples/tosca-templates/vnffgd/tosca-vnffgd-param-sample.yaml @@ -0,0 +1,52 @@ +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0 + +description: Sample VNFFG parameterized template + +topology_template: + description: Sample VNFFG parameterized template + + inputs: + net_src_port_id: + type: string + description: Port UUID of source VM. + + dst_port_range: + type: string + description: Destination port range + + ip_dst_pre: + type: string + description: Cidr format of destination ip. + + node_templates: + + Forwarding_path1: + type: tosca.nodes.nfv.FP.Tacker + description: creates path (CP12->CP22) + properties: + id: 51 + policy: + type: ACL + criteria: + - network_src_port_id: { get_input: net_src_port_id } + - destination_port_range: { get_input: dst_port_range } + - ip_proto: 6 + - ip_dst_prefix: { get_input: ip_dst_pre } + path: + - forwarder: VNFD1 + capability: CP12 + - forwarder: VNFD2 + capability: CP22 + + groups: + VNFFG1: + type: tosca.groups.nfv.VNFFG + description: HTTP to Corporate Net + properties: + vendor: tacker + version: 1.0 + number_of_endpoints: 5 + dependent_virtual_link: [VL12,VL22] + connection_point: [CP12,CP22] + constituent_vnfs: [VNFD1,VNFD2] + members: [Forwarding_path1] diff --git a/samples/tosca-templates/vnffgd/vnffg-param-file.yaml b/samples/tosca-templates/vnffgd/vnffg-param-file.yaml new file mode 100644 index 000000000..d9e744e26 --- /dev/null +++ b/samples/tosca-templates/vnffgd/vnffg-param-file.yaml @@ -0,0 +1,6 @@ +{ + net_src_port_id: 640dfd77-c92b-45a3-b8fc-22712de480e1 + dst_port_range: 80-1024 + ip_dst_pre: + - 192.168.1.2/24 +} diff --git a/tacker/common/exceptions.py b/tacker/common/exceptions.py index 0faf96b5d..76afae60c 100644 --- a/tacker/common/exceptions.py +++ b/tacker/common/exceptions.py @@ -269,3 +269,7 @@ class VnfPolicyTypeInvalid(BadRequest): class DuplicateResourceName(TackerException): message = _("%(resource)s with name %(name)s already exists") + + +class InvalidParam(TackerException): + message = _("Param values must be a dict type") diff --git a/tacker/db/nfvo/vnffg_db.py b/tacker/db/nfvo/vnffg_db.py index f208f5298..1a0acc1d9 100644 --- a/tacker/db/nfvo/vnffg_db.py +++ b/tacker/db/nfvo/vnffg_db.py @@ -91,6 +91,8 @@ class Vnffg(model_base.BASE, models_v1.HasTenant, models_v1.HasId): # Mapping of VNFD to VNF instance names vnf_mapping = sa.Column(types.Json) + attributes = sa.Column(types.Json) + class VnffgNfp(model_base.BASE, models_v1.HasTenant, models_v1.HasId): """Network Forwarding Path Data Model""" @@ -310,6 +312,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin): description=template_db.description, vnf_mapping=vnf_mapping, vnffgd_id=template_id, + attributes=template_db.get('template'), status=constants.PENDING_CREATE) context.session.add(vnffg_db) @@ -677,7 +680,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin): 'forwarding_paths': vnffg_db.forwarding_paths[0]['id'] } key_list = ('id', 'tenant_id', 'name', 'description', - 'vnf_mapping', 'status', 'vnffgd_id') + 'vnf_mapping', 'status', 'vnffgd_id', 'attributes') res.update((key, vnffg_db[key]) for key in key_list) return self._fields(res, fields) diff --git a/tacker/nfvo/nfvo_plugin.py b/tacker/nfvo/nfvo_plugin.py index 4f316907d..5f6acb664 100644 --- a/tacker/nfvo/nfvo_plugin.py +++ b/tacker/nfvo/nfvo_plugin.py @@ -218,6 +218,13 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin, @log.log def create_vnffg(self, context, vnffg): + vnffg_attributes = vnffg['vnffg']['attributes'] + if vnffg_attributes.get('param_values'): + param = vnffg_attributes['param_values'] + if isinstance(param, dict): + vnffg_attributes['param_values'] = yaml.safe_dump(param) + else: + raise exceptions.InvalidParam() vnffg_dict = super(NfvoPlugin, self)._create_vnffg_pre(context, vnffg) nfp = super(NfvoPlugin, self).get_nfp(context, vnffg_dict['forwarding_paths']) diff --git a/tacker/tests/unit/db/utils.py b/tacker/tests/unit/db/utils.py index 33c1116e1..ef2f06ba1 100644 --- a/tacker/tests/unit/db/utils.py +++ b/tacker/tests/unit/db/utils.py @@ -32,8 +32,11 @@ ipparams = _get_template('vnf_cirros_param_values_ipaddr.yaml') userdata_params = _get_template('vnf_cirros_param_values_user_data.yaml') config_data = _get_template('config_data.yaml') update_config_data = _get_template('update_config_data.yaml') +vnffg_params = _get_template('vnffg_params.yaml') vnffgd_template = yaml.load(_get_template('vnffgd_template.yaml')) vnffgd_tosca_template = yaml.load(_get_template('tosca_vnffgd_template.yaml')) +vnffgd_tosca_param_template = yaml.load(_get_template( + 'tosca_vnffgd_param_template.yaml')) vnffgd_invalid_tosca_template = yaml.load(_get_template( 'tosca_invalid_vnffgd_template.yaml')) vnfd_scale_tosca_template = _get_template('tosca_scale.yaml') @@ -164,15 +167,30 @@ def get_dummy_vnffg_obj(): 'vnffgd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e', 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437', 'name': 'dummy_vnffg', + u'attributes': {u'template': vnffgd_tosca_template}, 'vnf_mapping': {}, 'symmetrical': False}} +def get_dummy_vnffg_param_obj(): + return {'vnffg': {'description': 'dummy_vnf_description', + 'vnffgd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e', + 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437', + 'name': 'dummy_vnffg', + u'attributes': { + u'template': vnffgd_tosca_param_template}, + 'vnf_mapping': {}, + u'attributes': {u'param_values': + yaml.safe_load(vnffg_params)}, + 'symmetrical': False}} + + def get_dummy_vnffg_obj_vnf_mapping(): return {'vnffg': {'description': 'dummy_vnf_description', 'vnffgd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e', 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437', 'name': 'dummy_vnffg', + u'attributes': {u'template': vnffgd_tosca_template}, 'vnf_mapping': { 'VNF1': '91e32c20-6d1f-47a4-9ba7-08f5e5effe07', 'VNF3': '7168062e-9fa1-4203-8cb7-f5c99ff3ee1b' diff --git a/tacker/tests/unit/vm/infra_drivers/openstack/data/tosca_vnffgd_param_template.yaml b/tacker/tests/unit/vm/infra_drivers/openstack/data/tosca_vnffgd_param_template.yaml new file mode 100644 index 000000000..7cc55d662 --- /dev/null +++ b/tacker/tests/unit/vm/infra_drivers/openstack/data/tosca_vnffgd_param_template.yaml @@ -0,0 +1,46 @@ +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0 + +description: example template + +topology_template: + description: Example VNFFG template + + inputs: + net_name: + type: string + description: Name of the network + + node_templates: + + Forwarding_path1: + type: tosca.nodes.nfv.FP.Tacker + description: creates path (CP11->CP12->CP32) + properties: + id: 51 + policy: + type: ACL + criteria: + - network_name: { get_input: net_name } + - destination_port_range: 80-1024 + - ip_proto: 6 + - ip_dst_prefix: 192.168.1.2/24 + path: + - forwarder: VNF1 + capability: CP11 + - forwarder: VNF1 + capability: CP12 + - forwarder: VNF3 + capability: CP32 + + groups: + VNFFG1: + type: tosca.groups.nfv.VNFFG + description: HTTP to Corporate Net + properties: + vendor: tacker + version: 1.0 + number_of_endpoints: 5 + dependent_virtual_link: [VL1,VL2,VL3] + connection_point: [CP11,CP12,CP32] + constituent_vnfs: [VNF1,VNF3] + members: [Forwarding_path1] diff --git a/tacker/tests/unit/vm/infra_drivers/openstack/data/vnffg_params.yaml b/tacker/tests/unit/vm/infra_drivers/openstack/data/vnffg_params.yaml new file mode 100644 index 000000000..eade06dac --- /dev/null +++ b/tacker/tests/unit/vm/infra_drivers/openstack/data/vnffg_params.yaml @@ -0,0 +1,3 @@ +{ + 'net_name': tenant1_net +} diff --git a/tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py b/tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py index a8a855c76..a9bd7aefc 100644 --- a/tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py +++ b/tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py @@ -257,6 +257,18 @@ class TestNfvoPlugin(db_base.SqlTestCase): session.flush() return vnffg_template + def _insert_dummy_vnffg_param_template(self): + session = self.context.session + vnffg_template = vnffg_db.VnffgTemplate( + id='eb094833-995e-49f0-a047-dfb56aaf7c4e', + tenant_id='ad7ebc56538745a08ef7c5e97f8bd437', + name='fake_template', + description='fake_template_description', + template={u'vnffgd': utils.vnffgd_tosca_param_template}) + session.add(vnffg_template) + session.flush() + return vnffg_template + def _insert_dummy_vnffg(self): session = self.context.session vnffg = vnffg_db.Vnffg( @@ -363,6 +375,27 @@ class TestNfvoPlugin(db_base.SqlTestCase): symmetrical=mock.ANY ) + def test_create_vnffg_param_values(self): + with patch.object(TackerManager, 'get_service_plugins') as \ + mock_plugins: + mock_plugins.return_value = {'VNFM': FakeVNFMPlugin()} + mock.patch('tacker.common.driver_manager.DriverManager', + side_effect=FakeDriverManager()).start() + self._insert_dummy_vnffg_param_template() + vnffg_obj = utils.get_dummy_vnffg_param_obj() + result = self.nfvo_plugin.create_vnffg(self.context, vnffg_obj) + self.assertIsNotNone(result) + self.assertIn('id', result) + self.assertIn('status', result) + self.assertEqual('PENDING_CREATE', result['status']) + self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY, + name=mock.ANY, + vnfs=mock.ANY, + fc_id=mock.ANY, + auth_attr=mock.ANY, + symmetrical=mock.ANY + ) + def test_create_vnffg_vnf_mapping(self): with patch.object(TackerManager, 'get_service_plugins') as \ mock_plugins: