X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FRO.git;a=blobdiff_plain;f=RO%2Fosm_ro%2Fnfvo.py;h=e346699af2f783e1c656db73cdeb687ed486d2dd;hp=4f5ba797af5f5440245cc278e6b8dafaddea0a30;hb=274bfc7019af82dac67bbbeb121ef45739a45e4e;hpb=1d2f2609c00490a2b25ffedfc01ff97bc3ed571d diff --git a/RO/osm_ro/nfvo.py b/RO/osm_ro/nfvo.py index 4f5ba797..e346699a 100644 --- a/RO/osm_ro/nfvo.py +++ b/RO/osm_ro/nfvo.py @@ -29,12 +29,17 @@ __date__ ="$16-sep-2014 22:05:01$" # import imp import json +import string import yaml +from random import choice as random_choice from osm_ro import utils from osm_ro.utils import deprecated from osm_ro.vim_thread import vim_thread import osm_ro.console_proxy_thread as cli -from osm_ro import vimconn +from osm_ro_plugin.vim_dummy import VimDummyConnector +from osm_ro_plugin.sdn_dummy import SdnDummyConnector +from osm_ro_plugin.sdn_failing import SdnFailingConnector +from osm_ro_plugin import vimconn, sdnconn import logging import collections import math @@ -44,13 +49,7 @@ from osm_ro.db_base import db_base_Exception from osm_ro import nfvo_db from threading import Lock import time as t -# TODO py3 BEGIN from osm_ro.sdn import Sdn, SdnException as ovimException -# from lib_osm_openvim.ovim import ovimException -# from unittest.mock import MagicMock -# class ovimException(Exception): -# pass -# TODO py3 END from Crypto.PublicKey import RSA @@ -62,9 +61,6 @@ from pkg_resources import iter_entry_points # WIM -from .wim import sdnconn -from .wim.wimconn_fake import FakeConnector -from .wim.failing_connector import FailingConnector from .http_tools import errors as httperrors from .wim.engine import WimEngine from .wim.persistence import WimPersistence @@ -100,6 +96,7 @@ last_task_id = 0.0 db = None db_lock = Lock() +worker_id = None class NfvoException(httperrors.HttpMappedError): """Common Class for NFVO errors""" @@ -113,12 +110,12 @@ def _load_plugin(name, type="vim"): except Exception as e: logger.critical("Cannot load osm_{}: {}".format(name, e)) if name: - plugins[name] = FailingConnector("Cannot load osm_{}: {}".format(name, e)) + plugins[name] = SdnFailingConnector("Cannot load osm_{}: {}".format(name, e)) if name and name not in plugins: error_text = "Cannot load a module for {t} type '{n}'. The plugin 'osm_{n}' has not been" \ " registered".format(t=type, n=name) logger.critical(error_text) - plugins[name] = FailingConnector(error_text) + plugins[name] = SdnFailingConnector(error_text) # raise NfvoException("Cannot load a module for {t} type '{n}'. The plugin 'osm_{n}' has not been registered". # format(t=type, n=name), httperrors.Bad_Request) @@ -144,20 +141,31 @@ def new_task(name, params, depends=None): def is_task_id(id): return True if id[:5] == "TASK-" else False - -def get_non_used_vim_name(datacenter_name, datacenter_id, tenant_name, tenant_id): - name = datacenter_name[:16] - if name not in vim_threads["names"]: - vim_threads["names"].append(name) - return name - if tenant_name: - name = datacenter_name[:16] + "." + tenant_name[:16] - if name not in vim_threads["names"]: - vim_threads["names"].append(name) - return name - name = datacenter_id - vim_threads["names"].append(name) - return name +def get_process_id(): + """ + Obtain a unique ID for this process. If running from inside docker, it will get docker ID. If not it + will provide a random one + :return: Obtained ID + """ + # Try getting docker id. If fails, get pid + try: + with open("/proc/self/cgroup", "r") as f: + for text_id_ in f.readlines(): + if "docker/" not in text_id_: + continue + _, _, text_id = text_id_.rpartition("/") + text_id = text_id.replace("\n", "")[:12] + if text_id: + return text_id + except Exception: + pass + # Return a random id + return "".join(random_choice("0123456789abcdef") for _ in range(12)) + +def get_non_used_vim_name(datacenter_name, datacenter_id): + return "{}:{}:{}".format( + worker_id[:12], datacenter_id.replace("-", "")[:32], datacenter_name[:16] + ) # -- Move def get_non_used_wim_name(wim_name, wim_id, tenant_name, tenant_id): @@ -175,7 +183,7 @@ def get_non_used_wim_name(wim_name, wim_id, tenant_name, tenant_id): def start_service(mydb, persistence=None, wim=None): - global db, global_config, plugins, ovim + global db, global_config, plugins, ovim, worker_id db = nfvo_db.nfvo_db(lock=db_lock) mydb.lock = db_lock db.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name']) @@ -183,8 +191,11 @@ def start_service(mydb, persistence=None, wim=None): persistence = persistence or WimPersistence(db) try: - if "rosdn_fake" not in plugins: - plugins["rosdn_fake"] = FakeConnector + worker_id = get_process_id() + if "rosdn_dummy" not in plugins: + plugins["rosdn_dummy"] = SdnDummyConnector + if "rovim_dummy" not in plugins: + plugins["rovim_dummy"] = VimDummyConnector # starts ovim library ovim = Sdn(db, plugins) @@ -221,14 +232,14 @@ def start_service(mydb, persistence=None, wim=None): try: #if not tenant: # return -httperrors.Bad_Request, "You must provide a valid tenant name or uuid for VIM %s" % ( vim["type"]) - myvim = plugins[plugin_name].vimconnector( + myvim = plugins[plugin_name]( uuid=vim['datacenter_id'], name=vim['datacenter_name'], tenant_id=vim['vim_tenant_id'], tenant_name=vim['vim_tenant_name'], url=vim['vim_url'], url_admin=vim['vim_url_admin'], user=vim['user'], passwd=vim['passwd'], config=extra, persistent_info=vim_persistent_info[thread_id] ) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: myvim = e logger.error("Cannot launch thread for VIM {} '{}': {}".format(vim['datacenter_name'], vim['datacenter_id'], e)) @@ -237,8 +248,7 @@ def start_service(mydb, persistence=None, wim=None): vim['datacenter_id'], e)) # raise NfvoException("Error at VIM {}; {}: {}".format(vim["type"], type(e).__name__, e), # httperrors.Internal_Server_Error) - thread_name = get_non_used_vim_name(vim['datacenter_name'], vim['datacenter_id'], vim['vim_tenant_name'], - vim['vim_tenant_id']) + thread_name = get_non_used_vim_name(vim['datacenter_name'], vim['datacenter_id']) new_thread = vim_thread(task_lock, plugins, thread_name, None, vim['datacenter_tenant_id'], db=db) new_thread.start() @@ -252,7 +262,7 @@ def start_service(mydb, persistence=None, wim=None): _load_plugin(plugin_name, type="sdn") thread_id = wim['uuid'] - thread_name = get_non_used_vim_name(wim['name'], wim['uuid'], wim['uuid'], None) + thread_name = get_non_used_vim_name(wim['name'], wim['uuid']) new_thread = vim_thread(task_lock, plugins, thread_name, wim['uuid'], None, db=db) new_thread.start() vim_threads["running"][thread_id] = new_thread @@ -421,7 +431,7 @@ def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, da persistent_info = {} #if not tenant: # return -httperrors.Bad_Request, "You must provide a valid tenant name or uuid for VIM %s" % ( vim["type"]) - vim_dict[vim['datacenter_id']] = plugins[plugin_name].vimconnector( + vim_dict[vim['datacenter_id']] = plugins[plugin_name]( uuid=vim['datacenter_id'], name=vim['datacenter_name'], tenant_id=vim.get('vim_tenant_id',vim_tenant), tenant_name=vim.get('vim_tenant_name',vim_tenant_name), @@ -434,7 +444,7 @@ def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, da logger.error("Error at VIM {}; {}: {}".format(vim["type"], type(e).__name__, str(e))) continue http_code = httperrors.Internal_Server_Error - if isinstance(e, vimconn.vimconnException): + if isinstance(e, vimconn.VimConnException): http_code = e.http_code raise NfvoException("Error at VIM {}; {}: {}".format(vim["type"], type(e).__name__, str(e)), http_code) return vim_dict @@ -464,7 +474,7 @@ def rollback(mydb, vims, rollback_list): vim.delete_network(item["uuid"]) elif item["what"]=="vm": vim.delete_vminstance(item["uuid"]) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: logger.error("Error in rollback. Not possible to delete VIM %s '%s'. Message: %s", item['what'], item["uuid"], str(e)) undeleted_items.append("{} {} from VIM {}".format(item['what'], item["uuid"], vim["name"])) except db_base_Exception as e: @@ -480,9 +490,9 @@ def rollback(mydb, vims, rollback_list): logger.error("Error in rollback. Not possible to delete %s '%s' from DB. Message: %s", item['what'], item["uuid"], str(e)) undeleted_items.append("{} '{}'".format(item['what'], item["uuid"])) if len(undeleted_items)==0: - return True," Rollback successful." + return True, "Rollback successful." else: - return False," Rollback fails to delete: " + str(undeleted_items) + return False, "Rollback fails to delete: " + str(undeleted_items) def check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1): @@ -641,14 +651,14 @@ def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vi vim_images = vim.get_image_list(filter_dict) #logger.debug('>>>>>>>> VIM images: %s', str(vim_images)) if len(vim_images) > 1: - raise vimconn.vimconnException("More than one candidate VIM image found for filter: {}".format(str(filter_dict)), httperrors.Conflict) + raise vimconn.VimConnException("More than one candidate VIM image found for filter: {}".format(str(filter_dict)), httperrors.Conflict) elif len(vim_images) == 0: - raise vimconn.vimconnNotFoundException("Image not found at VIM with filter: '{}'".format(str(filter_dict))) + raise vimconn.VimConnNotFoundException("Image not found at VIM with filter: '{}'".format(str(filter_dict))) else: #logger.debug('>>>>>>>> VIM image 0: %s', str(vim_images[0])) image_vim_id = vim_images[0]['id'] - except vimconn.vimconnNotFoundException as e: + except vimconn.VimConnNotFoundException as e: #Create the image in VIM only if image_dict['location'] or image_dict['new_location'] is not None try: #image_dict['location']=image_dict.get('new_location') if image_dict['location'] is None @@ -658,15 +668,15 @@ def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vi image_created="true" else: #If we reach this point, then the image has image name, and optionally checksum, and could not be found - raise vimconn.vimconnException(str(e)) - except vimconn.vimconnException as e: + raise vimconn.VimConnException(str(e)) + except vimconn.VimConnException as e: if return_on_error: logger.error("Error creating image at VIM '%s': %s", vim["name"], str(e)) raise image_vim_id = None logger.warn("Error creating image at VIM '%s': %s", vim["name"], str(e)) continue - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: if return_on_error: logger.error("Error contacting VIM to know if the image exists at VIM: %s", str(e)) raise @@ -804,7 +814,7 @@ def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_ try: vim.get_flavor(flavor_vim_id) continue #flavor exist - except vimconn.vimconnException: + except vimconn.VimConnException: pass #create flavor at vim logger.debug("nfvo.create_or_use_flavor() adding flavor to VIM %s", vim["name"]) @@ -812,14 +822,14 @@ def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_ flavor_vim_id = None flavor_vim_id=vim.get_flavor_id_from_data(flavor_dict) flavor_created="false" - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: pass try: if not flavor_vim_id: flavor_vim_id = vim.new_flavor(flavor_dict) rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"flavor","uuid":flavor_vim_id}) flavor_created="true" - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: if return_on_error: logger.error("Error creating flavor at VIM %s: %s.", vim["name"], str(e)) raise @@ -977,6 +987,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor): # table nets (internal-vld) net_id2uuid = {} # for mapping interface with network + net_id2index = {} # for mapping interface with network for vld in vnfd.get("internal-vld").values(): net_uuid = str(uuid4()) uuid_list.append(net_uuid) @@ -989,6 +1000,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor): "type": "bridge", # TODO adjust depending on connection point type } net_id2uuid[vld.get("id")] = net_uuid + net_id2index[vld.get("id")] = len(db_nets) db_nets.append(db_net) # ip-profile, link db_ip_profile with db_sce_net if vld.get("ip-profile-ref"): @@ -1181,7 +1193,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor): raise KeyError() if vdu_id in vdu_id2cp_name: - vdu_id2cp_name[vdu_id] = None # more than two connecdtion point for this VDU + vdu_id2cp_name[vdu_id] = None # more than two connection point for this VDU else: vdu_id2cp_name[vdu_id] = db_interface["external_name"] @@ -1216,6 +1228,10 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor): if not icp: raise KeyError("is not referenced by any 'internal-vld'") + # set network type as data + if iface.get("virtual-interface") and iface["virtual-interface"].get("type") in \ + ("SR-IOV", "PCI-PASSTHROUGH"): + db_nets[net_id2index[icp_vld.get("id")]]["type"] = "data" db_interface["net_id"] = net_id2uuid[icp_vld.get("id")] if str(icp_descriptor.get("port-security-enabled")).lower() == "false": db_interface["port_security"] = 0 @@ -1532,7 +1548,7 @@ def new_vnf(mydb, tenant_id, vnf_descriptor): # Step 8. Adding the VNF to the NFVO DB vnf_id = mydb.new_vnf_as_a_whole(tenant_id,vnf_name,vnf_descriptor,VNFCDict) return vnf_id - except (db_base_Exception, vimconn.vimconnException, KeyError) as e: + except (db_base_Exception, vimconn.VimConnException, KeyError) as e: _, message = rollback(mydb, vims, rollback_list) if isinstance(e, db_base_Exception): error_text = "Exception at database" @@ -1668,7 +1684,7 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor): # Step 8. Adding the VNF to the NFVO DB vnf_id = mydb.new_vnf_as_a_whole2(tenant_id,vnf_name,vnf_descriptor,VNFCDict) return vnf_id - except (db_base_Exception, vimconn.vimconnException, KeyError) as e: + except (db_base_Exception, vimconn.VimConnException, KeyError) as e: _, message = rollback(mydb, vims, rollback_list) if isinstance(e, db_base_Exception): error_text = "Exception at database" @@ -1797,10 +1813,10 @@ def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None): continue try: myvim.delete_flavor(flavor_vim["vim_id"]) - except vimconn.vimconnNotFoundException: + except vimconn.VimConnNotFoundException: logger.warn("VIM flavor %s not exist at datacenter %s", flavor_vim["vim_id"], flavor_vim["datacenter_vim_id"] ) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: logger.error("Not possible to delete VIM flavor %s from datacenter %s: %s %s", flavor_vim["vim_id"], flavor_vim["datacenter_vim_id"], type(e).__name__, str(e)) undeletedItems.append("flavor {} from VIM {}".format(flavor_vim["vim_id"], @@ -1830,9 +1846,9 @@ def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None): myvim=vims[ image_vim["datacenter_id"] ] try: myvim.delete_image(image_vim["vim_id"]) - except vimconn.vimconnNotFoundException as e: + except vimconn.VimConnNotFoundException as e: logger.warn("VIM image %s not exist at datacenter %s", image_vim["vim_id"], image_vim["datacenter_id"] ) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: logger.error("Not possible to delete VIM image %s from datacenter %s: %s %s", image_vim["vim_id"], image_vim["datacenter_id"], type(e).__name__, str(e)) undeletedItems.append("image {} from VIM {}".format(image_vim["vim_id"], image_vim["datacenter_id"] )) @@ -1894,7 +1910,7 @@ def get_hosts(mydb, nfvo_tenant_id): #print 'datacenters '+ json.dumps(datacenter, indent=4) return datacenter - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: raise NfvoException("Not possible to get_host_list from VIM: {}".format(str(e)), e.http_code) @@ -2433,7 +2449,6 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor): elif vld.get("vim-network-name"): db_sce_net["vim_network_name"] = get_str(vld, "vim-network-name", 255) - # table sce_interfaces (vld:vnfd-connection-point-ref) for iface in vld.get("vnfd-connection-point-ref").values(): # Check if there are VDUs in the descriptor @@ -2447,7 +2462,7 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor): "'nsd':'constituent-vnfd'".format( str(nsd["id"]), str(vld["id"]), str(iface["member-vnf-index-ref"])), httperrors.Bad_Request) - + existing_ifaces = mydb.get_rows(SELECT=('i.uuid as uuid', 'i.type as iface_type'), FROM="interfaces as i join vms on i.vm_id=vms.uuid", WHERE={'vnf_id': vnf_index2vnf_uuid[vnf_index], @@ -2474,7 +2489,7 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor): "sce_net_id": sce_net_uuid, "interface_id": interface_uuid, "ip_address": iface_ip_address, - } + } db_sce_interfaces.append(db_sce_interface) if not db_sce_net["type"]: db_sce_net["type"] = "bridge" @@ -2860,7 +2875,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc instance_id = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_scenario_name, instance_scenario_description, scenarioDict) return mydb.get_instance_scenario(instance_id) - except (db_base_Exception, vimconn.vimconnException) as e: + except (db_base_Exception, vimconn.VimConnException) as e: _, message = rollback(mydb, vims, rollbackList) if isinstance(e, db_base_Exception): error_text = "Exception at database" @@ -2956,6 +2971,7 @@ def unify_cloud_config(cloud_config_preserve, cloud_config): def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_id=None): + global plugins datacenter_id = None datacenter_name = None thread = None @@ -2963,7 +2979,7 @@ def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_i if datacenter_tenant_id: thread_id = datacenter_tenant_id thread = vim_threads["running"].get(datacenter_tenant_id) - else: + if not thread: where_={"td.nfvo_tenant_id": tenant_id} if datacenter_id_name: if utils.check_valid_uuid(datacenter_id_name): @@ -2975,7 +2991,7 @@ def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_i if datacenter_tenant_id: where_["dt.uuid"] = datacenter_tenant_id datacenters = mydb.get_rows( - SELECT=("dt.uuid as datacenter_tenant_id",), + SELECT=("dt.uuid as datacenter_tenant_id, d.name as datacenter_name", "d.type as type"), FROM="datacenter_tenants as dt join tenants_datacenters as td on dt.uuid=td.datacenter_tenant_id " "join datacenters as d on d.uuid=dt.datacenter_id", WHERE=where_) @@ -2983,7 +2999,18 @@ def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_i raise NfvoException("More than one datacenters found, try to identify with uuid", httperrors.Conflict) elif datacenters: thread_id = datacenters[0]["datacenter_tenant_id"] + datacenter_name = datacenters[0]["datacenter_name"] thread = vim_threads["running"].get(thread_id) + if not thread: + datacenter_type = datacenters[0]["type"] + plugin_name = "rovim_" + datacenter_type + if plugin_name not in plugins: + _load_plugin(plugin_name, type="vim") + thread_name = get_non_used_vim_name(datacenter_name, datacenter_id) + thread = vim_thread(task_lock, plugins, thread_name, None, + thread_id, db=mydb) + thread.start() + vim_threads["running"][thread_id] = thread if not thread: raise NfvoException("datacenter '{}' not found".format(str(datacenter_id_name)), httperrors.Not_Found) return thread_id, thread @@ -3004,13 +3031,14 @@ def get_datacenter_uuid(mydb, tenant_id, datacenter_id_name): " dt on td.datacenter_tenant_id=dt.uuid" else: from_ = 'datacenters as d' - vimaccounts = mydb.get_rows(FROM=from_, SELECT=("d.uuid as uuid, d.name as name",), WHERE=WHERE_dict ) + vimaccounts = mydb.get_rows(FROM=from_, SELECT=("d.uuid as uuid", "d.name as name", "d.type as type"), + WHERE=WHERE_dict) if len(vimaccounts) == 0: raise NfvoException("datacenter '{}' not found".format(str(datacenter_id_name)), httperrors.Not_Found) - elif len(vimaccounts)>1: - #print "nfvo.datacenter_action() error. Several datacenters found" + elif len(vimaccounts) > 1: + # print "nfvo.datacenter_action() error. Several datacenters found" raise NfvoException("More than one datacenters found, try to identify with uuid", httperrors.Conflict) - return vimaccounts[0]["uuid"], vimaccounts[0]["name"] + return vimaccounts[0]["uuid"], vimaccounts[0] def get_datacenter_by_name_uuid(mydb, tenant_id, datacenter_id_name=None, **extra_filter): @@ -3205,12 +3233,12 @@ def create_instance(mydb, tenant_id, instance_dict): else: update(scenario_net['ip_profile'], ipprofile_db) - if 'provider-network' in net_instance_desc: - provider_network_db = net_instance_desc['provider-network'] - if 'provider-network' not in scenario_net: - scenario_net['provider-network'] = provider_network_db - else: - update(scenario_net['provider-network'], provider_network_db) + if net_instance_desc.get('provider-network'): + provider_network_db = net_instance_desc['provider-network'] + if 'provider_network' not in scenario_net: + scenario_net['provider_network'] = provider_network_db + else: + update(scenario_net['provider_network'], provider_network_db) for vdu_id, vdu_instance_desc in vnf_instance_desc.get("vdus", {}).items(): for scenario_vm in scenario_vnf['vms']: @@ -3287,28 +3315,6 @@ def create_instance(mydb, tenant_id, instance_dict): involved_datacenters.append(default_datacenter_id) target_wim_account = sce_net.get("wim_account", default_wim_account) - # --> WIM - # TODO: use this information during network creation - wim_account_id = wim_account_name = None - if len(involved_datacenters) > 1 and 'uuid' in sce_net: - if target_wim_account is None or target_wim_account is True: # automatic selection of WIM - # OBS: sce_net without uuid are used internally to VNFs - # and the assumption is that VNFs will not be split among - # different datacenters - wim_account = wim_engine.find_suitable_wim_account( - involved_datacenters, tenant_id) - wim_account_id = wim_account['uuid'] - wim_account_name = wim_account['name'] - wim_usage[sce_net['uuid']] = wim_account_id - elif isinstance(target_wim_account, str): # manual selection of WIM - wim_account.persist.get_wim_account_by(target_wim_account, tenant_id) - wim_account_id = wim_account['uuid'] - wim_account_name = wim_account['name'] - wim_usage[sce_net['uuid']] = wim_account_id - else: # not WIM usage - wim_usage[sce_net['uuid']] = False - # <-- WIM - descriptor_net = {} if instance_dict.get("networks"): if sce_net.get("uuid") in instance_dict["networks"]: @@ -3341,14 +3347,39 @@ def create_instance(mydb, tenant_id, instance_dict): ) if not target_instance_nets: raise NfvoException( - "Cannot find the target network at instance:networks[{}]:use-network".format(descriptor_net_name), - httperrors.Bad_Request) + "Cannot find the target network at instance:networks[{}]:use-network".format( + descriptor_net_name), httperrors.Bad_Request) else: use_network = target_instance_nets[0]["related"] if sce_net["external"]: number_mgmt_networks += 1 + # --> WIM + # TODO: use this information during network creation + wim_account_id = wim_account_name = None + if len(involved_datacenters) > 1 and 'uuid' in sce_net: + urls = [myvims[v].url for v in involved_datacenters] + if len(set(urls)) < 2: + wim_usage[sce_net['uuid']] = False + elif target_wim_account is None or target_wim_account is True: # automatic selection of WIM + # OBS: sce_net without uuid are used internally to VNFs + # and the assumption is that VNFs will not be split among + # different datacenters + wim_account = wim_engine.find_suitable_wim_account( + involved_datacenters, tenant_id) + wim_account_id = wim_account['uuid'] + wim_account_name = wim_account['name'] + wim_usage[sce_net['uuid']] = wim_account_id + elif isinstance(target_wim_account, str): # manual selection of WIM + wim_account.persist.get_wim_account_by(target_wim_account, tenant_id) + wim_account_id = wim_account['uuid'] + wim_account_name = wim_account['name'] + wim_usage[sce_net['uuid']] = wim_account_id + else: # not WIM usage + wim_usage[sce_net['uuid']] = False + # <-- WIM + for datacenter_id in involved_datacenters: netmap_use = None netmap_create = None @@ -3457,7 +3488,11 @@ def create_instance(mydb, tenant_id, instance_dict): "created": create_network, # TODO py3 "sdn": True, }) + task_wim_extra = {"params": [net_type, wim_account_name]} + # add sdn interfaces + if sce_net.get('provider_network') and sce_net['provider_network'].get("sdn-ports"): + task_wim_extra["sdn-ports"] = sce_net['provider_network'].get("sdn-ports") db_vim_action = { "instance_action_id": instance_action_id, "status": "SCHEDULED", @@ -3753,21 +3788,40 @@ def create_instance(mydb, tenant_id, instance_dict): returned_instance = mydb.get_instance_scenario(instance_uuid) returned_instance["action_id"] = instance_action_id return returned_instance - except (NfvoException, vimconn.vimconnException, sdnconn.SdnConnectorError, db_base_Exception) as e: - message = rollback(mydb, myvims, rollbackList) + except (NfvoException, vimconn.VimConnException, sdnconn.SdnConnectorError, db_base_Exception) as e: + _, message = rollback(mydb, myvims, rollbackList) if isinstance(e, db_base_Exception): error_text = "database Exception" - elif isinstance(e, vimconn.vimconnException): + elif isinstance(e, vimconn.VimConnException): error_text = "VIM Exception" elif isinstance(e, sdnconn.SdnConnectorError): error_text = "WIM Exception" else: - error_text = "Exception" - error_text += " {} {}. {}".format(type(e).__name__, str(e), message) + error_text = "Exception " + str(type(e).__name__) + error_text += " {}. {}".format(e, message) # logger.error("create_instance: %s", error_text) logger.exception(e) raise NfvoException(error_text, e.http_code) +def increment_ip_mac(ip_mac, vm_index=1): + if not isinstance(ip_mac, str): + return ip_mac + try: + # try with ipv4 look for last dot + i = ip_mac.rfind(".") + if i > 0: + i += 1 + return "{}{}".format(ip_mac[:i], int(ip_mac[i:]) + vm_index) + # try with ipv6 or mac look for last colon. Operate in hex + i = ip_mac.rfind(":") + if i > 0: + i += 1 + # format in hex, len can be 2 for mac or 4 for ipv6 + return ("{}{:0" + str(len(ip_mac) - i) + "x}").format(ip_mac[:i], int(ip_mac[i:], 16) + vm_index) + except: + pass + return None + def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList): default_datacenter_id = params["default_datacenter_id"] @@ -3793,6 +3847,7 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList): sce_net2wim_instance = params_out["sce_net2wim_instance"] vnf_net2instance = {} + vnf_net2wim_instance = {} # 2. Creating new nets (vnf internal nets) in the VIM" # For each vnf net, we create it and we add it to instanceNetlist. @@ -3840,6 +3895,7 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList): "created": True, # TODO py3 "sdn": True, }) + vnf_net2wim_instance[net_uuid] = sdn_net_id db_net = { "uuid": net_uuid, @@ -4076,7 +4132,7 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList): else: netDict['net_id'] = "TASK-{}".format(net2task_id[sce_vnf['uuid']][iface['net_id']]) instance_net_id = vnf_net2instance[sce_vnf['uuid']][iface['net_id']] - instance_wim_net_id = None + instance_wim_net_id = vnf_net2wim_instance.get(instance_net_id) task_depends_on.append(net2task_id[sce_vnf['uuid']][iface['net_id']]) # skip bridge ifaces not connected to any net if 'net_id' not in netDict or netDict['net_id'] == None: @@ -4124,16 +4180,11 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList): av_index = None for vm_index in range(0, vm.get('count', 1)): vm_name = myVMDict['name'] + "-" + str(vm_index+1) + vm_networks = deepcopy(myVMDict['networks']) task_params = (vm_name, myVMDict['description'], myVMDict.get('start', None), - myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'], cloud_config_vm, + myVMDict['imageRef'], myVMDict['flavorRef'], vm_networks, cloud_config_vm, myVMDict['disks'], av_index, vnf_availability_zones) - # put interface uuid back to scenario[vnfs][vms[[interfaces] - for net in myVMDict['networks']: - if "vim_id" in net: - for iface in vm['interfaces']: - if net["name"] == iface["internal_name"]: - iface["vim_id"] = net["vim_id"] - break + vm_uuid = str(uuid4()) uuid_list.append(vm_uuid) db_vm = { @@ -4147,28 +4198,32 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList): } db_instance_vms.append(db_vm) - iface_index = 0 - for db_vm_iface in db_vm_ifaces: + # put interface uuid back to scenario[vnfs][vms[[interfaces] + for net in vm_networks: + if "vim_id" in net: + for iface in vm['interfaces']: + if net["name"] == iface["internal_name"]: + iface["vim_id"] = net["vim_id"] + break + + if vm_index > 0: + if net.get("ip_address"): + net["ip_address"] = increment_ip_mac(net.get("ip_address"), vm_index) + if net.get("mac_address"): + net["mac_address"] = increment_ip_mac(net.get("mac_address"), vm_index) + + for iface_index, db_vm_iface in enumerate(db_vm_ifaces): iface_uuid = str(uuid4()) uuid_list.append(iface_uuid) db_vm_iface_instance = { "uuid": iface_uuid, - "instance_vm_id": vm_uuid + "instance_vm_id": vm_uuid, + "ip_address": vm_networks[iface_index].get("ip_address"), + "mac_address": vm_networks[iface_index].get("mac_address") } db_vm_iface_instance.update(db_vm_iface) - if db_vm_iface_instance.get("ip_address"): # increment ip_address - ip = db_vm_iface_instance.get("ip_address") - i = ip.rfind(".") - if i > 0: - try: - i += 1 - ip = ip[i:] + str(int(ip[:i]) + 1) - db_vm_iface_instance["ip_address"] = ip - except: - db_vm_iface_instance["ip_address"] = None db_instance_interfaces.append(db_vm_iface_instance) - myVMDict['networks'][iface_index]["uuid"] = iface_uuid - iface_index += 1 + vm_networks[iface_index]["uuid"] = iface_uuid db_vim_action = { "instance_action_id": instance_action_id, @@ -4609,7 +4664,7 @@ def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenan # try: # vm_dict.update(myvims[datacenter_key].refresh_vms_status(vm_list[datacenter_key]) ) # failed = False - # except vimconn.vimconnException as e: + # except vimconn.VimConnException as e: # logger.error("VIM exception %s %s", type(e).__name__, str(e)) # failed_message = str(e) # if failed: @@ -4671,7 +4726,7 @@ def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenan # try: # net_dict.update(myvims[datacenter_key].refresh_nets_status(net_list[datacenter_key]) ) # failed = False - # except vimconn.vimconnException as e: + # except vimconn.VimConnException as e: # logger.error("VIM exception %s %s", type(e).__name__, str(e)) # failed_message = str(e) # if failed: @@ -4795,6 +4850,16 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict): "extra": yaml.safe_dump({"params": vm_interfaces}, default_flow_style=True, width=256) } + # get affected instance_interfaces (deleted on cascade) to check if a wim_network must be updated + deleted_interfaces = mydb.get_rows( + SELECT=("instance_wim_net_id", ), + FROM="instance_interfaces", + WHERE={"instance_vm_id": vdu_id, "instance_wim_net_id<>": None}, + ) + for deleted_interface in deleted_interfaces: + db_vim_actions.append({"TO-UPDATE": {}, "WHERE": { + "item": "instance_wim_nets", "item_id": deleted_interface["instance_wim_net_id"]}}) + task_index += 1 db_vim_actions.append(db_vim_action) vm_result["deleted"].append(vdu_id) @@ -4815,7 +4880,6 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict): # TODO do the same for flavor and image when available task_depends_on = [] task_params = extra["params"] - task_params_networks = deepcopy(task_params[5]) for iface in task_params[5]: if iface["net_id"].startswith("TASK-"): if "." not in iface["net_id"]: @@ -4825,8 +4889,6 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict): iface["net_id"][5:]) else: task_depends_on.append(iface["net_id"][5:]) - if "mac_address" in iface: - del iface["mac_address"] vm_ifaces_to_clone = mydb.get_rows(FROM="instance_interfaces", WHERE={"instance_vm_id": target_vm["uuid"]}) for index in range(0, vdu_count): @@ -4853,26 +4915,26 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict): "uuid": iface_uuid, 'instance_vm_id': vm_uuid, "instance_net_id": vm_iface["instance_net_id"], + "instance_wim_net_id": vm_iface["instance_wim_net_id"], 'interface_id': vm_iface['interface_id'], 'type': vm_iface['type'], + 'model': vm_iface['model'], 'floating_ip': vm_iface['floating_ip'], 'port_security': vm_iface['port_security'] } db_instance_interfaces.append(db_vm_iface) + if db_vm_iface["instance_wim_net_id"]: + db_vim_actions.append({"TO-UPDATE": {}, "WHERE": { + "item": "instance_wim_nets", "item_id": db_vm_iface["instance_wim_net_id"]}}) task_params_copy = deepcopy(task_params) for iface in task_params_copy[5]: iface["uuid"] = iface2iface[iface["uuid"]] # increment ip_address - if "ip_address" in iface: - ip = iface.get("ip_address") - i = ip.rfind(".") - if i > 0: - try: - i += 1 - ip = ip[i:] + str(int(ip[:i]) + 1) - iface["ip_address"] = ip - except: - iface["ip_address"] = None + if iface.get("ip_address"): + iface["ip_address"] = increment_ip_mac(iface.get("ip_address"), index+1) + if iface.get("mac_address"): + iface["mac_address"] = increment_ip_mac(iface.get("mac_address"), index+1) + if vm_name: task_params_copy[0] = vm_name db_vim_action = { @@ -4994,7 +5056,7 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict): else: vm_result[ vm['uuid'] ] = {"vim_result": 200, "description": "ok", "name":vm['name']} vm_ok +=1 - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)} vm_error+=1 @@ -5085,6 +5147,15 @@ def new_datacenter(mydb, datacenter_descriptor): datacenter_type = datacenter_descriptor.get("type", "openvim"); # module_info = None + for url_field in ('vim_url', 'vim_url_admin'): + # It is common that users copy and paste the URL from the VIM website + # (example OpenStack), therefore a common mistake is to include blank + # characters at the end of the URL. Let's remove it and just in case, + # lets remove trailing slash as well. + url = datacenter_descriptor.get(url_field) + if url: + datacenter_descriptor[url_field] = url.strip(string.whitespace + '/') + # load plugin plugin_name = "rovim_" + datacenter_type if plugin_name not in plugins: @@ -5168,56 +5239,60 @@ def delete_datacenter(mydb, datacenter): def create_vim_account(mydb, nfvo_tenant, datacenter_id, name=None, vim_id=None, vim_tenant=None, vim_tenant_name=None, vim_username=None, vim_password=None, config=None): + global plugins # get datacenter info try: if not datacenter_id: if not vim_id: raise NfvoException("You must provide 'vim_id", http_code=httperrors.Bad_Request) datacenter_id = vim_id - datacenter_id, datacenter_name = get_datacenter_uuid(mydb, None, datacenter_id) + datacenter_id, datacenter = get_datacenter_uuid(mydb, None, datacenter_id) + datacenter_name = datacenter["name"] + datacenter_type = datacenter["type"] create_vim_tenant = True if not vim_tenant and not vim_tenant_name else False # get nfvo_tenant info tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', nfvo_tenant) - if vim_tenant_name==None: - vim_tenant_name=tenant_dict['name'] + if vim_tenant_name is None: + vim_tenant_name = tenant_dict['name'] - tenants_datacenter_dict={"nfvo_tenant_id":tenant_dict['uuid'], "datacenter_id":datacenter_id } + tenants_datacenter_dict = {"nfvo_tenant_id": tenant_dict['uuid'], "datacenter_id": datacenter_id} # #check that this association does not exist before # tenants_datacenters = mydb.get_rows(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict) # if len(tenants_datacenters)>0: - # raise NfvoException("datacenter '{}' and tenant'{}' are already attached".format(datacenter_id, tenant_dict['uuid']), httperrors.Conflict) + # raise NfvoException("datacenter '{}' and tenant'{}' are already attached".format( + # datacenter_id, tenant_dict['uuid']), httperrors.Conflict) - vim_tenant_id_exist_atdb=False + vim_tenant_id_exist_atdb = False if not create_vim_tenant: where_={"datacenter_id": datacenter_id} - if vim_tenant!=None: + if vim_tenant is not None: where_["vim_tenant_id"] = vim_tenant - if vim_tenant_name!=None: + if vim_tenant_name is not None: where_["vim_tenant_name"] = vim_tenant_name - #check if vim_tenant_id is already at database + # check if vim_tenant_id is already at database datacenter_tenants_dict = mydb.get_rows(FROM='datacenter_tenants', WHERE=where_) - if len(datacenter_tenants_dict)>=1: + if len(datacenter_tenants_dict) >= 1: datacenter_tenants_dict = datacenter_tenants_dict[0] - vim_tenant_id_exist_atdb=True - #TODO check if a field has changed and edit entry at datacenter_tenants at DB - else: #result=0 + vim_tenant_id_exist_atdb = True + # TODO check if a field has changed and edit entry at datacenter_tenants at DB + else: # result=0 datacenter_tenants_dict = {} - #insert at table datacenter_tenants - else: #if vim_tenant==None: - #create tenant at VIM if not provided + # insert at table datacenter_tenants + else: # if vim_tenant==None: + # create tenant at VIM if not provided try: - _, myvim = get_datacenter_by_name_uuid(mydb, None, datacenter, vim_user=vim_username, - vim_passwd=vim_password) + _, myvim = get_datacenter_by_name_uuid(mydb, None, datacenter_id, vim_user=vim_username, + vim_passwd=vim_password) datacenter_name = myvim["name"] vim_tenant = myvim.new_tenant(vim_tenant_name, "created by openmano for datacenter "+datacenter_name) - except vimconn.vimconnException as e: - raise NfvoException("Not possible to create vim_tenant {} at VIM: {}".format(vim_tenant_id, str(e)), httperrors.Internal_Server_Error) - datacenter_tenants_dict = {} - datacenter_tenants_dict["created"]="true" + except vimconn.VimConnException as e: + raise NfvoException("Not possible to create vim_tenant {} at VIM: {}".format(vim_tenant_name, e), + httperrors.Internal_Server_Error) + datacenter_tenants_dict = {"created": "true"} - #fill datacenter_tenants table + # fill datacenter_tenants table if not vim_tenant_id_exist_atdb: datacenter_tenants_dict["vim_tenant_id"] = vim_tenant datacenter_tenants_dict["vim_tenant_name"] = vim_tenant_name @@ -5233,19 +5308,22 @@ def create_vim_account(mydb, nfvo_tenant, datacenter_id, name=None, vim_id=None, id_ = mydb.new_row('datacenter_tenants', datacenter_tenants_dict, add_uuid=True, confidential_data=True) datacenter_tenants_dict["uuid"] = id_ - #fill tenants_datacenters table + # fill tenants_datacenters table datacenter_tenant_id = datacenter_tenants_dict["uuid"] tenants_datacenter_dict["datacenter_tenant_id"] = datacenter_tenant_id mydb.new_row('tenants_datacenters', tenants_datacenter_dict) - # create thread - thread_name = get_non_used_vim_name(datacenter_name, datacenter_id, tenant_dict['name'], tenant_dict['uuid']) + # load plugin and create thread + plugin_name = "rovim_" + datacenter_type + if plugin_name not in plugins: + _load_plugin(plugin_name, type="vim") + thread_name = get_non_used_vim_name(datacenter_name, datacenter_id) new_thread = vim_thread(task_lock, plugins, thread_name, None, datacenter_tenant_id, db=db) new_thread.start() thread_id = datacenter_tenants_dict["uuid"] vim_threads["running"][thread_id] = new_thread return thread_id - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: raise NfvoException(str(e), httperrors.Bad_Request) @@ -5271,7 +5349,7 @@ def edit_vim_account(mydb, nfvo_tenant, datacenter_tenant_id, datacenter_id=None if config: original_config_dict = yaml.load(original_config, Loader=yaml.Loader) original_config_dict.update(config) - update["config"] = yaml.safe_dump(original_config_dict, default_flow_style=True, width=256) + update_["config"] = yaml.safe_dump(original_config_dict, default_flow_style=True, width=256) if name: update_['name'] = name if vim_tenant: @@ -5324,7 +5402,7 @@ def delete_vim_account(mydb, tenant_id, vim_account_id, datacenter=None): try: datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter) myvim.delete_tenant(vim_tenant_dict['vim_tenant_id']) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: warning = "Not possible to delete vim_tenant_id {} from VIM: {} ".format(vim_tenant_dict['vim_tenant_id'], str(e)) logger.warn(warning) except db_base_Exception as e: @@ -5346,14 +5424,14 @@ def datacenter_action(mydb, tenant_id, datacenter, action_dict): if 'check-connectivity' in action_dict: try: myvim.check_vim_connectivity() - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: #logger.error("nfvo.datacenter_action() Not possible to get_network_list from VIM: %s ", str(e)) raise NfvoException(str(e), e.http_code) elif 'net-update' in action_dict: try: nets = myvim.get_network_list(filter_dict={'shared': True, 'admin_state_up': True, 'status': 'ACTIVE'}) #print content - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: #logger.error("nfvo.datacenter_action() Not possible to get_network_list from VIM: %s ", str(e)) raise NfvoException(str(e), httperrors.Internal_Server_Error) #update nets Change from VIM format to NFVO format @@ -5412,7 +5490,7 @@ def datacenter_new_netmap(mydb, tenant_id, datacenter, action_dict=None): try: vim_nets = myvim.get_network_list(filter_dict=filter_dict) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: #logger.error("nfvo.datacenter_new_netmap() Not possible to get_network_list from VIM: %s ", str(e)) raise NfvoException(str(e), httperrors.Internal_Server_Error) if len(vim_nets)>1 and action_dict: @@ -5453,7 +5531,7 @@ def get_sdn_net_id(mydb, tenant_id, datacenter, network_id): datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter) network = myvim.get_network_list(filter_dict=filter_dict) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: raise NfvoException("Not possible to get_sdn_net_id from VIM: {}".format(str(e)), e.http_code) # ensure the network is defined @@ -5624,7 +5702,7 @@ def vim_action_get(mydb, tenant_id, datacenter, item, name): datacenter) else: return {item: content} - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: print("vim_action Not possible to get_{}_list from VIM: {} ".format(item, str(e))) raise NfvoException("Not possible to get_{}_list from VIM: {}".format(item, str(e)), e.http_code) @@ -5688,7 +5766,7 @@ def vim_action_delete(mydb, tenant_id, datacenter, item, name): content = myvim.delete_image(item_id) else: raise NfvoException(item + "?", httperrors.Method_Not_Allowed) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: #logger.error( "vim_action Not possible to delete_{} {}from VIM: {} ".format(item, name, str(e))) raise NfvoException("Not possible to delete_{} {} from VIM: {}".format(item, name, str(e)), e.http_code) @@ -5750,21 +5828,30 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor): content = myvim.new_tenant(tenant["name"], tenant.get("description")) else: raise NfvoException(item + "?", httperrors.Method_Not_Allowed) - except vimconn.vimconnException as e: + except vimconn.VimConnException as e: raise NfvoException("Not possible to create {} at VIM: {}".format(item, str(e)), e.http_code) return vim_action_get(mydb, tenant_id, datacenter, item, content) def sdn_controller_create(mydb, tenant_id, sdn_controller): - wim_id = ovim.new_of_controller(sdn_controller) + try: + wim_id = ovim.new_of_controller(sdn_controller) + + # Load plugin if not previously loaded + controller_type = sdn_controller.get("type") + plugin_name = "rosdn_" + controller_type + if plugin_name not in plugins: + _load_plugin(plugin_name, type="sdn") - thread_name = get_non_used_vim_name(sdn_controller['name'], wim_id, wim_id, None) - new_thread = vim_thread(task_lock, plugins, thread_name, wim_id, None, db=db) - new_thread.start() - thread_id = wim_id - vim_threads["running"][thread_id] = new_thread - logger.debug('New SDN controller created with uuid {}'.format(wim_id)) - return wim_id + thread_name = get_non_used_vim_name(sdn_controller['name'], wim_id) + new_thread = vim_thread(task_lock, plugins, thread_name, wim_id, None, db=db) + new_thread.start() + thread_id = wim_id + vim_threads["running"][thread_id] = new_thread + logger.debug('New SDN controller created with uuid {}'.format(wim_id)) + return wim_id + except ovimException as e: + raise NfvoException(e) from e def sdn_controller_update(mydb, tenant_id, controller_id, sdn_controller): data = ovim.edit_of_controller(controller_id, sdn_controller) @@ -5820,6 +5907,8 @@ def datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, sdn_port_map pci = port.get("pci") element["switch_port"] = port.get("switch_port") element["switch_mac"] = port.get("switch_mac") + element["switch_dpid"] = port.get("switch_dpid") + element["switch_id"] = port.get("switch_id") if not element["switch_port"] and not element["switch_mac"]: raise NfvoException ("The mapping must contain 'switch_port' or 'switch_mac'", httperrors.Bad_Request) for pci_expanded in utils.expand_brackets(pci):