From ba1332dd34ebc360df712431494955a535ec4125 Mon Sep 17 00:00:00 2001 From: Hashir Mohammed Date: Wed, 26 Apr 2017 11:56:02 -0400 Subject: [PATCH] Merge TOSCA changes from RIFT to OSM Signed-off-by: Hashir Mohammed --- common/python/CMakeLists.txt | 1 + .../mano/tosca_translator/dummy_vnf_node.yaml | 2 +- .../rwmano/syntax/mano_template.py | 21 +- .../rwmano/tosca/tosca_compute.py | 35 +- .../rwmano/tosca/tosca_initial_config.py | 10 +- .../rwmano/tosca/tosca_network_network.py | 5 +- .../rwmano/tosca/tosca_nfv_vnf.py | 18 + .../rwmano/tosca/tosca_vnf_configuration.py | 11 +- .../tosca/tosca_vnf_ns_service_primitive.py | 119 ++ .../rwmano/translate_node_templates.py | 24 +- .../rift/mano/tosca_translator/shell.py | 2 + .../test/data/tosca_ping_pong_epa.zip | Bin 0 -> 16132 bytes .../Definitions/ping_pong_nsd.yaml | 57 + .../Definitions/ping_vnfd.yaml | 132 ++ .../Definitions/pong_vnfd.yaml | 127 ++ .../Definitions/riftiotypes.yaml | 1493 +++++++++++++++++ .../TOSCA-Metadata/TOSCA.meta | 4 + .../scripts/ping_set_rate.py | 108 ++ .../scripts/start_traffic.py | 118 ++ .../test/tosca_translator_ut.py | 14 +- .../mano/yang_translator/riftiotypes.yaml | 19 + .../rwmano/syntax/tosca_resource.py | 10 +- .../rwmano/syntax/tosca_template.py | 2 +- .../rwmano/translate_descriptors.py | 25 +- .../yang_translator/rwmano/yang/yang_nsd.py | 154 +- .../yang_translator/rwmano/yang/yang_vnfd.py | 41 +- .../yang_translator/rwmano/yang_translator.py | 29 +- 27 files changed, 2477 insertions(+), 104 deletions(-) create mode 100644 common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_ns_service_primitive.py create mode 100644 common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa.zip create mode 100644 common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/ping_pong_nsd.yaml create mode 100644 common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/ping_vnfd.yaml create mode 100644 common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/pong_vnfd.yaml create mode 100644 common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/riftiotypes.yaml create mode 100644 common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/TOSCA-Metadata/TOSCA.meta create mode 100755 common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/scripts/ping_set_rate.py create mode 100755 common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/scripts/start_traffic.py diff --git a/common/python/CMakeLists.txt b/common/python/CMakeLists.txt index 8fba078f..2c2c57da 100644 --- a/common/python/CMakeLists.txt +++ b/common/python/CMakeLists.txt @@ -124,6 +124,7 @@ rift_python_install_tree( rift/mano/tosca_translator/rwmano/tosca/tosca_initial_config.py rift/mano/tosca_translator/rwmano/tosca/tosca_placement_group.py rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py + rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_ns_service_primitive.py rift/mano/tosca_translator/rwmano/tosca/tosca_forwarding_graph.py rift/mano/tosca_translator/rwmano/tosca/tosca_forwarding_path.py rift/mano/tosca_translator/common/__init__.py diff --git a/common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml b/common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml index 4debc765..21e32f41 100644 --- a/common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml +++ b/common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml @@ -1,5 +1,5 @@ tosca_definitions_version: tosca_simple_profile_for_nfv_1_0 -description: Toy NS +description: Translated from Tosca data_types: tosca.datatypes.nfv.riftio.dashboard_params: properties: diff --git a/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_template.py b/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_template.py index d263e6f0..ba0bd2ac 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_template.py +++ b/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_template.py @@ -69,6 +69,8 @@ class ManoTemplate(object): nsd.vendor = self.metadata['vendor'] nsd.short_name = self.metadata['name'] nsd.version = self.metadata['version'] + if 'logo' in self.metadata: + nsd.logo = self.metadata['logo'] except Exception as e: self.log.warning(_("Unable to use YANG GI to generate " "descriptors, falling back to alternate " @@ -91,10 +93,25 @@ class ManoTemplate(object): if resource.type == 'vld': resource.generate_yang_model(nsd, vnfds, use_gi=use_gi) + vnf_type_duplicate = [] + vnfd_resources = [] + vnfd_duplicate_resource_list = [] for resource in self.resources: - # Do the vnfds next if resource.type == 'vnfd': + vnfd_resources.append(resource) + + vnfd_resources.sort(key=lambda x: x.member_vnf_id, reverse=False) + vnf_type_to_vnf_id = {} + for resource in vnfd_resources: + if resource.vnf_type not in vnf_type_duplicate: resource.generate_yang_model(nsd, vnfds, use_gi=use_gi) + vnf_type_to_vnf_id[resource.vnf_type] = resource.id + vnf_type_duplicate.append(resource.vnf_type) + else: + vnfd_duplicate_resource_list.append(resource) + + for resource in vnfd_duplicate_resource_list: + resource.generate_nsd_constiuent(nsd, vnf_type_to_vnf_id[resource.vnf_type]) for resource in self.resources: # Do the other nodes @@ -127,7 +144,7 @@ class ManoTemplate(object): # Need to add support to get script names, charms, etc. other_files = {} for resource in self.resources: - resource.get_supporting_files(other_files) + resource.get_supporting_files(other_files, desc_id=nsd_id) for policy in self.policies: policy.get_supporting_files(other_files, desc_id=nsd_id) diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py index 79384857..190a5b45 100755 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py @@ -101,12 +101,7 @@ class ToscaCompute(ManoResource): format(self.name, tosca_props)) vdu_props = {} for key, value in tosca_props.items(): - if key == 'cloud_init': - vdu_props['cloud-init'] = value - elif key == 'cloud-init-file': - self._cloud_init = "../cloud_init/{}".format(value) - else: - vdu_props[key] = value + vdu_props[key] = value if 'name' not in vdu_props: vdu_props['name'] = self.name @@ -290,8 +285,6 @@ class ToscaCompute(ManoResource): self.properties['guest-epa'] = get_guest_epa(tosca_caps['numa_extension'], tosca_caps['nfv_compute']) if 'monitoring_param' in tosca_caps: self._monitor_param.append(get_monitor_param(tosca_caps['monitoring_param'], '1')) - if 'monitoring_param_1' in tosca_caps: - self._monitor_param.append(get_monitor_param(tosca_caps['monitoring_param_1'], '2')) if 'mgmt_interface' in tosca_caps: self._mgmt_interface = get_mgmt_interface(tosca_caps['mgmt_interface']) if len(self._mgmt_interface) > 0: @@ -303,7 +296,20 @@ class ToscaCompute(ManoResource): prop['port'] = self._mgmt_interface['dashboard-params']['port'] self._http_endpoint = prop + mon_idx = 2 + monitoring_param_name = 'monitoring_param_1' + while True: + if monitoring_param_name in tosca_caps: + self._monitor_param.append(get_monitor_param(tosca_caps[monitoring_param_name], str(mon_idx))) + mon_idx += 1 + monitoring_param_name = 'monitoring_param_{}'.format(mon_idx) + else: + break + # THis is a quick hack to remove monitor params without name + for mon_param in list(self._monitor_param): + if 'name' not in mon_param: + self._monitor_param.remove(mon_param) def handle_artifacts(self): if self.artifacts is None: @@ -343,8 +349,8 @@ class ToscaCompute(ManoResource): self.artifacts = arts def handle_interfaces(self): - # Currently, we support only create operation - operations_deploy_sequence = ['create'] + # Currently, we support the following: + operations_deploy_sequence = ['create', 'configure'] operations = ManoResource._get_all_operations(self.nodetemplate) @@ -357,7 +363,11 @@ class ToscaCompute(ManoResource): self.operations[operation.name] = operation.implementation for name, details in self.artifacts.items(): if name == operation.implementation: - self._image = details['file'] + if operation.name == 'create': + self._image = details['file'] + elif operation.name == 'configure': + self._cloud_init = details['file'] + break except KeyError as e: self.log.exception(e) return None @@ -412,6 +422,9 @@ class ToscaCompute(ManoResource): if self._image_cksum: self.properties['image-checksum'] = self._image_cksum + if self._cloud_init: + self.properties['cloud-init-file'] = os.path.basename(self._cloud_init) + for key in ToscaCompute.IGNORE_PROPS: if key in self.properties: self.properties.pop(key) diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_initial_config.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_initial_config.py index 9b7cd039..262c11ad 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_initial_config.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_initial_config.py @@ -53,8 +53,7 @@ class ToscaInitialConfig(ManoResource): self.log.debug(_("{0} with tosca properties: {1}"). format(self, tosca_props)) self.properties['name'] = tosca_props['name'] - self.properties['seq'] = \ - tosca_props['seq'] + self.properties['seq'] = int(tosca_props['seq']) self.properties['user-defined-script'] = \ tosca_props['user_defined_script'] self.scripts.append('../scripts/{}'. \ @@ -62,12 +61,11 @@ class ToscaInitialConfig(ManoResource): if 'parameter' in tosca_props: self.properties['parameter'] = [] - for name, value in tosca_props['parameter'].items(): + for parameter in tosca_props['parameter']: self.properties['parameter'].append({ - 'name': name, - 'value': value, + 'name': parameter['name'], + 'value': str(parameter['value']), }) - self.log.debug(_("{0} properties: {1}").format(self, self.properties)) def get_policy_props(self): diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_network_network.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_network_network.py index a9f9c778..88a8a31b 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_network_network.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_network_network.py @@ -91,8 +91,8 @@ class ToscaNetwork(ManoResource): ip_profile_param['ip-version'] = 'ipv' + str(specs['ip_version']) if 'cidr' in specs: ip_profile_param['subnet-address'] = specs['cidr'] + ip_profile_prop['ip-profile-params'] = ip_profile_param - ip_profile_prop['ip-profile-params'] = ip_profile_param return ip_profile_prop tosca_props = self.get_tosca_props() self._vld = get_vld_props(tosca_props) @@ -128,7 +128,8 @@ class ToscaNetwork(ManoResource): ip_profile_props = convert_keys_to_python(self._ip_profile) try: nsd.vld.add().from_dict(vld_props) - nsd.ip_profiles.add().from_dict(ip_profile_props) + if len(ip_profile_props) > 1: + nsd.ip_profiles.add().from_dict(ip_profile_props) except Exception as e: err_msg = _("{0} Exception vld from dict {1}: {2}"). \ format(self, props, e) diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py index e4e045e3..3346da02 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py @@ -61,7 +61,9 @@ class ToscaNfvVnf(ManoResource): self._policies = [] self._cps = [] self.vnf_type = nodetemplate.type + self.member_vnf_id = None self._reqs = {} + self.logo = None def map_tosca_name_to_mano(self, name): new_name = super().map_tosca_name_to_mano(name) @@ -120,6 +122,7 @@ class ToscaNfvVnf(ManoResource): if key == 'id': self._const_vnfd['member-vnf-index'] = int(value) self._const_vnfd['vnfd-id-ref'] = self.id + self.member_vnf_id = int(value) elif key == 'vnf_configuration': self._vnf_config = get_vnf_config(value) else: @@ -145,6 +148,8 @@ class ToscaNfvVnf(ManoResource): vnf_props.pop('start_by_default') if 'logo' in self.metadata: vnf_props['logo'] = self.metadata['logo'] + self.logo = self.metadata['logo'] + self.log.debug(_("VNF {0} with constituent vnf: {1}"). format(self.name, self._const_vnfd)) @@ -295,6 +300,12 @@ class ToscaNfvVnf(ManoResource): nsd['constituent-vnfd'] = [] nsd['constituent-vnfd'].append(self._const_vnfd) + def generate_nsd_constiuent(self, nsd, vnf_id): + self._const_vnfd['vnfd-id-ref'] = vnf_id + props = convert_keys_to_python(self._const_vnfd) + nsd.constituent_vnfd.add().from_dict(props) + + def get_member_vnf_index(self): return self._const_vnfd['member-vnf-index'] @@ -311,3 +322,10 @@ class ToscaNfvVnf(ManoResource): 'type': 'cloud_init', 'name': vdu.cloud_init, },) + if self.logo is not None: + files[desc_id] = [] + file_location = "../icons/{}".format(self.logo) + files[desc_id].append({ + 'type': 'icons', + 'name': file_location, + },) diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py index f90c1874..23cebfd0 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py @@ -71,15 +71,16 @@ class ToscaVnfConfiguration(ManoResource): prop["script"]["script-type"] = tosca_props['config']['config_details']['script_type'] if 'initial_config' in tosca_props: prop['initial-config-primitive'] = [] - #print("Weleek " + str(tosca_props['initial_config'])) for init_config in tosca_props['initial_config']: if 'parameter' in init_config: parameters = init_config.pop('parameter') init_config['parameter'] = [] - for key, value in parameters.items(): - init_config['parameter'].append({'name': key, 'value': str(value)}) - if 'user_defined_script' in init_config: - self.scripts.append('../scripts/{}'. \ + for parameter in parameters: + for key, value in parameter.items(): + init_config['parameter'].append({'name': key, 'value': str(value)}) + + if 'user_defined_script' in init_config: + self.scripts.append('../scripts/{}'. \ format(init_config['user_defined_script'])) prop['initial-config-primitive'].append(init_config) diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_ns_service_primitive.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_ns_service_primitive.py new file mode 100644 index 00000000..eef4a9e0 --- /dev/null +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_ns_service_primitive.py @@ -0,0 +1,119 @@ +# +# Copyright 2016 RIFT.io Inc +# +# 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 rift.mano.tosca_translator.common.utils import _ +from rift.mano.tosca_translator.common.utils import convert_keys_to_python +from rift.mano.tosca_translator.rwmano.syntax.mano_resource import ManoResource +from toscaparser.functions import GetInput +from rift.mano.tosca_translator.common.utils import convert_keys_to_python + +from toscaparser.common.exception import ValidationError + + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaVnfNSServiceConfiguration' + + +class ToscaVnfNSServiceConfiguration(ManoResource): + '''Translate TOSCA node type tosca.policies.Scaling.''' + + toscatype = 'tosca.policies.nfv.riftio.ns_service_primitives' + + IGNORE_PROPS = [] + VALUE_TYPE_CONVERSION_MAP = { + 'integer': 'INTEGER', + 'string':'STRING', + 'float':'DECIMAL', + 'INTEGER': 'INTEGER', + 'FLOAT':'DECIMAL' + + } + + def __init__(self, log, policy, metadata=None, vnf_name = None): + self.log = log + self.name = policy.name + self.type_ = 'place-grp' + self.metadata = metadata + self.linked_to_vnf = False + self.policy = policy + self.service_primitive = None + self.properties = {} + self.scripts = [] + + def __str__(self): + return "%s(%s)" % (self.name, self.type) + + def handle_properties(self, nodes, groups): + tosca_props = self.get_policy_props() + service_primitive = {} + if 'name' in tosca_props: + service_primitive['name'] = tosca_props['name'] + if 'user_defined_script' in tosca_props: + service_primitive['user_defined_script'] = tosca_props['user_defined_script'] + self.scripts.append('../scripts/{}'. \ + format(tosca_props['user_defined_script'])) + + + if 'parameter' in tosca_props: + service_primitive['parameter'] = [] + for parameter in tosca_props['parameter']: + prop = {} + if 'name' in parameter: + prop['name'] = parameter['name'] + if 'hidden' in parameter: + prop['hidden'] = parameter['hidden'] + if 'mandatory' in parameter: + prop['mandatory'] = parameter['mandatory'] + if 'data_type' in parameter: + prop['data_type'] = ToscaVnfNSServiceConfiguration.VALUE_TYPE_CONVERSION_MAP[parameter['data_type']] + if 'default_value' in parameter: + prop['default_value'] = str(parameter['default_value']) + service_primitive['parameter'].append(prop) + + self.service_primitive = service_primitive + + + + + #self.properties = prop + + def generate_yang_submodel_gi(self, vnfd): + pass + + def generate_yang_model(self, nsd, vnfds, use_gi): + if self.service_primitive is not None: + nsd.service_primitive.add().from_dict(self.service_primitive) + + def get_policy_props(self): + tosca_props = {} + + for prop in self.policy.get_properties_objects(): + if isinstance(prop.value, GetInput): + tosca_props[prop.name] = {'get_param': prop.value.input_name} + else: + tosca_props[prop.name] = prop.value + return tosca_props + def get_supporting_files(self, files, desc_id=None): + if not len(self.scripts): + return + if desc_id not in files: + return + for script in self.scripts: + files[desc_id].append({ + 'type': 'script', + 'name': script, + },) \ No newline at end of file diff --git a/common/python/rift/mano/tosca_translator/rwmano/translate_node_templates.py b/common/python/rift/mano/tosca_translator/rwmano/translate_node_templates.py index 2d6c3e1a..ef36e569 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/translate_node_templates.py +++ b/common/python/rift/mano/tosca_translator/rwmano/translate_node_templates.py @@ -167,6 +167,7 @@ class TranslateNodeTemplates(object): self.log.debug(_("Metadata {0}").format(metadata)) self.metadata = metadata + def _recursive_handle_properties(self, resource): '''Recursively handle the properties of the depends_on_nodes nodes.''' # Use of hashtable (dict) here should be faster? @@ -227,8 +228,9 @@ class TranslateNodeTemplates(object): vnf_type_to_vdus_map[vnf_type].append(node.name) for policy in template.policies: policies.append(policy.name) - for req in template.substitution_mappings.requirements: - vnf_type_substitution_mapping[template.substitution_mappings.node_type].append(req) + if template.substitution_mappings.requirements: + for req in template.substitution_mappings.requirements: + vnf_type_substitution_mapping[template.substitution_mappings.node_type].append(req) if template.substitution_mappings.capabilities: for capability in template.substitution_mappings.capabilities: sub_list = template.substitution_mappings.capabilities[capability] @@ -273,7 +275,6 @@ class TranslateNodeTemplates(object): metadata=self.metadata) mano_node.vnf_type = vnf_type self.mano_resources.append(mano_node) - print("Adding a new node") for node in self.tosca.nodetemplates: if 'VDU' in node.type: @@ -317,6 +318,7 @@ class TranslateNodeTemplates(object): vnf_name=vnf_node) self.mano_policies.append(policy_node) + vnfd_resources = [] for node in self.mano_resources: self.log.debug(_("Handle properties for {0} of type {1}"). format(node.name, node.type_)) @@ -338,6 +340,22 @@ class TranslateNodeTemplates(object): format(node.name, node.type_)) node.update_image_checksum(self.tosca.path) + for node in list(self.mano_resources): + if node.type == "vnfd": + vnfd_resources.append(node) + self.mano_resources.remove(node) + + vnfd_resources.sort(key=lambda x: x.member_vnf_id, reverse=True) + vnf_type_duplicate_map = {} + for node in reversed(vnfd_resources): + if node.vnf_type in vnf_type_duplicate_map: + for policy in self.mano_policies: + if hasattr(policy, '_vnf_name') and policy._vnf_name == node.name: + policy._vnf_name = vnf_type_duplicate_map[node.vnf_type] + continue + vnf_type_duplicate_map[node.vnf_type] = node.name + + self.mano_resources.extend(vnfd_resources) for node in self.mano_resources: # Handle vnf and vdu dependencies first if node.type == "vnfd": diff --git a/common/python/rift/mano/tosca_translator/shell.py b/common/python/rift/mano/tosca_translator/shell.py index 9221c79a..b5529f07 100644 --- a/common/python/rift/mano/tosca_translator/shell.py +++ b/common/python/rift/mano/tosca_translator/shell.py @@ -364,6 +364,8 @@ class TranslatorShell(object): dest = os.path.join(output_dir, 'images') elif ty == 'script': dest = os.path.join(output_dir, 'scripts') + elif ty == 'icons': + dest = os.path.join(output_dir, 'icons') elif ty == 'cloud_init': dest = os.path.join(output_dir, 'cloud_init') else: diff --git a/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa.zip b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa.zip new file mode 100644 index 0000000000000000000000000000000000000000..5ca90640d45d515b5bafedf866967e3371f772cf GIT binary patch literal 16132 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\n\ + ping_mgmt_port=18888\n\n# VNF specific configuration\npong_server_ip=\nping_rate=5\nserver_port=5555\n\n# Make rest API calls\ + \ to configure VNF\ncurl -D /dev/null \\\n -H \"Accept: application/vnd.yang.data+xml\"\ + \ \\\n -H \"Content-Type: application/vnd.yang.data+json\" \\\n \ + \ -X POST \\\n -d \"{\\\"ip\\\":\\\"$pong_server_ip\\\", \\\"port\\\ + \":$server_port}\" \\\n http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/server\n\ + rc=$?\nif [ $rc -ne 0 ]\nthen\n echo \"Failed to set server info for\ + \ ping!\"\n exit $rc\nfi\nexit 0\n" + config_type: script + initial_config_primitive: + - name: set ping rate + parameter: + - rate: 5 + seq: 1 + user_defined_script: ping_set_rate.py + targets: [ping_vnfd_iovdu_0] + type: tosca.policies.nfv.riftio.vnf_configuration + substitution_mappings: + node_type: tosca.nodes.nfv.riftio.pingvnfdVNF + requirements: + - virtualLink1: [ping_vnfd_cp0, virtualLink] + node_templates: + ping_vnfd_iovdu_0: + type: tosca.nodes.nfv.riftio.VDU1 + properties: + cloud_init: "#cloud-config\npassword: fedora\nchpasswd: { expire: False }\n\ + ssh_pwauth: True\nruncmd:\n - [ systemctl, daemon-reload ]\n - [ systemctl,\ + \ enable, ping.service ]\n - [ systemctl, start, --no-block, ping.service\ + \ ]\n - [ ifup, eth1 ]\n" + count: 1 + capabilities: + hypervisor_epa: + properties: + type: PREFER_KVM + version: 1 + mgmt_interface: + properties: + dashboard_params: + path: api/v1/ping/stats + port: 18888 + port: 18888 + protocol: tcp + monitoring_param: + properties: + description: no of ping requests + json_query_method: namekey + name: ping-request-tx-count + ui_data: + group_tag: Group1 + units: packets + widget_type: counter + url_path: api/v1/ping/stats + monitoring_param_1: + properties: + description: no of ping responses + json_query_method: namekey + name: ping-response-rx-count + ui_data: + group_tag: Group1 + units: packets + widget_type: counter + url_path: api/v1/ping/stats + nfv_compute: + properties: + cpu_allocation: + cpu_affinity: dedicated + thread_allocation: prefer + disk_size: 4 GB + mem_page_size: normal + mem_size: 1024 MB + num_cpus: 4 + numa_extension: + properties: + mem_policy: STRICT + node: + - id: 0 + mem_size: 512 MB + vcpus: + - 0 + - 1 + - id: 1 + mem_size: 512 MB + vcpus: + - 2 + - 3 + node_cnt: 2 + vswitch_epa: + properties: + ovs_acceleration: DISABLED + ovs_offload: DISABLED + artifacts: + ping_vnfd_iovdu_0_vm_image: + file: ../images/Fedora-x86_64-20-20131211.1-sda-ping.qcow2 + image_checksum: a6ffaa77f949a9e4ebb082c6147187cf + type: tosca.artifacts.Deployment.Image.riftio.QCOW2 + interfaces: + Standard: + create: ping_vnfd_iovdu_0_vm_image + ping_vnfd_cp0: + type: tosca.nodes.nfv.riftio.CP1 + properties: + cp_type: VPORT + name: ping_vnfd/cp0 + vdu_intf_name: eth0 + vdu_intf_type: VIRTIO + requirements: + - virtualBinding: + node: ping_vnfd_iovdu_0 diff --git a/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/pong_vnfd.yaml b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/pong_vnfd.yaml new file mode 100644 index 00000000..aeb160c9 --- /dev/null +++ b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/pong_vnfd.yaml @@ -0,0 +1,127 @@ +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0 +description: This is an example RIFT.ware VNF +metadata: + ID: pong_vnfd + vendor: RIFT.io + version: 1.0 +imports: +- riftiotypes.yaml +node_types: + tosca.nodes.nfv.riftio.pongvnfdVNF: + derived_from: tosca.nodes.nfv.riftio.VNF1 + requirements: + - virtualLink1: + type: tosca.nodes.nfv.VL +topology_template: + policies: + - configuration: + properties: + config: + config_delay: 60 + config_details: + script_type: bash + config_priority: 1 + config_template: "\n#!/bin/bash\n\n# Rest API configuration\npong_mgmt_ip=\n\ + pong_mgmt_port=18889\n# username=\n# password=\n\ + \n# VNF specific configuration\npong_server_ip=\nserver_port=5555\n\n# Make Rest API calls to configure\ + \ VNF\ncurl -D /dev/null \\\n -H \"Accept: application/vnd.yang.data+xml\"\ + \ \\\n -H \"Content-Type: application/vnd.yang.data+json\" \\\n \ + \ -X POST \\\n -d \"{\\\"ip\\\":\\\"$pong_server_ip\\\", \\\"port\\\ + \":$server_port}\" \\\n http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/server\n\ + rc=$?\nif [ $rc -ne 0 ]\nthen\n echo \"Failed to set server(own) info\ + \ for pong!\"\n exit $rc\nfi\n\nexit 0\n" + config_type: script + targets: [pong_vnfd_iovdu_0] + type: tosca.policies.nfv.riftio.vnf_configuration + substitution_mappings: + node_type: tosca.nodes.nfv.riftio.pongvnfdVNF + requirements: + - virtualLink1: [pong_vnfd_cp0, virtualLink] + node_templates: + pong_vnfd_iovdu_0: + type: tosca.nodes.nfv.riftio.VDU1 + properties: + cloud_init: "#cloud-config\npassword: fedora\nchpasswd: { expire: False }\n\ + ssh_pwauth: True\nruncmd:\n - [ systemctl, daemon-reload ]\n - [ systemctl,\ + \ enable, pong.service ]\n - [ systemctl, start, --no-block, pong.service\ + \ ]\n - [ ifup, eth1 ]\n" + count: 1 + capabilities: + hypervisor_epa: + properties: + type: PREFER_KVM + version: 1 + mgmt_interface: + properties: + dashboard_params: + path: api/v1/pong/stats + port: 18889 + port: 18889 + protocol: tcp + monitoring_param: + properties: + description: no of ping requests + json_query_method: namekey + name: ping-request-rx-count + ui_data: + group_tag: Group1 + units: packets + widget_type: counter + url_path: api/v1/pong/stats + monitoring_param_1: + properties: + description: no of ping responses + json_query_method: namekey + name: ping-response-tx-count + ui_data: + group_tag: Group1 + units: packets + widget_type: counter + url_path: api/v1/pong/stats + nfv_compute: + properties: + cpu_allocation: + cpu_affinity: dedicated + thread_allocation: prefer + disk_size: 4 GB + mem_page_size: normal + mem_size: 1024 MB + num_cpus: 4 + numa_extension: + properties: + mem_policy: STRICT + node: + - id: 0 + mem_size: 512 MB + vcpus: + - 0 + - 1 + - id: 1 + mem_size: 512 MB + vcpus: + - 2 + - 3 + node_cnt: 2 + vswitch_epa: + properties: + ovs_acceleration: DISABLED + ovs_offload: DISABLED + artifacts: + pong_vnfd_iovdu_0_vm_image: + file: ../images/Fedora-x86_64-20-20131211.1-sda-pong.qcow2 + image_checksum: 977484d95575f80ef8399c9cf1d45ebd + type: tosca.artifacts.Deployment.Image.riftio.QCOW2 + interfaces: + Standard: + create: pong_vnfd_iovdu_0_vm_image + pong_vnfd_cp0: + type: tosca.nodes.nfv.riftio.CP1 + properties: + cp_type: VPORT + name: pong_vnfd/cp0 + vdu_intf_name: eth0 + vdu_intf_type: VIRTIO + requirements: + - virtualBinding: + node: pong_vnfd_iovdu_0 diff --git a/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/riftiotypes.yaml b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/riftiotypes.yaml new file mode 100644 index 00000000..18a07286 --- /dev/null +++ b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/Definitions/riftiotypes.yaml @@ -0,0 +1,1493 @@ +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0 +description: Extended types + + +data_types: + tosca.datatypes.nfv.riftio.dashboard_params: + properties: + path: + type: string + description: >- + The HTTP path for the dashboard + port: + type: tosca.datatypes.network.PortDef + description: >- + The HTTP port for the dashboard + default: 80 + https: + type: boolean + description: >- + Pick HTTPS instead of HTTP , Default is false + default: false + required: false + tosca.datatypes.nfv.riftio.monitoring_param_ui: + properties: + description: + type: string + required: false + group_tag: + type: string + description: >- + A simple tag to group monitoring parameters + required: false + widget_type: + type: string + description: >- + Type of the widget + default: counter + constraints: + - valid_values: + - histogram + - bar + - gauge + - slider + - counter + - textbox + units: + type: string + required: false + tosca.datatypes.nfv.riftio.monitoring_param_value: + properties: + value_type: + type: string + default: integer + constraints: + - valid_values: + - integer + - float + - string + numeric_min: + type: integer + description: >- + Minimum value for the parameter + required: false + numeric_max: + type: integer + description: >- + Maxium value for the parameter + required: false + string_min: + type: integer + description: >- + Minimum string length for the parameter + required: false + constraints: + - greater_or_equal: 0 + string_max: + type: integer + description: >- + Maximum string length for the parameter + required: false + constraints: + - greater_or_equal: 0 + tosca.datatypes.compute.Container.Architecture.CPUAllocation: + derived_from: tosca.datatypes.Root + properties: + cpu_affinity: + type: string + required: false + constraints: + - valid_values: [shared, dedicated, any] + thread_allocation: + type: string + required: false + constraints: + - valid_values: [avoid, separate, isolate, prefer] + socket_count: + type: integer + required: false + core_count: + type: integer + required: false + thread_count: + type: integer + required: false + + tosca.datatypes.compute.Container.Architecture.NUMA: + derived_from: tosca.datatypes.Root + properties: + id: + type: integer + constraints: + - greater_or_equal: 0 + vcpus: + type: list + entry_schema: + type: integer + constraints: + - greater_or_equal: 0 + mem_size: + type: scalar-unit.size + constraints: + - greater_or_equal: 0 MB + tosca.datatypes.nfv.riftio.paired_thread_map: + properties: + thread_a: + type: integer + required: true + constraints: + - greater_or_equal: 0 + thread_b: + type: integer + required: true + constraints: + - greater_or_equal: 0 + + tosca.datatypes.nfv.riftio.paired_threads: + properties: + num_paired_threads: + type: integer + constraints: + - greater_or_equal: 1 + paired_thread_ids: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.paired_thread_map + constraints: + - max_length: 16 + required: false + + tosca.datatypes.compute.riftio.numa: + properties: + id: + type: integer + constraints: + - greater_or_equal: 0 + vcpus: + type: list + entry_schema: + type: integer + constraints: + - greater_or_equal: 0 + required: false + mem_size: + type: scalar-unit.size + constraints: + - greater_or_equal: 0 MB + required: false + om_numa_type: + type: string + description: Openmano Numa type selection + constraints: + - valid_values: [cores, paired-threads, threads] + required: false + num_cores: + type: integer + description: Use when om_numa_type is cores + constraints: + - greater_or_equal: 1 + required: false + paired_threads: + type: tosca.datatypes.nfv.riftio.paired_threads + description: Use when om_numa_type is paired-threads + required: false + num_threads: + type: integer + description: Use when om_numa_type is threads + constraints: + - greater_or_equal: 1 + required: false + + tosca.nfv.datatypes.pathType: + properties: + forwarder: + type: string + required: true + capability: + type: string + required: true + + tosca.nfv.datatypes.aclType: + properties: + eth_type: + type: string + required: false + eth_src: + type: string + required: false + eth_dst: + type: string + required: false + vlan_id: + type: integer + constraints: + - in_range: [ 1, 4094 ] + required: false + vlan_pcp: + type: integer + constraints: + - in_range: [ 0, 7 ] + required: false + mpls_label: + type: integer + constraints: + - in_range: [ 16, 1048575] + required: false + mpls_tc: + type: integer + constraints: + - in_range: [ 0, 7 ] + required: false + ip_dscp: + type: integer + constraints: + - in_range: [ 0, 63 ] + required: false + ip_ecn: + type: integer + constraints: + - in_range: [ 0, 3 ] + required: false + ip_src_prefix: + type: string + required: false + ip_dst_prefix: + type: string + required: false + ip_proto: + type: integer + constraints: + - in_range: [ 1, 254 ] + required: false + destination_port_range: + type: integer + required: false + source_port_range: + type: integer + required: false + network_src_port_id: + type: string + required: false + network_dst_port_id: + type: string + required: false + network_id: + type: string + required: false + network_name: + type: string + required: false + tenant_id: + type: string + required: false + icmpv4_type: + type: integer + constraints: + - in_range: [ 0, 254 ] + required: false + icmpv4_code: + type: integer + constraints: + - in_range: [ 0, 15 ] + required: false + arp_op: + type: integer + constraints: + - in_range: [ 1, 25 ] + required: false + arp_spa: + type: string + required: false + arp_tpa: + type: string + required: false + arp_sha: + type: string + required: false + arp_tha: + type: string + required: false + ipv6_src: + type: string + required: false + ipv6_dst: + type: string + required: false + ipv6_flabel: + type: integer + constraints: + - in_range: [ 0, 1048575] + required: false + icmpv6_type: + type: integer + constraints: + - in_range: [ 0, 255] + required: false + icmpv6_code: + type: integer + constraints: + - in_range: [ 0, 7] + required: false + ipv6_nd_target: + type: string + required: false + ipv6_nd_sll: + type: string + required: false + ipv6_nd_tll: + type: string + required: false + + + tosca.datatypes.nfv.riftio.vnf_configuration: + properties: + config_type: + type: string + description: >- + Type of the configuration agent to use + constraints: + - valid_values: [script, netconf, rest, juju] + config_details: + type: map + description: >- + Specify the details for the config agent, like + script type, juju charm to use, etc. + config_template: + required: false + type: string + config_delay: + type: integer + constraints: + - greater_or_equal: 0 + default: 0 + required: false + config_priority: + type: integer + constraints: + - greater_than: 0 + + tosca.datatypes.nfv.riftio.parameter_value: + properties: + name: + type: string + description: Name of the parameter + value: + type: string + description: Value of the parameter + + tosca.datatypes.nfv.riftio.config_primitive: + properties: + name: + type: string + seq: + type: integer + description: >- + Order in which to apply, when multiple ones are defined + default: 0 + constraints: + - greater_or_equal: 0 + parameter: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.parameter_value + user_defined_script: + type: string + tosca.datatypes.nfv.riftio.primitive_parameter: + properties: + data_type: + type: string + description: >- + Data type associated with the name + constraints: + - valid_values: [string, integer, boolean] + mandatory: + type: boolean + description: >- + If this field is mandatory + default: false + required: false + default_value: + type: string + description: >- + The default value for this field + required: false + parameter_pool: + type: string + description: >- + Parameter pool name to use for this parameter + required: false + read_only: + type: boolean + description: >- + The value should be greyed out by the UI. + Only applies to parameters with default values. + required: false + default: false + hidden: + type: boolean + description: >- + The field should be hidden by the UI. + Only applies to parameters with default values. + required: false + default: false + tosca.datatypes.nfv.riftio.primitive_parameter_group: + properties: + name: + type: string + description: >- + Name of the parameter group + mandatory: + type: boolean + description: >- + If this group is mandatory + default: false + required: false + parameter: + type: map + description: >- + List of parameters for the service primitive + entry_schema: osca.datatypes.riftio.primitive_parameter + + tosca.datatypes.nfv.riftio.vnf_primitive_group: + properties: + vnf_name: + type: string + description: >- + Name of the VNF in the NS + primitive: + type: map + entry_schema: + type: string + description: >- + Index and name of the primitive + + +capability_types: + tosca.capabilities.nfv.riftio.mgmt_interface: + derived_from: tosca.capabilities.Endpoint + properties: + static_ip: + type: string + required: false + description: >- + Specifies the static IP address for managing the VNF + connection_point: + type: string + required: false + description: >- + Use the ip address associated with this connection point + dashboard_params: + type: tosca.datatypes.nfv.riftio.dashboard_params + required: false + description: >- + Parameters for the VNF dashboard + tosca.capabilities.nfv.riftio.monitoring_param: + derived_from: tosca.capabilities.nfv.Metric + properties: + name: + type: string + required: false + description: + type: string + required: false + protocol: + type: string + default: http + constraints: + - equal: http + polling_interval: + type: scalar-unit.time + description: >- + The HTTP polling interval in seconds + default: 2 s + username: + type: string + description: >- + The HTTP basic auth username + required: false + password: + type: string + description: >- + The HTTP basic auth password + required: false + method: + type: string + description: >- + This is the method to be performed at the uri. + GET by default for action + default: get + constraints: + - valid_values: [post, put, get, delete, options, patch] + headers: + type: map + entry_schema: + type: string + description: >- + Custom HTTP headers to put on HTTP request + required: false + json_query_method: + type: string + description: >- + The method to extract a value from a JSON response + namekey - Use the name as the key for a non-nested value. + jsonpath - Use jsonpath-rw implemenation to extract a value. + objectpath - Use objectpath implemenation to extract a value. + constraints: + - valid_values: [namekey, jsonpath, objectpath] + default: namekey + json_query_path: + type: string + description: >- + The json path to use to extract value from JSON structure + required: false + json_object_path: + type: string + description: >- + The object path to use to extract value from JSON structure + required: false + ui_data: + type: tosca.datatypes.nfv.riftio.monitoring_param_ui + required: false + constraints: + type: tosca.datatypes.nfv.riftio.monitoring_param_value + required: false + tosca.capabilities.nfv.riftio.numa_extension: + derived_from: tosca.capabilities.Root + properties: + node_cnt: + type: integer + description: >- + The number of numa nodes to expose to the VM + constraints: + - greater_or_equal: 0 + mem_policy: + type: string + description: >- + This policy specifies how the memory should + be allocated in a multi-node scenario. + STRICT - The memory must be allocated + strictly from the memory attached + to the NUMA node. + PREFERRED - The memory should be allocated + preferentially from the memory + attached to the NUMA node + constraints: + - valid_values: [strict, preferred, STRICT, PREFERRED] + node: + type: list + entry_schema: + type: tosca.datatypes.compute.riftio.numa + tosca.capabilities.nfv.riftio.vswitch_epa: + derived_from: tosca.capabilities.Root + properties: + ovs_acceleration: + type: string + description: |- + Specifies Open vSwitch acceleration mode. + MANDATORY - OVS acceleration is required + PREFERRED - OVS acceleration is preferred + constraints: + - valid_values: [mandatory, preferred, disabled, MANDATORY, PREFERRED, DISABLED] + ovs_offload: + type: string + description: |- + Specifies Open vSwitch hardware offload mode. + MANDATORY - OVS offload is required + PREFERRED - OVS offload is preferred + constraints: + - valid_values: [mandatory, preferred, disabled, MANDATORY, PREFERRED, DISABLED] + + tosca.capabilities.nfv.riftio.hypervisor_epa: + derived_from: tosca.capabilities.Root + properties: + type: + type: string + description: |- + Specifies the type of hypervisor. + constraints: + - valid_values: [prefer_kvm, require_kvm, PREFER_KVM, REQUIRE_KVM] + version: + type: string + + tosca.capabilities.nfv.riftio.host_epa: + derived_from: tosca.capabilities.Root + properties: + cpu_model: + type: string + description: >- + Host CPU model. Examples include SandyBridge, + IvyBridge, etc. + required: false + constraints: + - valid_values: + - prefer_westmere + - require_westmere + - prefer_sandbridge + - require_sandybridge + - prefer_ivybridge + - require_ivybridge + - prefer_haswell + - require_haswell + - prefer_broadwell + - require_broadwell + - prefer_nehalem + - require_nehalem + - prefer_penryn + - require_penryn + - prefer_conroe + - require_conroe + - prefer_core2duo + - require_core2duo + - PREFER_WESTMERE + - REQUIRE_WESTMERE + - PREFER_SANDBRIDGE + - REQUIRE_SANDYBRIDGE + - PREFER_IVYBRIDGE + - REQUIRE_IVYBRIDGE + - PREFER_HASWELL + - REQUIRE_HASWELL + - PREFER_BROADWELL + - REQUIRE_BROADWELL + - PREFER_NEHALEM + - REQUIRE_NEHALEM + - PREFER_PENRYN + - REQUIRE_PENRYN + - PREFER_CONROE + - REQUIRE_CONROE + - PREFER_CORE2DUO + - REQUIRE_CORE2DUO + cpu_arch: + type: string + description: >- + Host CPU architecture + required: false + constraints: + - valid_values: + - prefer_x86 + - require_x86 + - prefer_x86_64 + - require_x86_64 + - prefer_i686 + - require_i686 + - prefer_ia64 + - require_ia64 + - prefer_armv7 + - require_armv7 + - prefer_armv8 + - require_armv8 + - PREFER_X86 + - REQUIRE_X86 + - PREFER_X86_64 + - REQUIRE_X86_64 + - PREFER_I686 + - REQUIRE_I686 + - PREFER_IA64 + - REQUIRE_IA64 + - PREFER_ARMV7 + - REQUIRE_ARMV7 + - PREFER_ARMV8 + - REQUIRE_ARMV8 + cpu_vendor: + type: string + description: >- + Host CPU vendor + required: false + constraints: + - valid_values: + - prefer_intel + - require_intel + - prefer_amd + - requie_amd + - PREFER_INTEL + - REQUIRE_INTEL + - PREFER_AMD + - REQUIE_AMD + cpu_socket_count: + type: integer + description: >- + Number of sockets on the host + required: false + constraints: + - greater_than : 0 + cpu_core_count: + type: integer + description: >- + Number of cores on the host + required: false + constraints: + - greater_than : 0 + cpu_core_thread_count: + type: integer + description: >- + Number of threads per core on the host + required: false + constraints: + - greater_than : 0 + cpu_feature: + type: list + entry_schema: + type: string + description: |- + Enumeration for CPU features. + + AES- CPU supports advanced instruction set for + AES (Advanced Encryption Standard). + + CAT- Cache Allocation Technology (CAT) allows + an Operating System, Hypervisor, or similar + system management agent to specify the amount + of L3 cache (currently the last-level cache + in most server and client platforms) space an + application can fill (as a hint to hardware + functionality, certain features such as power + management may override CAT settings). + + CMT- Cache Monitoring Technology (CMT) allows + an Operating System, Hypervisor, or similar + system management agent to determine the + usage of cache based on applications running + on the platform. The implementation is + directed at L3 cache monitoring (currently + the last-level cache in most server and + client platforms). + + DDIO- Intel Data Direct I/O (DDIO) enables + Ethernet server NICs and controllers talk + directly to the processor cache without a + detour via system memory. This enumeration + specifies if the VM requires a DDIO + capable host. + required: false + constraints: + -valid_values: + - prefer_aes + - require_aes + - prefer_cat + - require_cat + - prefer_cmt + - require_cmt + - prefer_ddio + - require_ddio + - prefer_vme + - require_vme + - prefer_de + - require_de + - prefer_pse + - require_pse + - prefer_tsc + - require_tsc + - prefer_msr + - require_msr + - prefer_pae + - require_pae + - prefer_mce + - require_mce + - prefer_cx8 + - require_cx8 + - prefer_apic + - require_apic + - prefer_sep + - require_sep + - prefer_mtrr + - require_mtrr + - prefer_pge + - require_pge + - prefer_mca + - require_mca + - prefer_cmov + - require_cmov + - prefer_pat + - require_pat + - prefer_pse36 + - require_pse36 + - prefer_clflush + - require_clflush + - prefer_dts + - require_dts + - prefer_acpi + - require_acpi + - prefer_mmx + - require_mmx + - prefer_fxsr + - require_fxsr + - prefer_sse + - require_sse + - prefer_sse2 + - require_sse2 + - prefer_ss + - require_ss + - prefer_ht + - require_ht + - prefer_tm + - require_tm + - prefer_ia64 + - require_ia64 + - prefer_pbe + - require_pbe + - prefer_rdtscp + - require_rdtscp + - prefer_pni + - require_pni + - prefer_pclmulqdq + - require_pclmulqdq + - prefer_dtes64 + - require_dtes64 + - prefer_monitor + - require_monitor + - prefer_ds_cpl + - require_ds_cpl + - prefer_vmx + - require_vmx + - prefer_smx + - require_smx + - prefer_est + - require_est + - prefer_tm2 + - require_tm2 + - prefer_ssse3 + - require_ssse3 + - prefer_cid + - require_cid + - prefer_fma + - require_fma + - prefer_cx16 + - require_cx16 + - prefer_xtpr + - require_xtpr + - prefer_pdcm + - require_pdcm + - prefer_pcid + - require_pcid + - prefer_dca + - require_dca + - prefer_sse4_1 + - require_sse4_1 + - prefer_sse4_2 + - require_sse4_2 + - prefer_x2apic + - require_x2apic + - prefer_movbe + - require_movbe + - prefer_popcnt + - require_popcnt + - prefer_tsc_deadline_timer + - require_tsc_deadline_timer + - prefer_xsave + - require_xsave + - prefer_avx + - require_avx + - prefer_f16c + - require_f16c + - prefer_rdrand + - require_rdrand + - prefer_fsgsbase + - require_fsgsbase + - prefer_bmi1 + - require_bmi1 + - prefer_hle + - require_hle + - prefer_avx2 + - require_avx2 + - prefer_smep + - require_smep + - prefer_bmi2 + - require_bmi2 + - prefer_erms + - require_erms + - prefer_invpcid + - require_invpcid + - prefer_rtm + - require_rtm + - prefer_mpx + - require_mpx + - prefer_rdseed + - require_rdseed + - prefer_adx + - require_adx + - prefer_smap + - require_smap + - PREFER_AES + - REQUIRE_AES + - PREFER_CAT + - REQUIRE_CAT + - PREFER_CMT + - REQUIRE_CMT + - PREFER_DDIO + - REQUIRE_DDIO + - PREFER_VME + - REQUIRE_VME + - PREFER_DE + - REQUIRE_DE + - PREFER_PSE + - REQUIRE_PSE + - PREFER_TSC + - REQUIRE_TSC + - PREFER_MSR + - REQUIRE_MSR + - PREFER_PAE + - REQUIRE_PAE + - PREFER_MCE + - REQUIRE_MCE + - PREFER_CX8 + - REQUIRE_CX8 + - PREFER_APIC + - REQUIRE_APIC + - PREFER_SEP + - REQUIRE_SEP + - PREFER_MTRR + - REQUIRE_MTRR + - PREFER_PGE + - REQUIRE_PGE + - PREFER_MCA + - REQUIRE_MCA + - PREFER_CMOV + - REQUIRE_CMOV + - PREFER_PAT + - REQUIRE_PAT + - PREFER_PSE36 + - REQUIRE_PSE36 + - PREFER_CLFLUSH + - REQUIRE_CLFLUSH + - PREFER_DTS + - REQUIRE_DTS + - PREFER_ACPI + - REQUIRE_ACPI + - PREFER_MMX + - REQUIRE_MMX + - PREFER_FXSR + - REQUIRE_FXSR + - PREFER_SSE + - REQUIRE_SSE + - PREFER_SSE2 + - REQUIRE_SSE2 + - PREFER_SS + - REQUIRE_SS + - PREFER_HT + - REQUIRE_HT + - PREFER_TM + - REQUIRE_TM + - PREFER_IA64 + - REQUIRE_IA64 + - PREFER_PBE + - REQUIRE_PBE + - PREFER_RDTSCP + - REQUIRE_RDTSCP + - PREFER_PNI + - REQUIRE_PNI + - PREFER_PCLMULQDQ + - REQUIRE_PCLMULQDQ + - PREFER_DTES64 + - REQUIRE_DTES64 + - PREFER_MONITOR + - REQUIRE_MONITOR + - PREFER_DS_CPL + - REQUIRE_DS_CPL + - PREFER_VMX + - REQUIRE_VMX + - PREFER_SMX + - REQUIRE_SMX + - PREFER_EST + - REQUIRE_EST + - PREFER_TM2 + - REQUIRE_TM2 + - PREFER_SSSE3 + - REQUIRE_SSSE3 + - PREFER_CID + - REQUIRE_CID + - PREFER_FMA + - REQUIRE_FMA + - PREFER_CX16 + - REQUIRE_CX16 + - PREFER_XTPR + - REQUIRE_XTPR + - PREFER_PDCM + - REQUIRE_PDCM + - PREFER_PCID + - REQUIRE_PCID + - PREFER_DCA + - REQUIRE_DCA + - PREFER_SSE4_1 + - REQUIRE_SSE4_1 + - PREFER_SSE4_2 + - REQUIRE_SSE4_2 + - PREFER_X2APIC + - REQUIRE_X2APIC + - PREFER_MOVBE + - REQUIRE_MOVBE + - PREFER_POPCNT + - REQUIRE_POPCNT + - PREFER_TSC_DEADLINE_TIMER + - REQUIRE_TSC_DEADLINE_TIMER + - PREFER_XSAVE + - REQUIRE_XSAVE + - PREFER_AVX + - REQUIRE_AVX + - PREFER_F16C + - REQUIRE_F16C + - PREFER_RDRAND + - REQUIRE_RDRAND + - PREFER_FSGSBASE + - REQUIRE_FSGSBASE + - PREFER_BMI1 + - REQUIRE_BMI1 + - PREFER_HLE + - REQUIRE_HLE + - PREFER_AVX2 + - REQUIRE_AVX2 + - PREFER_SMEP + - REQUIRE_SMEP + - PREFER_BMI2 + - REQUIRE_BMI2 + - PREFER_ERMS + - REQUIRE_ERMS + - PREFER_INVPCID + - REQUIRE_INVPCID + - PREFER_RTM + - REQUIRE_RTM + - PREFER_MPX + - REQUIRE_MPX + - PREFER_RDSEED + - REQUIRE_RDSEED + - PREFER_ADX + - REQUIRE_ADX + - PREFER_SMAP + - REQUIRE_SMAP + om_cpu_model_string: + type: string + description: >- + Openmano CPU model string + required: false + om_cpu_feature: + type: list + entry_schema: + type: string + description: >- + List of openmano CPU features + required: false + + tosca.capabilities.nfv.riftio.sfc: + derived_from: tosca.capabilities.Root + description: >- + Service Function Chaining support on this VDU + properties: + sfc_type: + type: string + description: >- + Type of node in Service Function Chaining Architecture + constraints: + - valid_values: [unaware, classifier, sf, sff, UNAWARE, CLASSIFIER, SF, SFF] + default: unaware + sf_type: + type: string + description: >- + Type of Service Function. + NOTE- This needs to map with Service Function Type in ODL to + support VNFFG. Service Function Type is manadatory param in ODL + SFC. + required: false + tosca.capabilities.Compute.Container.Architecture: + derived_from: tosca.capabilities.Container + properties: + mem_page_size: + type: string + description: >- + Memory page allocation size. If a VM requires + hugepages, it should choose huge or size_2MB + or size_1GB. If the VM prefers hugepages, it + should chose prefer_huge. + huge/large - Require hugepages (either 2MB or 1GB) + normal - Does not require hugepages + size_2MB - Requires 2MB hugepages + size_1GB - Requires 1GB hugepages + prefer_huge - Application perfers hugepages + NOTE - huge and normal is only defined in standards as of + now. + required: false + constraints: + - valid_values: [normal, large, huge, size_2MB, size_1GB, prefer_huge, NORMAL,LARGE, HUGE, SIZE_2MB, SIZE_1GB, PREFER_HUGE] + cpu_allocation: + type: tosca.datatypes.compute.Container.Architecture.CPUAllocation + required: false + numa_nodes: + type: map + required: false + entry_schema: + type: tosca.datatypes.compute.Container.Architecture.NUMA + + +node_types: + tosca.nodes.nfv.riftio.VDU1: + derived_from: tosca.nodes.nfv.VDU + properties: + description: + type: string + required: false + image: + description: >- + If an image is specified here, it is assumed that the image + is already present in the RO or VIM and not in the package. + type: string + required: false + image_checksum: + type: string + description: >- + Image checksum for the image in RO or VIM. + required: false + cloud_init: + description: >- + Inline cloud-init specification + required: false + type: string + count: + default: 1 + type: integer + capabilities: + virtualLink: + type: tosca.capabilities.nfv.VirtualLinkable + monitoring_param_1: + type: tosca.capabilities.nfv.riftio.monitoring_param + mgmt_interface: + type: tosca.capabilities.nfv.riftio.mgmt_interface + monitoring_param: + type: tosca.capabilities.nfv.riftio.monitoring_param + numa_extension: + type: tosca.capabilities.nfv.riftio.numa_extension + vswitch_epa: + type: tosca.capabilities.nfv.riftio.vswitch_epa + hypervisor_epa: + type: tosca.capabilities.nfv.riftio.hypervisor_epa + host_epa: + type: tosca.capabilities.nfv.riftio.host_epa + tosca.nodes.nfv.riftio.CP1: + derived_from: tosca.nodes.nfv.CP + properties: + cp_type: + description: Type of the connection point + type: string + default: VPORT + constraints: + - valid_values: [VPORT] + name: + description: Name of the connection point + type: string + required: false + vdu_intf_name: + description: Name of the interface on VDU + type: string + vdu_intf_type: + description: >- + Specifies the type of virtual interface + between VM and host. + VIRTIO - Use the traditional VIRTIO interface. + PCI-PASSTHROUGH - Use PCI-PASSTHROUGH interface. + SR-IOV - Use SR-IOV interface. + E1000 - Emulate E1000 interface. + RTL8139 - Emulate RTL8139 interface. + PCNET - Emulate PCNET interface. + OM-MGMT - Used to specify openmano mgmt external-connection type + type: string + constraints: + - valid_values: [OM-MGMT, VIRTIO, E1000, SR-IOV] + bandwidth: + type: integer + description: Aggregate bandwidth of the NIC + constraints: + - greater_or_equal: 0 + required: false + vpci: + type: string + description: >- + Specifies the virtual PCI address. Expressed in + the following format dddd:dd:dd.d. For example + 0000:00:12.0. This information can be used to + pass as metadata during the VM creation. + required: false + capabilities: + sfc: + type: tosca.capabilities.nfv.riftio.sfc + tosca.nodes.nfv.riftio.VNF1: + derived_from: tosca.nodes.nfv.VNF + properties: + member_index: + type: integer + constraints: + - greater_or_equal: 1 + description: Index of the VNF in the NS + required: false + start_by_default: + type: boolean + default: true + description: Start this VNF on NS instantiate + logo: + type: string + description: >- + Logo to display with the VNF in the orchestrator + required: false + capabilities: + mgmt_interface: + type: tosca.capabilities.nfv.riftio.mgmt_interface + monitoring_param: + type: tosca.capabilities.nfv.riftio.monitoring_param + sfc: + type: tosca.capabilities.nfv.riftio.sfc + tosca.nodes.nfv.riftio.ELAN: + derived_from: tosca.nodes.nfv.VL.ELAN + properties: + description: + type: string + required: false + network_name: + type: string + description: >- + Name of network in VIM account. This is used to indicate + pre-provisioned network name in cloud account. + required: false + root_bandwidth: + type: integer + description: >- + This is the aggregate bandwidth + constraints: + - greater_or_equal: 0 + required: false + leaf_bandwidth: + type: integer + description: >- + This is the bandwidth of branches + constraints: + - greater_or_equal: 0 + required: false + tosca.nodes.nfv.riftio.FP1: + derived_from: tosca.nodes.nfv.FP + properties: + id: + type: integer + required: false + policy: + type: tosca.nfv.datatypes.policyType + required: true + description: policy to use to match traffic for this FP + path: + type: list + required: true + entry_schema: + type: tosca.nfv.datatypes.pathType + cp: + type: tosca.nfv.datatypes.pathType + required: true + + + +artifact_types: + tosca.artifacts.Deployment.riftio.cloud_init_file: + derived_from: tosca.artifacts.Deployment + file: + type: string + + tosca.artifacts.Deployment.Image.riftio.QCOW2: + derived_from: tosca.artifacts.Deployment.Image.VM.QCOW2 + image_checksum: + required: false + type: string + +group_types: + tosca.groups.nfv.VNFFG: + derived_from: tosca.groups.Root + properties: + vendor: + type: string + required: true + description: name of the vendor who generate this VNFFG + version: + type: string + required: true + description: version of this VNFFG + number_of_endpoints: + type: integer + required: true + description: count of the external endpoints included in this VNFFG + dependent_virtual_link: + type: list + entry_schema: + type: string + required: true + description: Reference to a VLD used in this Forwarding Graph + connection_point: + type: list + entry_schema: + type: string + required: true + description: Reference to Connection Points forming the VNFFG + constituent_vnfs: + type: list + entry_schema: + type: string + required: true + description: Reference to a list of VNFD used in this VNF Forwarding Graph + members: [ tosca.nodes.nfv.FP ] + + tosca.groups.nfv.riftio.scaling: + derived_from: tosca.groups.Root + properties: + name: + type: string + min_instances: + type: integer + description: >- + Minimum instances of the scaling group which are allowed. + These instances are created by default when the network service + is instantiated. + max_instances: + type: integer + description: >- + Maximum instances of this scaling group that are allowed + in a single network service. The network service scaling + will fail, when the number of service group instances + exceed the max-instance-count specified. + cooldown_time: + type: integer + description: >- + The duration after a scaling-in/scaling-out action has been + triggered, for which there will be no further optional + ratio: + type: map + entry_schema: + type: integer + description: >- + Specify the number of instances of each VNF to instantiate + for a scaling action + members: [tosca.nodes.nfv.VNF] + interfaces: + action: + type: tosca.interfaces.nfv.riftio.scaling.action + +interface_types: + tosca.interfaces.nfv.riftio.scaling.action: + pre_scale_in: + description: Operation to execute before a scale in + post_scale_in: + description: Operation to execute after a scale in + pre_scale_out: + description: Operation to execute before a scale out + post_scale_out: + description: Operation to execute after a scale out + +policy_types: + tosca.policies.nfv.riftio.placement: + derived_from: tosca.policies.Placement + properties: + name: + type: string + description: >- + Place group construct to define the compute resource placement strategy + in cloud environment + requirement: + type: string + description: >- + This is free text space used to describe the intent/rationale + behind this placement group. This is for human consumption only + strategy: + type: string + description: >- + Strategy associated with this placement group + Following values are possible + COLOCATION - Colocation strategy imply intent to share the physical + infrastructure (hypervisor/network) among all members + of this group. + ISOLATION - Isolation strategy imply intent to not share the physical + infrastructure (hypervisor/network) among the members + of this group. + constraints: + valid_values: + - COLOCATION + - ISOLATION + tosca.policies.nfv.riftio.vnf_configuration: + derived_from: tosca.policies.Root + properties: + config: + type: tosca.datatypes.nfv.riftio.vnf_configuration + initial_config: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.config_primitive + tosca.policies.nfv.riftio.vnf_service_primitives: + derived_from: tosca.policies.Root + properties: + parameter: + type: map + entry_schema: + type: primitive_parameter + tosca.policies.nfv.riftio.ns_service_primitives: + derived_from: tosca.policies.Root + properties: + parameter: + type: map + entry_schema: + type: primitive_parameter + parameter_group: + type: tosca.datatypes.nfv.riftio.primitive_parameter_group + description: >- + Grouping of parameters which are logically grouped in UI + required: false + vnf_primitive_group: + type: tosca.datatypes.nfv.riftio.vnf_primitive_group + description: >- + List of service primitives grouped by VNF + required: false + user_defined_script: + type: string + description: >- + A user defined script + required: false + tosca.policies.nfv.riftio.initial_config_primitive: + derived_from: tosca.policies.Root + properties: + name: + type: string + seq: + type: integer + description: >- + Order in which to apply, when multiple ones are defined + default: 0 + constraints: + - greater_or_equal: 0 + parameter: + type: map + entry_schema: + type: string + user_defined_script: + type: string + tosca.policies.nfv.riftio.users: + derived_from: tosca.policies.Root + description: >- + Specify list of public keys to be injected as + part of NS instantitation. Use default as entry, + to specify the key pairs for default user. + properties: + user_info: + type: string + description: >- + The user\'s real name + required: false + key_pairs: + type: map + description: >- + List of public keys for the user + entry_schema: + type: string + required: true + tosca.policies.nfv.riftio.dependency: + derived_from: tosca.policies.Root + description: >- + Map dependency between VDUs or VNFs + properties: + parameter: + type: map + entry_schema: + type: string + description: >- + Parameter and value for the config + tosca.nfv.datatypes.policyType: + properties: + type: + type: string + required: false + constraints: + - valid_values: [ ACL ] + criteria: + type: list + required: true + entry_schema: + type: tosca.nfv.datatypes.aclType + + + diff --git a/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/TOSCA-Metadata/TOSCA.meta b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/TOSCA-Metadata/TOSCA.meta new file mode 100644 index 00000000..2351efd0 --- /dev/null +++ b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/TOSCA-Metadata/TOSCA.meta @@ -0,0 +1,4 @@ +TOSCA-Meta-File-Version: 1.0 +CSAR-Version: 1.1 +Created-By: RIFT.io +Entry-Definitions: Definitions/ping_pong_nsd.yaml \ No newline at end of file diff --git a/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/scripts/ping_set_rate.py b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/scripts/ping_set_rate.py new file mode 100755 index 00000000..54629e85 --- /dev/null +++ b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/scripts/ping_set_rate.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# 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 argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def ping_set_rate(yaml_cfg, logger): + '''Use curl and set traffic rate on ping vnf''' + + def set_rate(mgmt_ip, port, rate): + curl_cmd = '''curl -D /dev/stdout \ + -H "Accept: application/vnd.yang.data+xml" \ + -H "Content-Type: application/vnd.yang.data+json" \ + -X POST \ + -d "{{ \\"rate\\":{ping_rate} }}" \ + http://{ping_mgmt_ip}:{ping_mgmt_port}/api/v1/ping/rate +'''.format(ping_mgmt_ip=mgmt_ip, + ping_mgmt_port=port, + ping_rate=rate) + + logger.debug("Executing cmd: %s", curl_cmd) + subprocess.check_call(curl_cmd, shell=True) + + # Get the ping rate + rate = yaml_cfg['parameter']['rate'] + + # Set ping rate + for index, vnfr in yaml_cfg['vnfr'].items(): + logger.debug("VNFR {}: {}".format(index, vnfr)) + + # Check if it is pong vnf + if 'ping_vnfd' in vnfr['name']: + vnf_type = 'ping' + port = 18888 + set_rate(vnfr['mgmt_ip_address'], port, rate) + break + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/ping_set_rate-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger() + + except Exception as e: + print("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + ch.setFormatter(formatter) + logger.addHandler(ch) + + except Exception as e: + logger.exception(e) + raise e + + try: + yaml_str = args.yaml_cfg_file.read() + # logger.debug("Input YAML file:\n{}".format(yaml_str)) + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + ping_set_rate(yaml_cfg, logger) + + except Exception as e: + logger.exception(e) + raise e + +if __name__ == "__main__": + main() diff --git a/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/scripts/start_traffic.py b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/scripts/start_traffic.py new file mode 100755 index 00000000..22de5426 --- /dev/null +++ b/common/python/rift/mano/tosca_translator/test/data/tosca_ping_pong_epa/scripts/start_traffic.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# 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 argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def start_traffic(yaml_cfg, logger): + '''Use curl and set admin status to enable on pong and ping vnfs''' + + def enable_service(mgmt_ip, port, vnf_type): + curl_cmd = 'curl -D /dev/null -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"enable\\":true}}" http://{mgmt_ip}:' \ + '{mgmt_port}/api/v1/{vnf_type}/adminstatus/state'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=port, + vnf_type=vnf_type) + + logger.debug("Executing cmd: %s", curl_cmd) + subprocess.check_call(curl_cmd, shell=True) + + # Enable pong service first + for index, vnfr in yaml_cfg['vnfr'].items(): + logger.debug("VNFR {}: {}".format(index, vnfr)) + + # Check if it is pong vnf + if 'pong_vnfd' in vnfr['name']: + vnf_type = 'pong' + port = 18889 + enable_service(vnfr['mgmt_ip_address'], port, vnf_type) + break + + # Add a delay to provide pong port to come up + time.sleep(0.1) + + # Enable ping service next + for index, vnfr in yaml_cfg['vnfr'].items(): + logger.debug("VNFR {}: {}".format(index, vnfr)) + + # Check if it is pong vnf + if 'ping_vnfd' in vnfr['name']: + vnf_type = 'ping' + port = 18888 + enable_service(vnfr['mgmt_ip_address'], port, vnf_type) + break + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/ping_pong_start_traffic-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger() + + except Exception as e: + print("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + ch.setFormatter(formatter) + logger.addHandler(ch) + + except Exception as e: + logger.exception(e) + raise e + + try: + yaml_str = args.yaml_cfg_file.read() + # logger.debug("Input YAML file:\n{}".format(yaml_str)) + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + start_traffic(yaml_cfg, logger) + + except Exception as e: + logger.exception(e) + raise e + +if __name__ == "__main__": + main() diff --git a/common/python/rift/mano/tosca_translator/test/tosca_translator_ut.py b/common/python/rift/mano/tosca_translator/test/tosca_translator_ut.py index 1b5b156b..1dc43d03 100755 --- a/common/python/rift/mano/tosca_translator/test/tosca_translator_ut.py +++ b/common/python/rift/mano/tosca_translator/test/tosca_translator_ut.py @@ -66,7 +66,7 @@ class TestToscaTranslator(unittest.TestCase): tosca_helloworld = os.path.join( os.path.dirname(os.path.abspath(__file__)), - "data/tosca_helloworld.yaml") + "data/tosca_helloworld_nfv.yaml") template_file = '--template-file=' + tosca_helloworld template_validation = "--validate-only" debug="--debug" @@ -109,6 +109,7 @@ class TestToscaTranslator(unittest.TestCase): (self.template_file, '--parameters=key')) + @unittest.skip def test_valid_template(self): try: shell.main([self.template_file]) @@ -116,6 +117,7 @@ class TestToscaTranslator(unittest.TestCase): self.log.exception(e) self.fail(self.failure_msg) + @unittest.skip def test_validate_only(self): try: shell.main([self.template_file, @@ -213,7 +215,7 @@ class TestToscaTranslator(unittest.TestCase): test_base_dir = os.path.join(os.path.dirname( os.path.abspath(__file__)), 'data') template_file = os.path.join(test_base_dir, - "ping_pong_csar/Definitions/ping_pong_nsd.yaml") + "tosca_ping_pong_epa/Definitions/ping_pong_nsd.yaml") template = '--template-file='+template_file temp_dir = tempfile.mkdtemp() output_dir = "--output-dir=" + temp_dir @@ -233,12 +235,13 @@ class TestToscaTranslator(unittest.TestCase): shutil.rmtree(temp_dir) else: self.log.warn("Generated desc in {}".format(temp_dir)) + def test_input_csar(self): test_base_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'data') - template_file = os.path.join(test_base_dir, "ping_pong_csar.zip") + template_file = os.path.join(test_base_dir, "tosca_ping_pong_epa.zip") template = '--template-file='+template_file temp_dir = tempfile.mkdtemp() output_dir = "--output-dir=" + temp_dir @@ -259,12 +262,13 @@ class TestToscaTranslator(unittest.TestCase): shutil.rmtree(temp_dir) else: self.log.warn("Generated desc in {}".format(temp_dir)) - + + @unittest.skip def test_input_csar_no_gi(self): test_base_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'data') - template_file = os.path.join(test_base_dir, "ping_pong_csar.zip") + template_file = os.path.join(test_base_dir, "tosca_ping_pong_epa.zip") template = '--template-file='+template_file temp_dir = tempfile.mkdtemp() output_dir = "--output-dir=" + temp_dir diff --git a/common/python/rift/mano/yang_translator/riftiotypes.yaml b/common/python/rift/mano/yang_translator/riftiotypes.yaml index 7e4158b2..7388d32c 100644 --- a/common/python/rift/mano/yang_translator/riftiotypes.yaml +++ b/common/python/rift/mano/yang_translator/riftiotypes.yaml @@ -1148,6 +1148,20 @@ node_types: type: tosca.capabilities.nfv.riftio.hypervisor_epa host_epa: type: tosca.capabilities.nfv.riftio.host_epa + monitoring_param_2: + type: tosca.capabilities.nfv.riftio.monitoring_param + monitoring_param_3: + type: tosca.capabilities.nfv.riftio.monitoring_param + monitoring_param_4: + type: tosca.capabilities.nfv.riftio.monitoring_param + monitoring_param_5: + type: tosca.capabilities.nfv.riftio.monitoring_param + monitoring_param_6: + type: tosca.capabilities.nfv.riftio.monitoring_param + monitoring_param_7: + type: tosca.capabilities.nfv.riftio.monitoring_param + monitoring_param_8: + type: tosca.capabilities.nfv.riftio.monitoring_param tosca.nodes.nfv.riftio.CP1: derived_from: tosca.nodes.nfv.CP properties: @@ -1428,6 +1442,11 @@ policy_types: description: >- A user defined script required: false + name: + type: string + description: >- + Name of primitive + required: false tosca.policies.nfv.riftio.initial_config_primitive: derived_from: tosca.policies.Root properties: diff --git a/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_resource.py b/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_resource.py index 57b0a31e..92041342 100644 --- a/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_resource.py +++ b/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_resource.py @@ -23,8 +23,8 @@ class ToscaResource(object): # from REQUIRED_FIELDS below NAME = 'name' - REQUIRED_FIELDS = (DESC, VERSION, VENDOR, ID) = \ - ('description', 'version', 'vendor', 'id') + REQUIRED_FIELDS = (DESC, VERSION, VENDOR, ID, LOGO) = \ + ('description', 'version', 'vendor', 'id', 'logo') COMMON_FIELDS = (PATH, PORT, HOST, XPATH, TYPE, COUNT, FILE, NFV_COMPUTE, HOST_EPA, VSWITCH_EPA, HYPERVISOR_EPA, GUEST_EPA) = \ @@ -98,6 +98,7 @@ class ToscaResource(object): T_ELAN, T_VNFFG, T_FP, + T_NS_PRIMITIVE, ) = \ ('tosca.policies.nfv.riftio.vnf_configuration', 'tosca.capabilities.riftio.http_endpoint_type', @@ -116,13 +117,14 @@ class ToscaResource(object): 'tosca.nodes.nfv.riftio.ELAN', 'tosca.groups.nfv.VNFFG', 'tosca.nodes.nfv.riftio.FP1', + 'tosca.policies.nfv.riftio.ns_service_primitives', ) SUPPORT_FILES = ( SRC, DEST, EXISTING) = \ ('source', 'destination', 'existing') - SUPPORT_DIRS = (IMAGE_DIR, SCRIPT_DIR, CLOUD_INIT_DIR) = \ - ('images', 'scripts','cloud_init') + SUPPORT_DIRS = (IMAGE_DIR, SCRIPT_DIR, CLOUD_INIT_DIR, ICON_DIR) = \ + ('images', 'scripts','cloud_init', 'icons') def __init__(self, log, diff --git a/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_template.py b/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_template.py index 95f2cb26..039648f5 100644 --- a/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_template.py +++ b/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_template.py @@ -138,7 +138,7 @@ class ToscaTemplate(object): # Add all types types_list = [ToscaResource.DATA_TYPES, ToscaResource.CAPABILITY_TYPES, - ToscaResource.NODE_TYPES, + ToscaResource.NODE_TYPES, ToscaResource.ARTIFACT_TYPES, ToscaResource.GROUP_TYPES, ToscaResource.POLICY_TYPES] for typ in types_list: if typ in tosca: diff --git a/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py b/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py index 707ab7fb..2023db5a 100644 --- a/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py +++ b/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py @@ -148,26 +148,31 @@ class TranslateDescriptors(object): for nsd in self.yangs[self.NSD]: self.log.debug(_("Translate descriptor of type nsd: {}"). format(nsd)) + node_name = nsd.pop(ToscaResource.NAME).replace(' ','_') + node_name = node_name if node_name.endswith('nsd') else ''.join([node_name, '_nsd']) tosca_node = TranslateDescriptors. \ YANG_TO_TOSCA_TYPE[self.NSD]( self.log, - nsd.pop(ToscaResource.NAME), + node_name, self.NSD, nsd, self.vnfd_files) self.tosca_resources.append(tosca_node) + vnfd_name_list = [] if self.VNFD in self.yangs: for vnfd in self.yangs[self.VNFD]: - self.log.debug(_("Translate descriptor of type vnfd: {}"). - format(vnfd)) - tosca_node = TranslateDescriptors. \ - YANG_TO_TOSCA_TYPE[self.VNFD]( - self.log, - vnfd.pop(ToscaResource.NAME), - self.VNFD, - vnfd) - self.tosca_resources.append(tosca_node) + if vnfd['name'] not in vnfd_name_list: + self.log.debug(_("Translate descriptor of type vnfd: {}"). + format(vnfd)) + vnfd_name_list.append(vnfd['name']) + tosca_node = TranslateDescriptors. \ + YANG_TO_TOSCA_TYPE[self.VNFD]( + self.log, + vnfd.pop(ToscaResource.NAME), + self.VNFD, + vnfd) + self.tosca_resources.append(tosca_node) # First translate VNFDs for node in self.tosca_resources: diff --git a/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py b/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py index d28b3e1d..94fff166 100644 --- a/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py +++ b/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py @@ -22,6 +22,7 @@ from rift.mano.yang_translator.rwmano.syntax.tosca_resource \ import ToscaResource from rift.mano.yang_translator.rwmano.yang.yang_vld import YangVld from collections import OrderedDict +import re TARGET_CLASS_NAME = 'YangNsd' @@ -44,7 +45,7 @@ class YangNsd(ToscaResource): 'constituent_vnfd', 'vnfd_member', 'min_instance_count', 'max_instance_count', 'input_parameter_xpath', 'config_actions', - 'initial_config_primitive',) + 'initial_config_primitive', ) def __init__(self, log, @@ -63,6 +64,7 @@ class YangNsd(ToscaResource): self.conf_prims = [] self.scale_grps = [] self.initial_cfg = [] + self.service_primitive = [] self.placement_groups = [] self.vnf_id_to_vnf_map = {} self.vnfd_files = vnfd_files @@ -73,6 +75,7 @@ class YangNsd(ToscaResource): self.forwarding_paths = {} self.substitution_mapping_forwarder = [] self.vnfd_sfc_map = None + self.duplicate_vnfd_name_list = [] def handle_yang(self, vnfds): self.log.debug(_("Process NSD desc {0}: {1}"). @@ -155,12 +158,12 @@ class YangNsd(ToscaResource): if key in dic: icp[key] = dic.pop(key) - params = {} + params = [] if self.PARAM in dic: for p in dic.pop(self.PARAM): if (self.NAME in p and self.VALUE in p): - params[p[self.NAME]] = p[self.VALUE] + params.append({self.NAME: p[self.NAME], self.VALUE:p[self.VALUE]}) else: # TODO (pjoseph): Need to add support to read the # config file and get the value from that @@ -175,6 +178,31 @@ class YangNsd(ToscaResource): self.log.debug(_("{0}, Initial config {1}").format(self, icp)) self.initial_cfg.append({self.PROPERTIES : icp}) + def process_service_primitive(dic): + prop = {} + params = [] + for key in [self.NAME, self.USER_DEF_SCRIPT]: + if key in dic: + prop[key] = dic.pop(key) + + if self.PARAM in dic: + for p in dic.pop(self.PARAM): + p_entry = {} + for name, value in p.items(): + p_entry[name] = value + params.append(p_entry) + + if len(params): + prop[self.PARAM] = params + + conf_prim = {self.NAME: prop[self.NAME], self.DESC : 'TestDescription'} + if self.USER_DEF_SCRIPT in prop: + conf_prim[self.USER_DEF_SCRIPT] = prop[self.USER_DEF_SCRIPT] + self.conf_prims.append(conf_prim) + + self.service_primitive.append({self.PROPERTIES : prop}) + + def process_vld(vld, dic): vld_conf = {} vld_prop = {} @@ -415,14 +443,22 @@ class YangNsd(ToscaResource): dic = deepcopy(self.yang) try: for key in self.REQUIRED_FIELDS: - self.props[key] = dic.pop(key) + if key in dic: + self.props[key] = dic.pop(key) self.id = self.props[self.ID] # Process constituent VNFDs + + vnfd_name_list = [] + member_vnf_index_list = [] if self.CONST_VNFD in dic: for cvnfd in dic.pop(self.CONST_VNFD): - process_const_vnfd(cvnfd) + if cvnfd[self.VNFD_ID_REF] not in member_vnf_index_list: + member_vnf_index_list.append(cvnfd[self.VNFD_ID_REF]) + process_const_vnfd(cvnfd) + else: + self.duplicate_vnfd_name_list.append(self.vnf_id_to_vnf_map[cvnfd[self.VNFD_ID_REF]]) # Process VLDs if self.VLD in dic: @@ -435,33 +471,23 @@ class YangNsd(ToscaResource): process_vnffgd(dic[self.VNFFGD], dic) - #if self. + + + # Process initial config primitives + if self.INITIAL_CFG in dic: + for icp_dic in dic.pop(self.INITIAL_CFG): + process_initial_config(icp_dic) - # Process config primitives + # NS service prmitive if self.CONF_PRIM in dic: - for cprim in dic.pop(self.CONF_PRIM): - conf_prim = {self.NAME: cprim.pop(self.NAME), self.DESC : 'TestDescription'} - if self.USER_DEF_SCRIPT in cprim: - conf_prim[self.USER_DEF_SCRIPT] = \ - cprim.pop(self.USER_DEF_SCRIPT) - self.conf_prims.append(conf_prim) - else: - err_msg = (_("{0}, Only user defined script supported " - "in config-primitive for now {}: {}"). - format(self, conf_prim, cprim)) - self.log.error(err_msg) - raise ValidationError(message=err_msg) + for icp_dic in dic.pop(self.CONF_PRIM): + process_service_primitive(icp_dic) # Process scaling group if self.SCALE_GRP in dic: for sg_dic in dic.pop(self.SCALE_GRP): process_scale_grp(sg_dic) - # Process initial config primitives - if self.INITIAL_CFG in dic: - for icp_dic in dic.pop(self.INITIAL_CFG): - process_initial_config(icp_dic) - # Process the input params if self.INPUT_PARAM_XPATH in dic: for param in dic.pop(self.INPUT_PARAM_XPATH): @@ -556,10 +582,13 @@ class YangNsd(ToscaResource): self.VENDOR: self.props[self.VENDOR], self.VERSION: self.props[self.VERSION], } + if self.LOGO in self.props: + tosca[self.METADATA][self.LOGO] = self.props[self.LOGO] + if len(self.vnfd_files) > 0: tosca[self.IMPORT] = [] imports = [] - for vnfd_file in self.vnfd_files: + for vnfd_file in set(self.vnfd_files): tosca[self.IMPORT].append('"{0}.yaml"'.format(vnfd_file)) tosca[self.TOPOLOGY_TMPL] = {} @@ -578,6 +607,7 @@ class YangNsd(ToscaResource): tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL] = {} # Add the VNFDs and VLDs + vnf_type_vld_list = [] for idx, vnfd in self.vnfds.items(): #vnfd.generate_vnf_template(tosca, idx) node = { @@ -591,28 +621,44 @@ class YangNsd(ToscaResource): if vnfd.name in self.vnf_to_vld_map: vld_list = self.vnf_to_vld_map[vnfd.name] node[self.REQUIREMENTS] = [] + for vld_idx in range(0, len(vld_list)): - vld_link_name = "{0}{1}".format("virtualLink", vld_idx + 1) - vld_prop = {} - vld_prop[vld_link_name] = vld_list[vld_idx] - node[self.REQUIREMENTS].append(vld_prop) - if vnfd.name in self._vnf_vld_conn_point_map: - vnf_vld_list = self._vnf_vld_conn_point_map[vnfd.name] - for vnf_vld in vnf_vld_list: - vnfd.generate_vld_link(vld_link_name, vnf_vld[1]) + if vnfd.vnf_type not in vnf_type_vld_list: + vld_link_name = "{0}{1}".format("virtualLink", vld_idx + 1) + vld_prop = {} + vld_prop[vld_link_name] = vld_list[vld_idx] + node[self.REQUIREMENTS].append(vld_prop) + if vnfd.vnf_type not in vnf_type_vld_list: + vnf_type_vld_list.append(vnfd.vnf_type) + if vnfd.name in self._vnf_vld_conn_point_map: + vnf_vld_list = set(self._vnf_vld_conn_point_map[vnfd.name]) + for vnf_vld in vnf_vld_list: + vnfd.generate_vld_link(vld_link_name, vnf_vld[1]) for sub_mapping in self.substitution_mapping_forwarder: if sub_mapping[0] == vnfd.name: vnfd.generate_forwarder_sub_mapping(sub_mapping) - for vnfd_name, cp_name in self.vnfd_sfc_map.items(): - if vnfd.name == vnfd_name: - vnfd.generate_sfc_link(cp_name) + if self.vnfd_sfc_map: + for vnfd_name, cp_name in self.vnfd_sfc_map.items(): + if vnfd.name == vnfd_name: + vnfd.generate_sfc_link(cp_name) tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vnfd.name] = node + v_idx = len(self.vnfds) + 1 + len(self.duplicate_vnfd_name_list) + for vnfd_name in self.duplicate_vnfd_name_list: + node = tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vnfd_name] + new_node = deepcopy(node) + st = re.sub(r'\d+$', '', vnfd_name.rstrip('_vnfd')) + + new_node[self.PROPERTIES][self.ID] = v_idx + node_name = "{}{}_vnfd".format(st, v_idx) + tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][node_name] = new_node + v_idx += 1 + for vld_node_name in self.vlds: tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vld_node_name] = self.vlds[vld_node_name] @@ -672,6 +718,23 @@ class YangNsd(ToscaResource): self.INITIAL_CFG: icpt }) + if len(self.service_primitive) > 0: + if self.POLICIES not in tosca[self.TOPOLOGY_TMPL]: + tosca[self.TOPOLOGY_TMPL][self.POLICIES] = [] + + for icp in self.service_primitive: + if len(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL]) > 0: + node_name = list(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL].keys())[0] + icpt = { + self.TYPE: self.T_NS_PRIMITIVE, + self.TARGETS : "[{0}]".format(node_name) + } + icpt.update(icp) + tosca[self.TOPOLOGY_TMPL][self.POLICIES].append({ + 'ns_service_primitives': icpt + }) + + if len(self.placement_groups) > 0: if self.POLICIES not in tosca[self.TOPOLOGY_TMPL]: tosca[self.TOPOLOGY_TMPL][self.POLICIES] = [] @@ -701,6 +764,25 @@ class YangNsd(ToscaResource): self.DEST: "{}/{}".format(self.SCRIPT_DIR, script), }) + for prim in self.service_primitive: + if 'properties' in prim: + if 'user_defined_script' in prim['properties']: + script = os.path.basename(prim['properties']['user_defined_script']) + files.append({ + self.TYPE: 'script', + self.NAME: script, + self.DEST: "{}/{}".format(self.SCRIPT_DIR, script), + }) + + if 'logo' in self.props: + icon = os.path.basename(self.props['logo']) + files.append({ + self.TYPE: 'icons', + self.NAME: icon, + self.DEST: "{}/{}".format(self.ICON_DIR, icon), + }) + + # TODO (pjoseph): Add support for config scripts, # charms, etc diff --git a/common/python/rift/mano/yang_translator/rwmano/yang/yang_vnfd.py b/common/python/rift/mano/yang_translator/rwmano/yang/yang_vnfd.py index ec21e3c5..84fdf22e 100644 --- a/common/python/rift/mano/yang_translator/rwmano/yang/yang_vnfd.py +++ b/common/python/rift/mano/yang_translator/rwmano/yang/yang_vnfd.py @@ -35,6 +35,15 @@ class YangVnfd(ToscaResource): ('mgmt_interface', 'http_endpoint', 'monitoring_param') vnf_prefix_type = 'tosca.nodes.nfv.riftio.' + VALUE_TYPE_CONVERSION_MAP = { + 'INTEGER' : 'integer', + 'INT' : 'integer', + 'STRING' : 'string', + 'DECIMAL' : 'float', + 'INTEGER': 'INTEGER', + 'DECIMAL' : 'float' + + } def __init__(self, log, @@ -100,7 +109,7 @@ class YangVnfd(ToscaResource): for parameter in init_conf_prim['parameter']: init_conf['parameter'].append({parameter['name']: parameter['value']}) init_config_prims.append(init_conf) - vnf_conf['initial_config_primitive'] = init_config_prims + vnf_conf['initial_config'] = init_config_prims self.vnf_configuration = vnf_conf @@ -168,6 +177,9 @@ class YangVnfd(ToscaResource): mon_param['url_path'] = param['http_endpoint_ref'] if 'json_query_method' in param: mon_param['json_query_method'] = param['json_query_method'].lower() + #if 'value_type' in param: + # mon_param['constraints'] = {} + # mon_param['constraints']['value_type'] = YangVnfd.VALUE_TYPE_CONVERSION_MAP[param['value_type'].upper()] if 'group_tag' in param: ui_param['group_tag'] = param['group_tag'] if 'widget_type' in param: @@ -210,7 +222,8 @@ class YangVnfd(ToscaResource): dic = deepcopy(self.yang) try: for key in self.REQUIRED_FIELDS: - self.props[key] = dic.pop(key) + if key in dic: + self.props[key] = dic.pop(key) self.id = self.props[self.ID] @@ -292,11 +305,13 @@ class YangVnfd(ToscaResource): mon_param = {} mon_param['properties'] = self.mon_param[0] tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES]['monitoring_param'] = mon_param #TEST - if len(self.mon_param) == 2: - mon_param = {} - mon_param = {} - mon_param['properties'] = self.mon_param[1] - tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES]['monitoring_param_1'] = mon_param + if len(self.mon_param) > 1: + for idx in range(1, len(self.mon_param)): + monitor_param_name = "monitoring_param_{}".format(idx) + mon_param = {} + mon_param = {} + mon_param['properties'] = self.mon_param[idx] + tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES][monitor_param_name] = mon_param node = {} node[self.TYPE] = self.T_VNF1 @@ -363,13 +378,17 @@ class YangVnfd(ToscaResource): for vdu in self.vdus: if conn_point in vdu.cp_name_to_cp_node: conn_point_node_name = vdu.cp_name_to_cp_node[conn_point] - self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.REQUIREMENTS].\ - append({virtualLink : "[{0}, {1}]".format(conn_point_node_name, "virtualLink")}) + if {virtualLink : "[{0}, {1}]".format(conn_point_node_name, "virtualLink")} not in \ + self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.REQUIREMENTS]: + self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.REQUIREMENTS].\ + append({virtualLink : "[{0}, {1}]".format(conn_point_node_name, "virtualLink")}) if self.REQUIREMENTS not in self.tosca[self.NODE_TYPES][self.vnf_type]: self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS] = [] - self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS].append({virtualLink : { - "type": "tosca.nodes.nfv.VL"}}) + if {virtualLink : {"type": "tosca.nodes.nfv.VL"}} not in self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS]: + self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS].append({virtualLink : { + "type": "tosca.nodes.nfv.VL"}}) + def generate_forwarder_sub_mapping(self, sub_link): if self.CAPABILITIES not in self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]: self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.CAPABILITIES] = {} diff --git a/common/python/rift/mano/yang_translator/rwmano/yang_translator.py b/common/python/rift/mano/yang_translator/rwmano/yang_translator.py index 09194943..90288a83 100644 --- a/common/python/rift/mano/yang_translator/rwmano/yang_translator.py +++ b/common/python/rift/mano/yang_translator/rwmano/yang_translator.py @@ -59,10 +59,10 @@ class YangTranslator(object): self.get_yangs() else: if 'nsd' in self.yangs: - self.output_files['nsd'].append(self.yangs['nsd'][0]['short_name']) + self.output_files['nsd'].append(self.yangs['nsd'][0]['short_name'].replace(' ','_')) if 'vnfd' in self.yangs: for yang_vnfd in self.yangs['vnfd']: - self.output_files['vnfd'].append(yang_vnfd['short_name']) + self.output_files['vnfd'].append(yang_vnfd['short_name'].replace(' ','_')) self.node_translator = TranslateDescriptors(self.log, self.yangs, @@ -133,9 +133,9 @@ class YangTranslator(object): raise ValidationError(message="No NSD or VNFD uploaded") else: if 'nsd' in self.yangs: - sub_folder_name = self.yangs['nsd'][0]['short_name'] + sub_folder_name = self.yangs['nsd'][0]['short_name'].replace(' ','_') elif 'vnfd' in self.yangs: - sub_folder_name = self.yangs['vnfd'][0]['short_name'] + sub_folder_name = self.yangs['vnfd'][0]['short_name'].replace(' ','_') subdir = os.path.join(output_dir, sub_folder_name) @@ -147,14 +147,20 @@ class YangTranslator(object): def_dir = os.path.join(subdir, 'Definitions') os.makedirs(def_dir) shutil.copy2(riftio_src_file, def_dir + "/riftiotypes.yaml") + tosca_meta_entry_file = None for tmpl_key in tmpl_out: tmpl = tmpl_out[tmpl_key] - entry_file = os.path.join(def_dir, tmpl_key+'.yaml') + file_name = tmpl_key.replace(' ','_') + entry_file = os.path.join(def_dir, file_name+'.yaml') + if file_name.endswith('nsd'): + tosca_meta_entry_file = file_name self.log.debug(_("Writing file {0}"). format(entry_file)) with open(entry_file, 'w+') as f: f.write(tmpl[ToscaTemplate.TOSCA]) + if tosca_meta_entry_file is None: + tosca_meta_entry_file = sub_folder_name # Create the Tosca meta meta_dir = os.path.join(subdir, 'TOSCA-Metadata') os.makedirs(meta_dir) @@ -162,7 +168,7 @@ class YangTranslator(object): CSAR-Version: 1.1 Created-By: RIFT.io Entry-Definitions: Definitions/''' - meta_data = "{}{}".format(meta, sub_folder_name+'.yaml') + meta_data = "{}{}".format(meta, tosca_meta_entry_file+'.yaml') meta_file = os.path.join(meta_dir, 'TOSCA.meta') self.log.debug(_("Writing file {0}:\n{1}"). format(meta_file, meta_data)) @@ -213,6 +219,15 @@ Entry-Definitions: Definitions/''' pkg.extract_file(script_file_map[fname], dest_path) break + elif ftype == 'icons': + icon_file_map = \ + rift.package.icon.PackageIconExtractor.package_icon_files(pkg) + if fname in icon_file_map: + self.log.debug(_("Extracting script {0} to {1}"). + format(fname, dest_path)) + pkg.extract_file(icon_file_map[fname], + dest_path) + break else: self.log.warn(_("Unknown file type {0}: {1}"). @@ -226,7 +241,7 @@ Entry-Definitions: Definitions/''' os.chdir(subdir) try: - zip_file = key + '.zip' + zip_file = sub_folder_name + '.zip' zip_path = os.path.join(output_dir, zip_file) self.log.debug(_("Creating zip file {0}").format(zip_path)) zip_cmd = "zip -r {}.partial ." -- 2.25.1