From 438606259fca179b732eeba350ba95f04f5ba838 Mon Sep 17 00:00:00 2001 From: maopengzhang Date: Mon, 10 Sep 2018 22:53:26 +0800 Subject: [PATCH] Support PNFD in NSD Support to parser PNFD in NSD Change-Id: I6cdcb5ca275ca532c10e47107a635685e62cfacc Issue-ID: VFC-1102 Signed-off-by: maopengzhang --- catalog/packages/urls.py | 10 +- catalog/pub/utils/toscaparser/basemodel.py | 102 +++---- catalog/pub/utils/toscaparser/nsdmodel.py | 314 ++++--------------- catalog/pub/utils/toscaparser/pnfmodel.py | 8 +- catalog/pub/utils/toscaparser/testdata/ns/ran.csar | Bin 0 -> 2977 bytes .../testdata/resource-ZteMmeFixVl-csar.csar | Bin 34326 -> 0 bytes .../toscaparser/testdata/{vcpe => vnf}/infra.csar | Bin .../toscaparser/testdata/{vcpe => vnf}/vbng.csar | Bin .../testdata/{vcpe => vnf}/vbrgemu.csar | Bin .../toscaparser/testdata/{vcpe => vnf}/vgmux.csar | Bin .../toscaparser/testdata/{vcpe => vnf}/vgw.csar | Bin catalog/pub/utils/toscaparser/tests.py | 29 +- catalog/pub/utils/toscaparser/vnfdmodel.py | 334 +++++++-------------- 13 files changed, 224 insertions(+), 573 deletions(-) create mode 100644 catalog/pub/utils/toscaparser/testdata/ns/ran.csar delete mode 100644 catalog/pub/utils/toscaparser/testdata/resource-ZteMmeFixVl-csar.csar rename catalog/pub/utils/toscaparser/testdata/{vcpe => vnf}/infra.csar (100%) rename catalog/pub/utils/toscaparser/testdata/{vcpe => vnf}/vbng.csar (100%) rename catalog/pub/utils/toscaparser/testdata/{vcpe => vnf}/vbrgemu.csar (100%) rename catalog/pub/utils/toscaparser/testdata/{vcpe => vnf}/vgmux.csar (100%) rename catalog/pub/utils/toscaparser/testdata/{vcpe => vnf}/vgw.csar (100%) diff --git a/catalog/packages/urls.py b/catalog/packages/urls.py index ca090c74..e62bc76a 100644 --- a/catalog/packages/urls.py +++ b/catalog/packages/urls.py @@ -19,27 +19,31 @@ from catalog.packages.views import catalog_views, ns_descriptor_views, pnf_descr urlpatterns = [ + + # Sync package from SDC url(r'^api/catalog/v1/nspackages$', catalog_views.nspackages_rc, name='nspackages_rc'), url(r'^api/catalog/v1/nspackages/(?P[0-9a-zA-Z\-\_]+)$', catalog_views.ns_rd_csar, name='nspackage_rd'), url(r'^api/catalog/v1/vnfpackages$', catalog_views.nfpackages_rc, name='nfpackages_rc'), url(r'^api/catalog/v1/vnfpackages/(?P[0-9a-zA-Z\-\_]+)$', catalog_views.nf_rd_csar, name='nfpackage_rd'), + + # NFV Model Parser url(r'^api/catalog/v1/parsernsd$', catalog_views.ns_model_parser, name='nsmodelparser_rc'), url(r'^api/catalog/v1/parservnfd$', catalog_views.vnf_model_parser, name='vnfmodelparser_rc'), url(r'^api/catalog/v1/parserpnfd$', pnf_descriptor_views.pnf_model_parser, name='pnfmodelparser_rc'), - # NSPakcage& NSD + # ETSI SOL005 NSD API url(r'^api/nsd/v1/ns_descriptors$', ns_descriptor_views.ns_descriptors_rc, name='ns_descriptors_rc'), url(r'^api/nsd/v1/ns_descriptors/(?P[0-9a-zA-Z\-\_]+)$', ns_descriptor_views.ns_info_rd, name='ns_info_rd'), url(r'^api/nsd/v1/ns_descriptors/(?P[0-9a-zA-Z\-\_]+)/nsd_content$', ns_descriptor_views.nsd_content_ru, name='nsd_content_ru'), # url(r'^api/nsd/v1/subscriptions', nsd_subscriptions.as_view(), name='subscriptions_rc'), # url(r'^api/nsd/v1/subscriptions/(?P[0-9a-zA-Z\-\_]+)$', nsd_subscription.as_view(), name='subscription_rd'), - # PNF Package and PNFD + # ETSI SOL005 PNFD url(r'^api/nsd/v1/pnf_descriptors$', pnf_descriptor_views.pnf_descriptors_rc, name='pnf_descriptors_rc'), url(r'^api/nsd/v1/pnf_descriptors/(?P[0-9a-zA-Z\-\_]+)$', pnf_descriptor_views.pnfd_info_rd, name='pnfd_info_rd'), url(r'^api/nsd/v1/pnf_descriptors/(?P[0-9a-zA-Z\-\_]+)/pnfd_content$', pnf_descriptor_views.pnfd_content_ru, name='pnfd_content_ru'), - # VNFD + # ETSI SOL005&SOL003 VNF Package url(r'^api/vnfpkgm/v1/vnf_packages$', vnf_package_views.vnf_packages_rc, name='vnf_packages_rc'), url(r'^api/vnfpkgm/v1/vnf_packages/(?P[0-9a-zA-Z\-\_]+)$', vnf_package_views.vnf_package_rd, name='vnf_package_rd'), url(r'^api/vnfpkgm/v1/vnf_packages/(?P[0-9a-zA-Z\-\_]+)/package_content$', vnf_package_views.package_content_ru, name='package_content_ru'), diff --git a/catalog/pub/utils/toscaparser/basemodel.py b/catalog/pub/utils/toscaparser/basemodel.py index 192e44b6..54e1fd97 100644 --- a/catalog/pub/utils/toscaparser/basemodel.py +++ b/catalog/pub/utils/toscaparser/basemodel.py @@ -30,10 +30,6 @@ from catalog.pub.utils.toscaparser.dataentityext import DataEntityExt logger = logging.getLogger(__name__) -# TOSCA template key names -SECTIONS = (VDU_TYPE, VL_TYPE, CP_TYPE) = \ - ('tosca.nodes.nfv.Vdu.Compute', 'tosca.nodes.nfv.VnfVirtualLink', 'tosca.nodes.nfv.Cp') - class BaseInfoModel(object): @@ -354,22 +350,6 @@ class BaseInfoModel(object): return False return True - def isVnf(self, node): - # return node['nodeType'].upper().find('.VNF.') >= 0 or node['nodeType'].upper().endswith('.VNF') - return node['nodeType'].upper().find('.VF.') >= 0 or node['nodeType'].upper().endswith('.VF') - - def isPnf(self, node): - return node['nodeType'].upper().find('.PNF.') >= 0 or node['nodeType'].upper().endswith('.PNF') - - def isCp(self, node, node_types): - return node['nodeType'].upper().find('TOSCA.NODES.NFV.VDUCP') >= 0 - - def isVl(self, node, node_types): - return node['nodeType'].upper().find('TOSCA.NODES.NFV.VNFVIRTUALLINK') >= 0 - - def isService(self, node): - return node['nodeType'].upper().find('.SERVICE.') >= 0 or node['nodeType'].upper().endswith('.SERVICE') - def get_requirement_node_name(self, req_value): return self.get_prop_from_obj(req_value, 'node') @@ -389,12 +369,6 @@ class BaseInfoModel(object): def getNodeDependencys(self, node): return self.getRequirementByName(node, 'dependency') - def getVirtualLinks(self, node): - return self.getRequirementByName(node, 'virtual_link') - - def getVirtualbindings(self, node): - return self.getRequirementByName(node, 'virtual_binding') - def getRequirementByName(self, node, requirementName): requirements = [] if 'requirements' in node: @@ -404,15 +378,6 @@ class BaseInfoModel(object): requirements.append(value) return requirements - def get_networks(self, node): - rets = [] - if 'requirements' in node: - for item in node['requirements']: - for key, value in item.items(): - if key.upper().find('VIRTUAL_LINK') >= 0 or key.upper().find('VIRTUALLINK') >= 0: - rets.append({"key_name": key, "vl_id": self.get_requirement_node_name(value)}) - return rets - def _verify_value(self, value, inputs, parsed_params): if value == '{}': return '' @@ -443,44 +408,51 @@ class BaseInfoModel(object): value = value.replace(getInput, json.dumps(input_def.default)) return value - def get_node_vl_id(self, node): - vl_ids = map(lambda x: self.get_requirement_node_name(x), self.getVirtualLinks(node)) - if len(vl_ids) > 0: - return vl_ids[0] - return "" - def get_node_by_name(self, node_templates, name): for node in node_templates: if node['name'] == name: return node return None - def get_all_nested_ns(self, nodes): - nss = [] - for node in nodes: - if self.is_nested_ns(node): - ns = {} - ns['ns_id'] = node['name'] - ns['description'] = node['description'] - ns['properties'] = node['properties'] - ns['networks'] = self.get_networks(node) - - nss.append(ns) - return nss - - def is_nested_ns(self, node): - return node['nodeType'].upper().find('.NS.') >= 0 or node['nodeType'].upper().endswith('.NS') - - def isVdu(self, node, node_types): - return node['nodeType'].upper().find('TOSCA.NODES.NFV.VDU.COMPUTE') >= 0 - def getCapabilityByName(self, node, capabilityName): if 'capabilities' in node and capabilityName in node['capabilities']: return node['capabilities'][capabilityName] return None - def get_node_vdu_id(self, node): - vdu_ids = map(lambda x: self.get_requirement_node_name(x), self.getVirtualbindings(node)) - if len(vdu_ids) > 0: - return vdu_ids[0] - return "" + def get_base_path(self, tosca): + fpath, fname = os.path.split(tosca.path) + return fpath + + def build_artifacts(self, node): + rets = [] + if 'artifacts' in node and len(node['artifacts']) > 0: + artifacts = node['artifacts'] + for name, value in artifacts.items(): + ret = {} + if isinstance(value, dict): + ret['artifact_name'] = name + ret['type'] = value.get('type', '') + ret['file'] = value.get('file', '') + ret['repository'] = value.get('repository', '') + ret['deploy_path'] = value.get('deploy_path', '') + else: + ret['artifact_name'] = name + ret['type'] = '' + ret['file'] = value + ret['repository'] = '' + ret['deploy_path'] = '' + rets.append(ret) + return rets + + def get_node_by_req(self, node_templates, req): + req_node_name = self.get_requirement_node_name(req) + return self.get_node_by_name(node_templates, req_node_name) + + def isGroupTypeX(self, group, groupTypes, x): + group_type = group['groupType'] + while group_type != x: + group_type_derived = group_type + group_type = groupTypes[group_type]['derived_from'] + if group_type == "tosca.groups.Root" or group_type == group_type_derived: + return False + return True diff --git a/catalog/pub/utils/toscaparser/nsdmodel.py b/catalog/pub/utils/toscaparser/nsdmodel.py index c01ea7e4..ae99e9bb 100644 --- a/catalog/pub/utils/toscaparser/nsdmodel.py +++ b/catalog/pub/utils/toscaparser/nsdmodel.py @@ -13,244 +13,89 @@ # limitations under the License. import functools - +import logging from catalog.pub.utils.toscaparser.basemodel import BaseInfoModel +logger = logging.getLogger(__name__) + +SECTIONS = (NS_TYPE, NS_VNF_TYPE, NS_VL_TYPE, NS_PNF_TYPE, NS_NFP_TYPE, NS_VNFFG_TYPE) = \ + ('tosca.nodes.nfv.NS', + 'tosca.nodes.nfv.VNF', + 'tosca.nodes.nfv.NsVirtualLink', + 'tosca.nodes.nfv.PNF', + 'tosca.nodes.nfv.NFP', + 'tosca.nodes.nfv.VNFFG') class EtsiNsdInfoModel(BaseInfoModel): def __init__(self, path, params): - tosca = self.buildToscaTemplate(path, params) - self.parseModel(tosca) + super(EtsiNsdInfoModel, self).__init__(path, params) def parseModel(self, tosca): self.buidMetadata(tosca) if hasattr(tosca, 'topology_template') and hasattr(tosca.topology_template, 'inputs'): self.inputs = self.buildInputs(tosca.topology_template.inputs) - - nodeTemplates = map(functools.partial(self.buildNode, tosca=tosca), - tosca.nodetemplates) - node_types = tosca.topology_template.custom_defs - self.vnfs = self._get_all_vnf(nodeTemplates) - self.pnfs = self._get_all_pnf(nodeTemplates) - self.vls = self.get_all_vl(nodeTemplates, node_types) - self.cps = self.get_all_cp(nodeTemplates, node_types) - self.routers = self.get_all_router(nodeTemplates) - self.fps = self._get_all_fp(nodeTemplates, node_types) - self.vnffgs = self._get_all_vnffg(tosca.topology_template.groups) - self.server_groups = self.get_all_server_group(tosca.topology_template.groups) - self.ns_exposed = self.get_all_endpoint_exposed(tosca.topology_template) - self.policies = self._get_policies_scaling(tosca.topology_template.policies) - self.ns_flavours = self.get_all_flavour(tosca.topology_template.groups) - self.nested_ns = self.get_all_nested_ns(nodeTemplates) - - def buildInputs(self, top_inputs): - ret = {} - for tmpinput in top_inputs: - tmp = {} - tmp['type'] = tmpinput.type - tmp['description'] = tmpinput.description - tmp['default'] = tmpinput.default - - ret[tmpinput.name] = tmp - return ret - - def buildNode(self, nodeTemplate, tosca): - inputs = tosca.inputs - parsed_params = tosca.parsed_params - ret = {} - ret['name'] = nodeTemplate.name - ret['nodeType'] = nodeTemplate.type - if 'description' in nodeTemplate.entity_tpl: - ret['description'] = nodeTemplate.entity_tpl['description'] - else: - ret['description'] = '' - if 'metadata' in nodeTemplate.entity_tpl: - ret['metadata'] = nodeTemplate.entity_tpl['metadata'] - else: - ret['metadata'] = '' - props = self.buildProperties_ex(nodeTemplate, tosca.topology_template) - ret['properties'] = self.verify_properties(props, inputs, parsed_params) - ret['requirements'] = self.build_requirements(nodeTemplate) - self.buildCapabilities(nodeTemplate, inputs, ret) - self.buildArtifacts(nodeTemplate, inputs, ret) - interfaces = self.build_interfaces(nodeTemplate) - if interfaces: - ret['interfaces'] = interfaces - return ret - - def _get_all_vnf(self, nodeTemplates): + nodeTemplates = map(functools.partial(self.buildNode, tosca=tosca), tosca.nodetemplates) + types = tosca.topology_template.custom_defs + self.basepath = self.get_base_path(tosca) + self.vnfs = self._get_all_vnf(nodeTemplates, types) + self.pnfs = self._get_all_pnf(nodeTemplates, types) + self.vls = self._get_all_vl(nodeTemplates, types) + self.fps = self._get_all_fp(nodeTemplates, types) + self.vnffgs = self._get_all_vnffg(tosca.topology_template.groups, types) + self.ns_exposed = self._get_all_endpoint_exposed(tosca.topology_template) + self.nested_ns = self._get_all_nested_ns(nodeTemplates, types) + + def _get_all_vnf(self, nodeTemplates, node_types): vnfs = [] for node in nodeTemplates: - if self.isVnf(node): + if self.isNodeTypeX(node, node_types, NS_VNF_TYPE): vnf = {} vnf['vnf_id'] = node['name'] vnf['description'] = node['description'] vnf['properties'] = node['properties'] - vnf['properties']['id'] = node['metadata'].get('UUID', 'undefined') - # for key in vnf['properties'].iterkeys(): - # if key.endswith('_version'): - # vnf['properties'].update(version=vnf['properties'].pop(key)) - # if key.endswith('_id'): - # vnf['properties'].update(id=vnf['properties'].pop(key)) - # if key.endswith('_csarProvider'): - # vnf['properties'].update(csarProvider=vnf['properties'].pop(key)) - # if key.endswith('_csarVersion'): - # vnf['properties'].update(csarVersion=vnf['properties'].pop(key)) - # if key.endswith('_vendor'): - # vnf['properties'].update(vendor=vnf['properties'].pop(key)) - # if key.endswith('_csarType'): - # vnf['properties'].update(csarType=vnf['properties'].pop(key)) - # if key.endswith('_vnfm_type') or key.endswith('_vnfmType'): - # vnf['properties'].update(vnfmType=vnf['properties'].pop(key)) - # vnf['dependencies'] = map(lambda x: self.get_requirement_node_name(x), self.getNodeDependencys(node)) - vnf['dependencies'] = self.get_networks(node) - vnf['networks'] = self.get_networks(node) - + if node['metadata']: + vnf['properties']['id'] = node['metadata'].get('UUID', 'undefined') + vnf['dependencies'] = self._get_networks(node, node_types) + vnf['networks'] = self._get_networks(node, node_types) vnfs.append(vnf) return vnfs - def _get_all_pnf(self, nodeTemplates): + def _get_all_pnf(self, nodeTemplates, node_types): pnfs = [] for node in nodeTemplates: - if self.isPnf(node): + if self.isNodeTypeX(node, node_types, NS_PNF_TYPE): pnf = {} pnf['pnf_id'] = node['name'] pnf['description'] = node['description'] pnf['properties'] = node['properties'] - pnf['cps'] = self.getVirtalBindingCpIds(node, nodeTemplates) - + pnf['networks'] = self._get_networks(node, node_types) pnfs.append(pnf) return pnfs - def getVirtalBindingCpIds(self, node, nodeTemplates): - return map(lambda x: x['name'], self.getVirtalBindingCps(node, nodeTemplates)) - - def getVirtalBindingCps(self, node, nodeTemplates): - cps = [] - for tmpnode in nodeTemplates: - if 'requirements' in tmpnode: - for item in tmpnode['requirements']: - for key, value in item.items(): - if key.upper().startswith('VIRTUAL_BINDING'): - req_node_name = self.get_requirement_node_name(value) - if req_node_name is not None and req_node_name == node['name']: - cps.append(tmpnode) - return cps - - def get_all_vl(self, nodeTemplates, node_types): + def _get_all_vl(self, nodeTemplates, node_types): vls = [] for node in nodeTemplates: - if self.isVl(node, node_types) or self._isExternalVL(node): + if self.isNodeTypeX(node, node_types, NS_VL_TYPE): vl = dict() vl['vl_id'] = node['name'] vl['description'] = node['description'] vl['properties'] = node['properties'] - vl['route_external'] = False if self.isVl(node, node_types) else True - # vl['route_id'] = self._get_vl_route_id(node) vls.append(vl) return vls - def _get_vl_route_id(self, node): - route_ids = map(lambda x: self.get_requirement_node_name(x), - self.getRequirementByName(node, 'virtual_route')) - if len(route_ids) > 0: - return route_ids[0] - return "" - - def _isExternalVL(self, node): - return node['nodeType'].upper().find('.ROUTEEXTERNALVL') >= 0 - - def get_all_cp(self, nodeTemplates, node_types): - cps = [] - for node in nodeTemplates: - if self.isCp(node, node_types): - cp = {} - cp['cp_id'] = node['name'] - cp['cpd_id'] = node['name'] - cp['description'] = node['description'] - cp['properties'] = node['properties'] - cp['vl_id'] = self.get_node_vl_id(node) - binding_node_ids = map(lambda x: self.get_requirement_node_name(x), self.getVirtualbindings(node)) - # cp['vnf_id'] = self._filter_vnf_id(binding_node_ids, nodeTemplates) - cp['pnf_id'] = self._filter_pnf_id(binding_node_ids, nodeTemplates) - vls = self.buil_cp_vls(node) - if len(vls) > 1: - cp['vls'] = vls - cps.append(cp) - return cps - - def buil_cp_vls(self, node): - return map(lambda x: self._build_cp_vl(x), self.getVirtualLinks(node)) - - def _build_cp_vl(self, req): - cp_vl = {} - cp_vl['vl_id'] = self.get_prop_from_obj(req, 'node') - relationship = self.get_prop_from_obj(req, 'relationship') - if relationship is not None: - properties = self.get_prop_from_obj(relationship, 'properties') - if properties is not None and isinstance(properties, dict): - for key, value in properties.items(): - cp_vl[key] = value - return cp_vl - - def _filter_pnf_id(self, node_ids, node_templates): - for node_id in node_ids: - node = self.get_node_by_name(node_templates, node_id) - if self.isPnf(node): - return node_id - return "" - - def get_all_router(self, nodeTemplates): - rets = [] - for node in nodeTemplates: - if self._isRouter(node): - ret = {} - ret['router_id'] = node['name'] - ret['description'] = node['description'] - ret['properties'] = node['properties'] - ret['external_vl_id'] = self._get_router_external_vl_id(node) - ret['external_ip_addresses'] = self._get_external_ip_addresses(node) - - rets.append(ret) - return rets - - def _isRouter(self, node): - return node['nodeType'].upper().find('.ROUTER.') >= 0 or node['nodeType'].upper().endswith('.ROUTER') - - def _get_router_external_vl(self, node): - return self.getRequirementByName(node, 'external_virtual_link') - - def _get_router_external_vl_id(self, node): - ids = map(lambda x: self.get_requirement_node_name(x), self._get_router_external_vl(node)) - if len(ids) > 0: - return ids[0] - return "" - - def _get_external_ip_addresses(self, node): - external_vls = self._get_router_external_vl(node) - if len(external_vls) > 0: - if 'relationship' in external_vls[0] and 'properties' in external_vls[0]['relationship'] and 'router_ip_address' in external_vls[0]['relationship']['properties']: - return external_vls[0]['relationship']['properties']['router_ip_address'] - return [] - def _get_all_fp(self, nodeTemplates, node_types): fps = [] for node in nodeTemplates: - if self._isFp(node): + if self.isNodeTypeX(node, node_types, NS_NFP_TYPE): fp = {} fp['fp_id'] = node['name'] fp['description'] = node['description'] fp['properties'] = node['properties'] fp['forwarder_list'] = self._getForwarderList(node, nodeTemplates, node_types) - fps.append(fp) return fps - def _isFp(self, node): - return node['nodeType'].upper().find('.FP.') >= 0 or node['nodeType'].upper().find('.SFP.') >= 0 or node[ - 'nodeType'].upper().endswith('.FP') or node['nodeType'].upper().endswith('.SFP') - def _getForwarderList(self, node, node_templates, node_types): forwarderList = [] if 'requirements' in node: @@ -258,57 +103,29 @@ class EtsiNsdInfoModel(BaseInfoModel): for key, value in item.items(): if key == 'forwarder': tmpnode = self.get_node_by_req(node_templates, value) - type = 'cp' if self.isCp(tmpnode, node_types) else 'vnf' + type = 'pnf' if self.isNodeTypeX(tmpnode, node_types, NS_PNF_TYPE) else 'vnf' req_node_name = self.get_requirement_node_name(value) if isinstance(value, dict) and 'capability' in value: forwarderList.append( {"type": type, "node_name": req_node_name, "capability": value['capability']}) else: forwarderList.append({"type": type, "node_name": req_node_name, "capability": ""}) - return forwarderList - def get_node_by_req(self, node_templates, req): - req_node_name = self.get_requirement_node_name(req) - return self.get_node_by_name(node_templates, req_node_name) - - def _get_all_vnffg(self, groups): + def _get_all_vnffg(self, groups, group_types): vnffgs = [] for group in groups: - if self._isVnffg(group): + if self.isGroupTypeX(group, group_types, NS_VNFFG_TYPE): vnffg = {} vnffg['vnffg_id'] = group.name vnffg['description'] = group.description if 'properties' in group.tpl: vnffg['properties'] = group.tpl['properties'] vnffg['members'] = group.members - vnffgs.append(vnffg) return vnffgs - def _isVnffg(self, group): - return group.type.upper().find('.VNFFG.') >= 0 or group.type.upper().find( - '.SFC.') >= 0 or group.type.upper().endswith('.VNFFG') or group.type.upper().endswith('.SFC') - - def get_all_server_group(self, groups): - rets = [] - for group in groups: - if self._isServerGroup(group): - ret = {} - ret['group_id'] = group.name - ret['description'] = group.description - if 'properties' in group.tpl: - ret['properties'] = group.tpl['properties'] - ret['members'] = group.members - - rets.append(ret) - return rets - - def _isServerGroup(self, group): - return group.type.upper().find('.AFFINITYORANTIAFFINITYGROUP.') >= 0 or group.type.upper().endswith( - '.AFFINITYORANTIAFFINITYGROUP') - - def get_all_endpoint_exposed(self, topo_tpl): + def _get_all_endpoint_exposed(self, topo_tpl): if 'substitution_mappings' in topo_tpl.tpl: external_cps = self._get_external_cps(topo_tpl.tpl['substitution_mappings']) forward_cps = self._get_forward_cps(topo_tpl.tpl['substitution_mappings']) @@ -335,43 +152,22 @@ class EtsiNsdInfoModel(BaseInfoModel): forward_cps.append({"key_name": key, "cpd_id": value}) return forward_cps - def _get_policies_scaling(self, top_policies): - policies_scaling = [] - scaling_policies = self.get_scaling_policies(top_policies) - if len(scaling_policies) > 0: - policies_scaling.append({"scaling": scaling_policies}) - return policies_scaling - - def get_policies_by_keyword(self, top_policies, keyword): - ret = [] - for policy in top_policies: - if policy.type.upper().find(keyword) >= 0: - tmp = {} - tmp['policy_id'] = policy.name - tmp['description'] = policy.description - if 'properties' in policy.entity_tpl: - tmp['properties'] = policy.entity_tpl['properties'] - tmp['targets'] = policy.targets - ret.append(tmp) - - return ret - - def get_scaling_policies(self, top_policies): - return self.get_policies_by_keyword(top_policies, '.SCALING') - - def get_all_flavour(self, groups): + def _get_all_nested_ns(self, nodes, node_types): + nss = [] + for node in nodes: + if self.isNodeTypeX(node, node_types, NS_TYPE): + ns = {} + ns['ns_id'] = node['name'] + ns['description'] = node['description'] + ns['properties'] = node['properties'] + ns['networks'] = self._get_networks(node) + nss.append(ns) + return nss + + def _get_networks(self, node, node_types): rets = [] - for group in groups: - if self._isFlavour(group): - ret = {} - ret['flavour_id'] = group.name - ret['description'] = group.description - if 'properties' in group.tpl: - ret['properties'] = group.tpl['properties'] - ret['members'] = group.members - - rets.append(ret) + if 'requirements' in node and (self.isNodeTypeX(node, node_types, NS_TYPE) or self.isNodeTypeX(node, node_types, NS_VNF_TYPE)): + for item in node['requirements']: + for key, value in item.items(): + rets.append({"key_name": key, "vl_id": self.get_requirement_node_name(value)}) return rets - - def _isFlavour(self, group): - return group.type.upper().find('FLAVOUR') >= 0 diff --git a/catalog/pub/utils/toscaparser/pnfmodel.py b/catalog/pub/utils/toscaparser/pnfmodel.py index ce531d6d..7dc99d80 100644 --- a/catalog/pub/utils/toscaparser/pnfmodel.py +++ b/catalog/pub/utils/toscaparser/pnfmodel.py @@ -14,7 +14,6 @@ import functools import logging -import os from catalog.pub.utils.toscaparser.basemodel import BaseInfoModel logger = logging.getLogger(__name__) @@ -31,15 +30,10 @@ class PnfdInfoModel(BaseInfoModel): nodeTemplates = map(functools.partial(self.buildNode, tosca=tosca), tosca.nodetemplates) - print nodeTemplates - self.basepath = self._get_base_path(tosca) + self.basepath = self.get_base_path(tosca) self.pnf = {} self.get_all_cp(nodeTemplates) - def _get_base_path(self, tosca): - fpath, fname = os.path.split(tosca.path) - return fpath - def get_substitution_mappings(self, tosca): pnf_substitution_mappings = tosca.tpl['topology_template']['substitution_mappings'] if pnf_substitution_mappings: diff --git a/catalog/pub/utils/toscaparser/testdata/ns/ran.csar b/catalog/pub/utils/toscaparser/testdata/ns/ran.csar new file mode 100644 index 0000000000000000000000000000000000000000..ba78605d0ab7626bc572c7ff2037f6d8649ca637 GIT binary patch literal 2977 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 Xu^Q?zGSSom7-`os9RToz<-q$7mofAq literal 0 HcmV?d00001 diff --git a/catalog/pub/utils/toscaparser/testdata/resource-ZteMmeFixVl-csar.csar b/catalog/pub/utils/toscaparser/testdata/resource-ZteMmeFixVl-csar.csar deleted file mode 100644 index 4d7e3984f308099140d0d4c9b4d5e08b8e9fcbdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34326 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%0AHyDvHZ*lwKX> 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 I-`{8d3ykLNDgXcg diff --git a/catalog/pub/utils/toscaparser/testdata/vcpe/infra.csar b/catalog/pub/utils/toscaparser/testdata/vnf/infra.csar similarity index 100% rename from catalog/pub/utils/toscaparser/testdata/vcpe/infra.csar rename to catalog/pub/utils/toscaparser/testdata/vnf/infra.csar diff --git a/catalog/pub/utils/toscaparser/testdata/vcpe/vbng.csar b/catalog/pub/utils/toscaparser/testdata/vnf/vbng.csar similarity index 100% rename from catalog/pub/utils/toscaparser/testdata/vcpe/vbng.csar rename to catalog/pub/utils/toscaparser/testdata/vnf/vbng.csar diff --git a/catalog/pub/utils/toscaparser/testdata/vcpe/vbrgemu.csar b/catalog/pub/utils/toscaparser/testdata/vnf/vbrgemu.csar similarity index 100% rename from catalog/pub/utils/toscaparser/testdata/vcpe/vbrgemu.csar rename to catalog/pub/utils/toscaparser/testdata/vnf/vbrgemu.csar diff --git a/catalog/pub/utils/toscaparser/testdata/vcpe/vgmux.csar b/catalog/pub/utils/toscaparser/testdata/vnf/vgmux.csar similarity index 100% rename from catalog/pub/utils/toscaparser/testdata/vcpe/vgmux.csar rename to catalog/pub/utils/toscaparser/testdata/vnf/vgmux.csar diff --git a/catalog/pub/utils/toscaparser/testdata/vcpe/vgw.csar b/catalog/pub/utils/toscaparser/testdata/vnf/vgw.csar similarity index 100% rename from catalog/pub/utils/toscaparser/testdata/vcpe/vgw.csar rename to catalog/pub/utils/toscaparser/testdata/vnf/vgw.csar diff --git a/catalog/pub/utils/toscaparser/tests.py b/catalog/pub/utils/toscaparser/tests.py index 296cb15f..36ceb2a3 100644 --- a/catalog/pub/utils/toscaparser/tests.py +++ b/catalog/pub/utils/toscaparser/tests.py @@ -17,7 +17,7 @@ import logging from django.test import TestCase -from catalog.pub.utils.toscaparser import parse_vnfd, parse_pnfd +from catalog.pub.utils.toscaparser import parse_vnfd, parse_pnfd, parse_nsd logger = logging.getLogger(__name__) @@ -30,17 +30,23 @@ class TestToscaparser(TestCase): pass def test_nsd_parse(self): - # csar_path = os.path.dirname(os.path.abspath(__file__)) + "/testdata/resource-ZteMmeFixVl-csar.csar" - # input_parameters = [{"value": "111111", "key": "sdncontroller"}] - # logger.debug("csar_path:%s", csar_path) - # vnfd_json = parse_vnfd(csar_path, input_parameters) - # metadata = json.loads(vnfd_json).get("metadata") - # self.assertEqual("ZTE-MME-FIX-VL", metadata.get("name", "")) - # TODO - pass + ran_csar = os.path.dirname(os.path.abspath(__file__)) + "/testdata/ns/ran.csar" + nsd_json = parse_nsd(ran_csar) + metadata = json.loads(nsd_json).get("metadata") + self.assertEqual("RAN-NS", metadata.get("template_name", "")) + + pnf_csar = os.path.dirname(os.path.abspath(__file__)) + "/testdata/pnf/ran-du.csar" + nsd_json = parse_nsd(pnf_csar) + metadata = json.loads(nsd_json).get("metadata") + self.assertNotEqual("RAN-NS", metadata.get("template_name", "")) + + pnf_csar = os.path.dirname(os.path.abspath(__file__)) + "/testdata/vnf/vgw.csar" + nsd_json = parse_nsd(pnf_csar) + metadata = json.loads(nsd_json).get("metadata") + self.assertNotEqual("RAN-NS", metadata.get("template_name", "")) - def test_vcpe_parse(self): - csar_path = os.path.dirname(os.path.abspath(__file__)) + "/testdata/vcpe" + def test_vnf_parse(self): + csar_path = os.path.dirname(os.path.abspath(__file__)) + "/testdata/vnf" input_parameters = [{"value": "222222", "key": "sdncontroller"}] vcpe = ["infra", "vbng", "vbrgemu", "vgmux", "vgw"] for vcpe_part in vcpe: @@ -54,6 +60,5 @@ class TestToscaparser(TestCase): def test_pnfd_parse(self): csar_path = os.path.dirname(os.path.abspath(__file__)) + "/testdata/pnf/ran-du.csar" pnfd_json = parse_pnfd(csar_path) - print pnfd_json metadata = json.loads(pnfd_json).get("metadata") self.assertEqual("RAN_DU", metadata.get("template_name", "")) diff --git a/catalog/pub/utils/toscaparser/vnfdmodel.py b/catalog/pub/utils/toscaparser/vnfdmodel.py index c85b7d1b..135963b2 100644 --- a/catalog/pub/utils/toscaparser/vnfdmodel.py +++ b/catalog/pub/utils/toscaparser/vnfdmodel.py @@ -15,11 +15,14 @@ import functools import logging import os -from catalog.pub.utils.toscaparser import EtsiNsdInfoModel +from catalog.pub.utils.toscaparser.basemodel import BaseInfoModel logger = logging.getLogger(__name__) +SECTIONS = (VDU_COMPUTE_TYPE, VNF_VL_TYPE, VDU_CP_TYPE, VDU_STORAGE_TYPE) = \ + ('tosca.nodes.nfv.Vdu.Compute', 'tosca.nodes.nfv.VnfVirtualLink', 'tosca.nodes.nfv.VduCp', 'tosca.nodes.nfv.Vdu.VirtualStorage') -class EtsiVnfdInfoModel(EtsiNsdInfoModel): + +class EtsiVnfdInfoModel(BaseInfoModel): def __init__(self, path, params): super(EtsiVnfdInfoModel, self).__init__(path, params) @@ -32,148 +35,33 @@ class EtsiVnfdInfoModel(EtsiNsdInfoModel): nodeTemplates = map(functools.partial(self.buildNode, tosca=tosca), tosca.nodetemplates) node_types = tosca.topology_template.custom_defs - logger.error("customdefs:%s", node_types) - self.basepath = self._get_base_path(tosca) - self.services = self._get_all_services(nodeTemplates) - self.vcloud = self._get_all_vcloud(nodeTemplates) - self.vcenter = self._get_all_vcenter(nodeTemplates) - self.image_files = self._get_all_image_file(nodeTemplates) - self.local_storages = self._get_all_local_storage(nodeTemplates) - self.volume_storages = self._get_all_volume_storage(nodeTemplates) + self.basepath = self.get_base_path(tosca) + self.volume_storages = self._get_all_volume_storage(nodeTemplates, node_types) self.vdus = self._get_all_vdu(nodeTemplates, node_types) - self.vls = self.get_all_vl(nodeTemplates, node_types) - logger.debug("vls:%s", self.vls) - self.cps = self.get_all_cp(nodeTemplates, node_types) - self.plugins = self.get_all_plugin(nodeTemplates) - self.routers = self.get_all_router(nodeTemplates) - self.server_groups = self.get_all_server_group(tosca.topology_template.groups) - self.element_groups = self._get_all_element_group(tosca.topology_template.groups) - self.policies = self._get_policies(tosca.topology_template.policies) - self.vnf_exposed = self.get_all_endpoint_exposed(tosca.topology_template) - self.vnf_flavours = self.get_all_flavour(tosca.topology_template.groups) - - def _get_base_path(self, tosca): - fpath, fname = os.path.split(tosca.path) - return fpath - - def _get_all_services(self, nodeTemplates): - ret = [] - for node in nodeTemplates: - if self.isService(node): - service = {} - service['serviceId'] = node['name'] - if 'description' in node: - service['description'] = node['description'] - service['properties'] = node['properties'] - service['dependencies'] = map(lambda x: self.get_requirement_node_name(x), - self.getNodeDependencys(node)) - service['networks'] = map(lambda x: self.get_requirement_node_name(x), self.getVirtualLinks(node)) - - ret.append(service) - return ret + self.vls = self._get_all_vl(nodeTemplates, node_types) + self.cps = self._get_all_cp(nodeTemplates, node_types) + self.vnf_exposed = self._get_all_endpoint_exposed(tosca.topology_template) - def _get_all_vcloud(self, nodeTemplates): + def _get_all_volume_storage(self, nodeTemplates, node_types): rets = [] for node in nodeTemplates: - if self._isVcloud(node): - ret = {} - if 'vdc_name' in node['properties']: - ret['vdc_name'] = node['properties']['vdc_name'] - else: - ret['vdc_name'] = "" - if 'storage_clusters' in node['properties']: - ret['storage_clusters'] = node['properties']['storage_clusters'] - else: - ret['storage_clusters'] = [] - - rets.append(ret) - return rets - - def _isVcloud(self, node): - return node['nodeType'].upper().find('.VCLOUD.') >= 0 or node['nodeType'].upper().endswith('.VCLOUD') - - def _get_all_vcenter(self, nodeTemplates): - rets = [] - for node in nodeTemplates: - if self._isVcenter(node): - ret = {} - if 'compute_clusters' in node['properties']: - ret['compute_clusters'] = node['properties']['compute_clusters'] - else: - ret['compute_clusters'] = [] - if 'storage_clusters' in node['properties']: - ret['storage_clusters'] = node['properties']['storage_clusters'] - else: - ret['storage_clusters'] = [] - if 'network_clusters' in node['properties']: - ret['network_clusters'] = node['properties']['network_clusters'] - else: - ret['network_clusters'] = [] - - rets.append(ret) - return rets - - def _isVcenter(self, node): - return node['nodeType'].upper().find('.VCENTER.') >= 0 or node['nodeType'].upper().endswith('.VCENTER') - - def _get_all_image_file(self, nodeTemplates): - rets = [] - for node in nodeTemplates: - if self._isImageFile(node): - ret = {} - ret['image_file_id'] = node['name'] - if 'description' in node: - ret['description'] = node['description'] - ret['properties'] = node['properties'] - - rets.append(ret) - return rets - - def _isImageFile(self, node): - return node['nodeType'].upper().find('.IMAGEFILE.') >= 0 or node['nodeType'].upper().endswith('.IMAGEFILE') - - def _get_all_local_storage(self, nodeTemplates): - rets = [] - for node in nodeTemplates: - if self._isLocalStorage(node): - ret = {} - ret['local_storage_id'] = node['name'] - if 'description' in node: - ret['description'] = node['description'] - ret['properties'] = node['properties'] - - rets.append(ret) - return rets - - def _isLocalStorage(self, node): - return node['nodeType'].upper().find('.LOCALSTORAGE.') >= 0 or node['nodeType'].upper().endswith( - '.LOCALSTORAGE') - - def _get_all_volume_storage(self, nodeTemplates): - rets = [] - for node in nodeTemplates: - if self._isVolumeStorage(node): + if self.isNodeTypeX(node, node_types, VDU_STORAGE_TYPE): ret = {} ret['volume_storage_id'] = node['name'] if 'description' in node: ret['description'] = node['description'] ret['properties'] = node['properties'] - ret['image_file'] = map(lambda x: self.get_requirement_node_name(x), - self.getRequirementByName(node, 'image_file')) - + # image_file should be gotten form artifacts TODO + # ret['artifacts'] = self._build_artifacts(node) rets.append(ret) return rets - def _isVolumeStorage(self, node): - return node['nodeType'].upper().find('.VOLUMESTORAGE.') >= 0 or node['nodeType'].upper().endswith( - '.VOLUMESTORAGE') - def _get_all_vdu(self, nodeTemplates, node_types): rets = [] inject_files = [] for node in nodeTemplates: logger.error("nodeTemplates :%s", node) - if self.isVdu(node, node_types): + if self.isNodeTypeX(node, node_types, VDU_COMPUTE_TYPE): ret = {} ret['vdu_id'] = node['name'] ret['type'] = node['nodeType'] @@ -189,136 +77,128 @@ class EtsiVnfdInfoModel(EtsiNsdInfoModel): source_data = f.read() source_data_base64 = source_data.encode("base64") inject_file["source_data_base64"] = source_data_base64 - - local_storages = self.getRequirementByName(node, 'local_storage') - ret['local_storages'] = map(lambda x: self.get_requirement_node_name(x), local_storages) - volume_storages = self.getRequirementByName(node, 'volume_storage') - ret['volume_storages'] = map(functools.partial(self._trans_volume_storage), volume_storages) + virtual_storages = self.getRequirementByName(node, 'virtual_storage') + ret['virtual_storages'] = map(functools.partial(self._trans_virtual_storage), virtual_storages) ret['dependencies'] = map(lambda x: self.get_requirement_node_name(x), self.getNodeDependencys(node)) - virtual_compute = self.getCapabilityByName(node, 'virtual_compute') if virtual_compute is not None and 'properties' in virtual_compute: ret['virtual_compute'] = virtual_compute['properties'] - - ret['vls'] = self.get_linked_vl_ids(node, nodeTemplates) - - scalable = self.getCapabilityByName(node, 'scalable') - if scalable is not None and 'properties' in scalable: - ret['scalable'] = scalable['properties'] - - ret['cps'] = self.getVirtalBindingCpIds(node, nodeTemplates) - ret['artifacts'] = self._build_artifacts(node) - + ret['vls'] = self._get_linked_vl_ids(node, nodeTemplates) + ret['cps'] = self._get_virtal_binding_cp_ids(node, nodeTemplates) + ret['artifacts'] = self.build_artifacts(node) rets.append(ret) logger.debug("rets:%s", rets) return rets - def get_node_image_file(self, node): - rets = map(lambda x: self.get_requirement_node_name(x), self.getRequirementByName(node, 'guest_os')) - if len(rets) > 0: - return rets[0] - return "" - - def _trans_volume_storage(self, volume_storage): - if isinstance(volume_storage, str): - return {"volume_storage_id": volume_storage} + def _trans_virtual_storage(self, virtual_storage): + if isinstance(virtual_storage, str): + return {"vitual_storage_id": virtual_storage} else: ret = {} - ret['volume_storage_id'] = self.get_requirement_node_name(volume_storage) - if 'relationship' in volume_storage and 'properties' in volume_storage['relationship']: - if 'location' in volume_storage['relationship']['properties']: - ret['location'] = volume_storage['relationship']['properties']['location'] - if 'device' in volume_storage['relationship']['properties']: - ret['device'] = volume_storage['relationship']['properties']['device'] - + ret['vitual_storage_id'] = self.get_requirement_node_name(virtual_storage) return ret - def get_linked_vl_ids(self, node, node_templates): + def _get_linked_vl_ids(self, node, node_templates): vl_ids = [] - cps = self.getVirtalBindingCps(node, node_templates) + cps = self._get_virtal_binding_cps(node, node_templates) for cp in cps: - vl_reqs = self.getVirtualLinks(cp) + vl_reqs = self.getRequirementByName(cp, 'virtual_link') for vl_req in vl_reqs: vl_ids.append(self.get_requirement_node_name(vl_req)) return vl_ids - def _build_artifacts(self, node): - rets = [] - if 'artifacts' in node and len(node['artifacts']) > 0: - artifacts = node['artifacts'] - for name, value in artifacts.items(): - ret = {} - if isinstance(value, dict): - ret['artifact_name'] = name - ret['type'] = value.get('type', '') - ret['file'] = value.get('file', '') - ret['repository'] = value.get('repository', '') - ret['deploy_path'] = value.get('deploy_path', '') - else: - ret['artifact_name'] = name - ret['type'] = '' - ret['file'] = value - ret['repository'] = '' - ret['deploy_path'] = '' - rets.append(ret) - return rets + def _get_virtal_binding_cp_ids(self, node, nodeTemplates): + return map(lambda x: x['name'], self._get_virtal_binding_cps(node, nodeTemplates)) + + def _get_virtal_binding_cps(self, node, nodeTemplates): + cps = [] + for tmpnode in nodeTemplates: + if 'requirements' in tmpnode: + for item in tmpnode['requirements']: + for key, value in item.items(): + if key.upper().startswith('VIRTUAL_BINDING'): + req_node_name = self.get_requirement_node_name(value) + if req_node_name is not None and req_node_name == node['name']: + cps.append(tmpnode) + return cps - def get_all_cp(self, nodeTemplates, node_types): + def _get_all_vl(self, nodeTemplates, node_types): + vls = [] + for node in nodeTemplates: + if self.isNodeTypeX(node, node_types, VNF_VL_TYPE): + vl = dict() + vl['vl_id'] = node['name'] + vl['description'] = node['description'] + vl['properties'] = node['properties'] + vls.append(vl) + return vls + + def _get_all_cp(self, nodeTemplates, node_types): cps = [] for node in nodeTemplates: - if self.isCp(node, node_types): + if self.isNodeTypeX(node, node_types, VDU_CP_TYPE): cp = {} cp['cp_id'] = node['name'] cp['cpd_id'] = node['name'] cp['description'] = node['description'] cp['properties'] = node['properties'] - cp['vl_id'] = self.get_node_vl_id(node) - cp['vdu_id'] = self.get_node_vdu_id(node) - vls = self.buil_cp_vls(node) + cp['vl_id'] = self._get_node_vl_id(node) + cp['vdu_id'] = self._get_node_vdu_id(node) + vls = self._buil_cp_vls(node) if len(vls) > 1: cp['vls'] = vls cps.append(cp) return cps - def get_all_plugin(self, node_templates): - plugins = [] - for node in node_templates: - if self._isPlugin(node): - plugin = {} - plugin['plugin_id'] = node['name'] - plugin['description'] = node['description'] - plugin['properties'] = node['properties'] - if 'interfaces' in node: - plugin['interfaces'] = node['interfaces'] - - plugins.append(plugin) - return plugins - - def _isPlugin(self, node): - return node['nodeType'].lower().find('.plugin.') >= 0 or node['nodeType'].lower().endswith('.plugin') - - def _get_all_element_group(self, groups): - rets = [] - for group in groups: - if self._isVnfdElementGroup(group): - ret = {} - ret['group_id'] = group.name - ret['description'] = group.description - if 'properties' in group.tpl: - ret['properties'] = group.tpl['properties'] - ret['members'] = group.members - rets.append(ret) - return rets - - def _isVnfdElementGroup(self, group): - return group.type.upper().find('.VNFDELEMENTGROUP.') >= 0 or group.type.upper().endswith('.VNFDELEMENTGROUP') + def _get_node_vdu_id(self, node): + vdu_ids = map(lambda x: self.get_requirement_node_name(x), self.getRequirementByName(node, 'virtual_binding')) + if len(vdu_ids) > 0: + return vdu_ids[0] + return "" - def _get_policies(self, top_policies): - policies = [] - scaling_policies = self.get_scaling_policies(top_policies) - healing_policies = self.get_healing_policies(top_policies) - policies.append({"scaling": scaling_policies, 'healing': healing_policies}) - return policies + def _get_node_vl_id(self, node): + vl_ids = map(lambda x: self.get_requirement_node_name(x), self.getRequirementByName(node, 'virtual_link')) + if len(vl_ids) > 0: + return vl_ids[0] + return "" - def get_healing_policies(self, top_policies): - return self.get_policies_by_keyword(top_policies, '.HEALING') + def _buil_cp_vls(self, node): + return map(lambda x: self._build_cp_vl(x), self.getRequirementByName(node, 'virtual_link')) + + def _build_cp_vl(self, req): + cp_vl = {} + cp_vl['vl_id'] = self.get_prop_from_obj(req, 'node') + relationship = self.get_prop_from_obj(req, 'relationship') + if relationship is not None: + properties = self.get_prop_from_obj(relationship, 'properties') + if properties is not None and isinstance(properties, dict): + for key, value in properties.items(): + cp_vl[key] = value + return cp_vl + + def _get_all_endpoint_exposed(self, topo_tpl): + if 'substitution_mappings' in topo_tpl.tpl: + external_cps = self._get_external_cps(topo_tpl.tpl['substitution_mappings']) + forward_cps = self._get_forward_cps(topo_tpl.tpl['substitution_mappings']) + return {"external_cps": external_cps, "forward_cps": forward_cps} + return {} + + def _get_external_cps(self, subs_mappings): + external_cps = [] + if 'requirements' in subs_mappings: + for key, value in subs_mappings['requirements'].items(): + if isinstance(value, list) and len(value) > 0: + external_cps.append({"key_name": key, "cpd_id": value[0]}) + else: + external_cps.append({"key_name": key, "cpd_id": value}) + return external_cps + + def _get_forward_cps(self, subs_mappings): + forward_cps = [] + if 'capabilities' in subs_mappings: + for key, value in subs_mappings['capabilities'].items(): + if isinstance(value, list) and len(value) > 0: + forward_cps.append({"key_name": key, "cpd_id": value[0]}) + else: + forward_cps.append({"key_name": key, "cpd_id": value}) + return forward_cps -- 2.16.6