From: garciadeblas Date: Thu, 31 Jan 2019 16:01:31 +0000 (+0000) Subject: Fix support of multi-segment networks in Openstack; added created_items in all vimconns X-Git-Tag: v6.0.0~37 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FRO.git;a=commitdiff_plain;h=ebd66721b39fda2bf315102db63ea7e0dcf7becc Fix support of multi-segment networks in Openstack; added created_items in all vimconns Change-Id: I641a337e58138d693970616360dfb9aab021fd2a Signed-off-by: garciadeblas --- diff --git a/Dockerfile b/Dockerfile index 6f1f208d..68a68909 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get -y install software-properties-common && \ DEBIAN_FRONTEND=noninteractive add-apt-repository -y cloud-archive:queens && \ DEBIAN_FRONTEND=noninteractive apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get -y install python-novaclient python-keystoneclient python-glanceclient python-cinderclient python-neutronclient && \ + DEBIAN_FRONTEND=noninteractive apt-get -y install python-novaclient python-keystoneclient python-glanceclient python-cinderclient python-neutronclient python-networking-l2gw && \ DEBIAN_FRONTEND=noninteractive pip install -U progressbar pyvmomi pyvcloud==19.1.1 && \ DEBIAN_FRONTEND=noninteractive apt-get -y install python-argcomplete python-bottle python-cffi python-packaging python-paramiko python-pkgconfig libmysqlclient-dev libssl-dev libffi-dev python-mysqldb && \ DEBIAN_FRONTEND=noninteractive apt-get -y install python-logutils python-openstackclient python-openstacksdk && \ diff --git a/docker/Dockerfile-local b/docker/Dockerfile-local index 8ec5e331..86bc9949 100644 --- a/docker/Dockerfile-local +++ b/docker/Dockerfile-local @@ -10,7 +10,7 @@ RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get -y install wget tox && \ DEBIAN_FRONTEND=noninteractive pip2 install pip==9.0.3 && \ DEBIAN_FRONTEND=noninteractive pip2 install -U progressbar pyvmomi pyvcloud==19.1.1 && \ - DEBIAN_FRONTEND=noninteractive apt-get -y install python-novaclient python-keystoneclient python-glanceclient python-cinderclient python-neutronclient && \ + DEBIAN_FRONTEND=noninteractive apt-get -y install python-novaclient python-keystoneclient python-glanceclient python-cinderclient python-neutronclient python-networking-l2gw && \ DEBIAN_FRONTEND=noninteractive apt-get -y install python-cffi libmysqlclient-dev libssl-dev libffi-dev python-mysqldb && \ DEBIAN_FRONTEND=noninteractive apt-get -y install python-openstacksdk python-openstackclient && \ DEBIAN_FRONTEND=noninteractive apt-get -y install python-networkx && \ diff --git a/osm_ro/nfvo.py b/osm_ro/nfvo.py index ebb30976..677c7c6d 100644 --- a/osm_ro/nfvo.py +++ b/osm_ro/nfvo.py @@ -2408,7 +2408,7 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor): str(iface.get("vnfd-id-ref"))[:255]), httperrors.Bad_Request) interface_uuid = existing_ifaces[0]["uuid"] - if existing_ifaces[0]["iface_type"] == "data" and not db_sce_net["type"]: + if existing_ifaces[0]["iface_type"] == "data": db_sce_net["type"] = "data" sce_interface_uuid = str(uuid4()) uuid_list.append(sce_net_uuid) @@ -2634,7 +2634,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc #We should use the dictionary as input parameter for new_network #print myNetDict if not sce_net["external"]: - network_id = myvim.new_network(myNetName, myNetType, myNetIPProfile) + network_id, _ = myvim.new_network(myNetName, myNetType, myNetIPProfile) #print "New VIM network created for scenario %s. Network id: %s" % (scenarioDict['name'],network_id) sce_net['vim_id'] = network_id auxNetDict['scenario'][sce_net['uuid']] = network_id @@ -2667,7 +2667,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc #print myNetDict #TODO: #We should use the dictionary as input parameter for new_network - network_id = myvim.new_network(myNetName, myNetType, myNetIPProfile) + network_id, _ = myvim.new_network(myNetName, myNetType, myNetIPProfile) #print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id) net['vim_id'] = network_id if sce_vnf['uuid'] not in auxNetDict: @@ -5450,7 +5450,7 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor): net_public = net.pop("shared", False) net_ipprofile = net.pop("ip_profile", None) net_vlan = net.pop("vlan", None) - content = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net) + content, _ = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net) #If the datacenter has a SDN controller defined and the network is of dataplane type, then create the sdn network if get_sdn_controller_id(mydb, datacenter) != None and (net_type == 'data' or net_type == 'ptp'): @@ -5464,7 +5464,7 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor): sdn_network['type'] = net_type sdn_network['name'] = net_name sdn_network['region'] = datacenter_tenant_id - ovim_content = ovim.new_network(sdn_network) + ovim_content = ovim.new_network(sdn_network) except ovimException as e: logger.error("ovimException creating SDN network={} ".format( sdn_network) + str(e), exc_info=True) diff --git a/osm_ro/vim_thread.py b/osm_ro/vim_thread.py index d57ec470..02b3bccf 100644 --- a/osm_ro/vim_thread.py +++ b/osm_ro/vim_thread.py @@ -990,7 +990,7 @@ class vim_thread(threading.Thread): # CREATE params = task["params"] action_text = "creating VIM" - vim_net_id = self.vim.new_network(*params[0:3]) + vim_net_id, created_items = self.vim.new_network(*params[0:3]) net_name = params[0] net_type = params[1] @@ -1039,6 +1039,7 @@ class vim_thread(threading.Thread): task["extra"]["vim_info"] = {} task["extra"]["sdn_net_id"] = sdn_net_id task["extra"]["created"] = True + task["extra"]["created_items"] = created_items task["error_msg"] = None task["vim_id"] = vim_net_id instance_element_update = {"vim_net_id": vim_net_id, "sdn_net_id": sdn_net_id, "status": "BUILD", @@ -1068,7 +1069,7 @@ class vim_thread(threading.Thread): self.ovim.delete_port(port['uuid'], idempotent=True) self.ovim.delete_network(sdn_net_id, idempotent=True) if net_vim_id: - self.vim.delete_network(net_vim_id) + self.vim.delete_network(net_vim_id, task["extra"].get("created_items")) task["status"] = "DONE" task["error_msg"] = None return True, None diff --git a/osm_ro/vimconn.py b/osm_ro/vimconn.py index 4a7cc095..5c948409 100644 --- a/osm_ro/vimconn.py +++ b/osm_ro/vimconn.py @@ -331,7 +331,11 @@ class vimconnector(): 'dhcp_count': number of IPs to grant. 'shared': if this network can be seen/use by other tenants/organization 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network - Returns the network identifier on success or raises and exception on failure + Returns a tuple with the network identifier and created_items, or raises an exception on error + created_items can be None or a dictionary where this method can include key-values that will be passed to + the method delete_network. Can be used to store created segments, created l2gw connections, etc. + Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same + as not present. """ raise vimconnNotImplemented( "Should have implemented this" ) @@ -370,8 +374,11 @@ class vimconnector(): """ raise vimconnNotImplemented( "Should have implemented this" ) - def delete_network(self, net_id): - """Deletes a tenant network from VIM + def delete_network(self, net_id, created_items=None): + """ + Removes a tenant network from VIM and its associated elements + :param net_id: VIM identifier of the network, provided by method new_network + :param created_items: dictionary with extra items to be deleted. provided by method new_network Returns the network identifier or raises an exception upon error or when network is not found """ raise vimconnNotImplemented( "Should have implemented this" ) @@ -533,7 +540,7 @@ class vimconnector(): def delete_vminstance(self, vm_id, created_items=None): """ - Removes a VM instance from VIM and each associate elements + Removes a VM instance from VIM and its associated elements :param vm_id: VIM identifier of the VM, provided by method new_vminstance :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method action_vminstance diff --git a/osm_ro/vimconn_aws.py b/osm_ro/vimconn_aws.py index 22103d0c..bcd8cbc8 100644 --- a/osm_ro/vimconn_aws.py +++ b/osm_ro/vimconn_aws.py @@ -299,11 +299,16 @@ class vimconnector(vimconn.vimconnector): 'count': number of IPs to grant. 'shared': if this network can be seen/use by other tenants/organization 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network - Returns the network identifier on success or raises and exception on failure + Returns a tuple with the network identifier and created_items, or raises an exception on error + created_items can be None or a dictionary where this method can include key-values that will be passed to + the method delete_network. Can be used to store created segments, created l2gw connections, etc. + Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same + as not present. """ self.logger.debug("Adding a subnet to VPC") try: + created_items = {} self._reload_connection() subnet = None vpc_id = self.vpc_id @@ -314,7 +319,7 @@ class vimconnector(vimconn.vimconnector): subnet_list = self.subnet_sizes(len(self.get_availability_zones_list()), vpc['cidr_block']) cidr_block = list(set(subnet_list) - set(self.get_network_details({'tenant_id': vpc['id']}, detail='cidr_block')))[0] subnet = self.conn_vpc.create_subnet(vpc_id, cidr_block) - return subnet.id + return subnet.id, created_items except Exception as e: self.format_vimconn_exception(e) @@ -384,8 +389,11 @@ class vimconnector(vimconn.vimconnector): except Exception as e: self.format_vimconn_exception(e) - def delete_network(self, net_id): - """Deletes a tenant network from VIM + def delete_network(self, net_id, created_items=None): + """ + Removes a tenant network from VIM and its associated elements + :param net_id: VIM identifier of the network, provided by method new_network + :param created_items: dictionary with extra items to be deleted. provided by method new_network Returns the network identifier or raises an exception upon error or when network is not found """ diff --git a/osm_ro/vimconn_opennebula.py b/osm_ro/vimconn_opennebula.py index 5a0ea870..8f7fad84 100644 --- a/osm_ro/vimconn_opennebula.py +++ b/osm_ro/vimconn_opennebula.py @@ -209,9 +209,33 @@ class vimconnector(vimconn.vimconnector): # os.remove("manage_bridge_OSM") def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None): # , **vim_specific): - """Returns the network identifier""" + """Adds a tenant network to VIM + Params: + 'net_name': name of the network + 'net_type': one of: + 'bridge': overlay isolated network + 'data': underlay E-LAN network for Passthrough and SRIOV interfaces + 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces. + 'ip_profile': is a dict containing the IP parameters of the network + 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented) + 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y + 'gateway_address': (Optional) ip_schema, that is X.X.X.X + 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X] + 'dhcp_enabled': True or False + 'dhcp_start_address': ip_schema, first IP to grant + 'dhcp_count': number of IPs to grant. + 'shared': if this network can be seen/use by other tenants/organization + 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network + Returns a tuple with the network identifier and created_items, or raises an exception on error + created_items can be None or a dictionary where this method can include key-values that will be passed to + the method delete_network. Can be used to store created segments, created l2gw connections, etc. + Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same + as not present. + """ + # oca library method cannot be used in this case (problem with cluster parameters) try: + created_items = {} # vlan = str(random.randint(self.config["vlan"]["start-range"], self.config["vlan"]["finish-range"])) # self.create_bridge_host(vlan) bridge_config = self.config["bridge_service"] @@ -262,7 +286,7 @@ class vimconnector(vimconn.vimconnector): '.format(self.user, self.passwd, config, self.config["cluster"]["id"]) r = requests.post(self.url, params) obj = untangle.parse(str(r.content)) - return obj.methodResponse.params.param.value.array.data.value[1].i4.cdata.encode('utf-8') + return obj.methodResponse.params.param.value.array.data.value[1].i4.cdata.encode('utf-8'), created_items except Exception as e: self.logger.error("Create new network error: " + str(e)) raise vimconn.vimconnException(e) @@ -328,9 +352,12 @@ class vimconnector(vimconn.vimconnector): self.logger.error("Get network " + str(net_id) + " error): " + str(e)) raise vimconn.vimconnException(e) - def delete_network(self, net_id): - """Deletes a tenant network from VIM - Returns the network identifier + def delete_network(self, net_id, created_items=None): + """ + Removes a tenant network from VIM and its associated elements + :param net_id: VIM identifier of the network, provided by method new_network + :param created_items: dictionary with extra items to be deleted. provided by method new_network + Returns the network identifier or raises an exception upon error or when network is not found """ try: # self.delete_bridge_host() diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index b698eda3..cfeeae0d 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -109,7 +109,11 @@ class vimconnector(vimconn.vimconnector): if config.get('dataplane_net_vlan_range') is not None: #validate vlan ranges provided by user - self._validate_vlan_ranges(config.get('dataplane_net_vlan_range')) + self._validate_vlan_ranges(config.get('dataplane_net_vlan_range'), 'dataplane_net_vlan_range') + + if config.get('multisegment_vlan_range') is not None: + #validate vlan ranges provided by user + self._validate_vlan_ranges(config.get('multisegment_vlan_range'), 'multisegment_vlan_range') vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config) @@ -488,20 +492,63 @@ class vimconnector(vimconn.vimconnector): self._format_exception(e) def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None): - '''Adds a tenant network to VIM. Returns the network identifier''' + """Adds a tenant network to VIM + Params: + 'net_name': name of the network + 'net_type': one of: + 'bridge': overlay isolated network + 'data': underlay E-LAN network for Passthrough and SRIOV interfaces + 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces. + 'ip_profile': is a dict containing the IP parameters of the network + 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented) + 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y + 'gateway_address': (Optional) ip_schema, that is X.X.X.X + 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X] + 'dhcp_enabled': True or False + 'dhcp_start_address': ip_schema, first IP to grant + 'dhcp_count': number of IPs to grant. + 'shared': if this network can be seen/use by other tenants/organization + 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network + Returns a tuple with the network identifier and created_items, or raises an exception on error + created_items can be None or a dictionary where this method can include key-values that will be passed to + the method delete_network. Can be used to store created segments, created l2gw connections, etc. + Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same + as not present. + """ self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type) - #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile)) + # self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile)) try: new_net = None + created_items = {} self._reload_connection() network_dict = {'name': net_name, 'admin_state_up': True} if net_type=="data" or net_type=="ptp": if self.config.get('dataplane_physical_net') == None: raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network") - network_dict["provider:physical_network"] = self.config['dataplane_physical_net'] #"physnet_sriov" #TODO physical - network_dict["provider:network_type"] = "vlan" - if vlan!=None: - network_dict["provider:network_type"] = vlan + if not self.config.get('multisegment_support'): + network_dict["provider:physical_network"] = self.config[ + 'dataplane_physical_net'] # "physnet_sriov" #TODO physical + network_dict["provider:network_type"] = "vlan" + if vlan!=None: + network_dict["provider:network_type"] = vlan + else: + ###### Multi-segment case ###### + segment_list = [] + segment1_dict = {} + segment1_dict["provider:physical_network"] = '' + segment1_dict["provider:network_type"] = 'vxlan' + segment_list.append(segment1_dict) + segment2_dict = {} + segment2_dict["provider:physical_network"] = self.config['dataplane_physical_net'] + segment2_dict["provider:network_type"] = "vlan" + if self.config.get('multisegment_vlan_range'): + vlanID = self._generate_multisegment_vlanID() + segment2_dict["provider:segmentation_id"] = vlanID + # else + # raise vimconn.vimconnConflictException( + # "You must provide 'multisegment_vlan_range' at config dict before creating a multisegment network") + segment_list.append(segment2_dict) + network_dict["segments"] = segment_list ####### VIO Specific Changes ######### if self.vim_type == "VIO": @@ -513,12 +560,12 @@ class vimconnector(vimconn.vimconnector): "'dataplane_net_vlan_range' in format [start_ID - end_ID]"\ "at config value before creating sriov network with vlan tag") - network_dict["provider:segmentation_id"] = self._genrate_vlanID() + network_dict["provider:segmentation_id"] = self._generate_vlanID() - network_dict["shared"]=shared - new_net=self.neutron.create_network({'network':network_dict}) - #print new_net - #create subnetwork, even if there is no profile + network_dict["shared"] = shared + new_net = self.neutron.create_network({'network':network_dict}) + # print new_net + # create subnetwork, even if there is no profile if not ip_profile: ip_profile = {} if not ip_profile.get('subnet_address'): @@ -527,7 +574,7 @@ class vimconnector(vimconn.vimconnector): ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand) if 'ip_version' not in ip_profile: ip_profile['ip_version'] = "IPv4" - subnet = {"name":net_name+"-subnet", + subnet = {"name": net_name+"-subnet", "network_id": new_net["network"]["id"], "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6, "cidr": ip_profile['subnet_address'] @@ -555,8 +602,29 @@ class vimconnector(vimconn.vimconnector): subnet['allocation_pools'][0]['end'] = ip_str #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet)) self.neutron.create_subnet({"subnet": subnet} ) - return new_net["network"]["id"] + + if net_type == "data" and self.config.get('multisegment_support'): + if self.config.get('l2gw_support'): + l2gw_list = self.neutron.list_l2_gateways().get("l2_gateways", ()) + for l2gw in l2gw_list: + l2gw_conn = {} + l2gw_conn["l2_gateway_id"] = l2gw["id"] + l2gw_conn["network_id"] = new_net["network"]["id"] + l2gw_conn["segmentation_id"] = str(vlanID) + new_l2gw_conn = self.neutron.create_l2_gateway_connection({"l2_gateway_connection": l2gw_conn}) + created_items["l2gwconn:" + str(new_l2gw_conn["l2_gateway_connection"]["id"])] = True + return new_net["network"]["id"], created_items except Exception as e: + #delete l2gw connections (if any) before deleting the network + for k, v in created_items.items(): + if not v: # skip already deleted + continue + try: + k_item, _, k_id = k.partition(":") + if k_item == "l2gwconn": + self.neutron.delete_l2_gateway_connection(k_id) + except Exception as e2: + self.logger.error("Error deleting l2 gateway connection: {}: {}".format(type(e2).__name__, e2)) if new_net: self.neutron.delete_network(new_net['network']['id']) self._format_exception(e) @@ -611,11 +679,28 @@ class vimconnector(vimconn.vimconnector): net["encapsulation_id"] = net.get('provider:segmentation_id') return net - def delete_network(self, net_id): - '''Deletes a tenant network from VIM. Returns the old network identifier''' + def delete_network(self, net_id, created_items=None): + """ + Removes a tenant network from VIM and its associated elements + :param net_id: VIM identifier of the network, provided by method new_network + :param created_items: dictionary with extra items to be deleted. provided by method new_network + Returns the network identifier or raises an exception upon error or when network is not found + """ self.logger.debug("Deleting network '%s' from VIM", net_id) + if created_items == None: + created_items = {} try: self._reload_connection() + #delete l2gw connections (if any) before deleting the network + for k, v in created_items.items(): + if not v: # skip already deleted + continue + try: + k_item, _, k_id = k.partition(":") + if k_item == "l2gwconn": + self.neutron.delete_l2_gateway_connection(k_id) + except Exception as e: + self.logger.error("Error deleting l2 gateway connection: {}: {}".format(type(e).__name__, e)) #delete VM ports attached to this networks before the network ports = self.neutron.list_ports(network_id=net_id) for p in ports['ports']: @@ -1625,7 +1710,7 @@ class vimconnector(vimconn.vimconnector): #TODO insert exception vimconn.HTTP_Unauthorized ####### VIO Specific Changes ######### - def _genrate_vlanID(self): + def _generate_vlanID(self): """ Method to get unused vlanID Args: @@ -1655,35 +1740,69 @@ class vimconnector(vimconn.vimconnector): " All given Vlan IDs {} are in use.".format(self.config.get('dataplane_net_vlan_range'))) - def _validate_vlan_ranges(self, dataplane_net_vlan_range): + def _generate_multisegment_vlanID(self): + """ + Method to get unused vlanID + Args: + None + Returns: + vlanID + """ + #Get used VLAN IDs + usedVlanIDs = [] + networks = self.get_network_list() + for net in networks: + if net.get('provider:network_type') == "vlan" and net.get('provider:segmentation_id'): + usedVlanIDs.append(net.get('provider:segmentation_id')) + elif net.get('segments'): + for segment in net.get('segments'): + if segment.get('provider:network_type') == "vlan" and segment.get('provider:segmentation_id'): + usedVlanIDs.append(segment.get('provider:segmentation_id')) + used_vlanIDs = set(usedVlanIDs) + + #find unused VLAN ID + for vlanID_range in self.config.get('multisegment_vlan_range'): + try: + start_vlanid , end_vlanid = map(int, vlanID_range.replace(" ", "").split("-")) + for vlanID in xrange(start_vlanid, end_vlanid + 1): + if vlanID not in used_vlanIDs: + return vlanID + except Exception as exp: + raise vimconn.vimconnException("Exception {} occurred while generating VLAN ID.".format(exp)) + else: + raise vimconn.vimconnConflictException("Unable to create the VLAN segment."\ + " All VLAN IDs {} are in use.".format(self.config.get('multisegment_vlan_range'))) + + + def _validate_vlan_ranges(self, input_vlan_range, text_vlan_range): """ Method to validate user given vlanID ranges Args: None Returns: None """ - for vlanID_range in dataplane_net_vlan_range: + for vlanID_range in input_vlan_range: vlan_range = vlanID_range.replace(" ", "") #validate format vlanID_pattern = r'(\d)*-(\d)*$' match_obj = re.match(vlanID_pattern, vlan_range) if not match_obj: - raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}.You must provide "\ - "'dataplane_net_vlan_range' in format [start_ID - end_ID].".format(vlanID_range)) + raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}.You must provide "\ + "'{}' in format [start_ID - end_ID].".format(text_vlan_range, vlanID_range, text_vlan_range)) start_vlanid , end_vlanid = map(int,vlan_range.split("-")) if start_vlanid <= 0 : - raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\ + raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\ "Start ID can not be zero. For VLAN "\ - "networks valid IDs are 1 to 4094 ".format(vlanID_range)) + "networks valid IDs are 1 to 4094 ".format(text_vlan_range, vlanID_range)) if end_vlanid > 4094 : - raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\ + raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\ "End VLAN ID can not be greater than 4094. For VLAN "\ - "networks valid IDs are 1 to 4094 ".format(vlanID_range)) + "networks valid IDs are 1 to 4094 ".format(text_vlan_range, vlanID_range)) if start_vlanid > end_vlanid: - raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\ - "You must provide a 'dataplane_net_vlan_range' in format start_ID - end_ID and "\ - "start_ID < end_ID ".format(vlanID_range)) + raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\ + "You must provide '{}' in format start_ID - end_ID and "\ + "start_ID < end_ID ".format(text_vlan_range, vlanID_range, text_vlan_range)) #NOT USED FUNCTIONS diff --git a/osm_ro/vimconn_openvim.py b/osm_ro/vimconn_openvim.py index f6d22376..6f584c5e 100644 --- a/osm_ro/vimconn_openvim.py +++ b/osm_ro/vimconn_openvim.py @@ -485,9 +485,31 @@ class vimconnector(vimconn.vimconnector): self._format_request_exception(e) def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None): #, **vim_specific): - '''Adds a tenant network to VIM''' - '''Returns the network identifier''' + """Adds a tenant network to VIM + Params: + 'net_name': name of the network + 'net_type': one of: + 'bridge': overlay isolated network + 'data': underlay E-LAN network for Passthrough and SRIOV interfaces + 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces. + 'ip_profile': is a dict containing the IP parameters of the network + 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented) + 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y + 'gateway_address': (Optional) ip_schema, that is X.X.X.X + 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X] + 'dhcp_enabled': True or False + 'dhcp_start_address': ip_schema, first IP to grant + 'dhcp_count': number of IPs to grant. + 'shared': if this network can be seen/use by other tenants/organization + 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network + Returns a tuple with the network identifier and created_items, or raises an exception on error + created_items can be None or a dictionary where this method can include key-values that will be passed to + the method delete_network. Can be used to store created segments, created l2gw connections, etc. + Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same + as not present. + """ try: + created_items = {} self._get_my_tenant() if net_type=="bridge": net_type="bridge_data" @@ -507,7 +529,7 @@ class vimconnector(vimconn.vimconnector): #if r is not None: # self.logger.warn("Warning: remove extra items %s", str(r)) network_id = response['network']['id'] - return network_id + return network_id, created_items except (requests.exceptions.RequestException, js_e.ValidationError) as e: self._format_request_exception(e) @@ -558,9 +580,13 @@ class vimconnector(vimconn.vimconnector): except (requests.exceptions.RequestException, js_e.ValidationError) as e: self._format_request_exception(e) - def delete_network(self, net_id): - '''Deletes a tenant network from VIM''' - '''Returns the network identifier''' + def delete_network(self, net_id, created_items=None): + """ + Removes a tenant network from VIM and its associated elements + :param net_id: VIM identifier of the network, provided by method new_network + :param created_items: dictionary with extra items to be deleted. provided by method new_network + Returns the network identifier or raises an exception upon error or when network is not found + """ try: self._get_my_tenant() url = self.url+'/networks/'+net_id diff --git a/osm_ro/vimconn_vmware.py b/osm_ro/vimconn_vmware.py index 4a5ab972..2910e924 100644 --- a/osm_ro/vimconn_vmware.py +++ b/osm_ro/vimconn_vmware.py @@ -500,17 +500,35 @@ class vimconnector(vimconn.vimconnector): return vdclist - def new_network(self, net_name, net_type, ip_profile=None, shared=False): + def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None): """Adds a tenant network to VIM - net_name is the name - net_type can be 'bridge','data'.'ptp'. - ip_profile is a dict containing the IP parameters of the network - shared is a boolean - Returns the network identifier""" + Params: + 'net_name': name of the network + 'net_type': one of: + 'bridge': overlay isolated network + 'data': underlay E-LAN network for Passthrough and SRIOV interfaces + 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces. + 'ip_profile': is a dict containing the IP parameters of the network + 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented) + 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y + 'gateway_address': (Optional) ip_schema, that is X.X.X.X + 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X] + 'dhcp_enabled': True or False + 'dhcp_start_address': ip_schema, first IP to grant + 'dhcp_count': number of IPs to grant. + 'shared': if this network can be seen/use by other tenants/organization + 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network + Returns a tuple with the network identifier and created_items, or raises an exception on error + created_items can be None or a dictionary where this method can include key-values that will be passed to + the method delete_network. Can be used to store created segments, created l2gw connections, etc. + Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same + as not present. + """ self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}" .format(net_name, net_type, ip_profile, shared)) + created_items = {} isshared = 'false' if shared: isshared = 'true' @@ -524,7 +542,7 @@ class vimconnector(vimconn.vimconnector): network_uuid = self.create_network(network_name=net_name, net_type=net_type, ip_profile=ip_profile, isshared=isshared) if network_uuid is not None: - return network_uuid + return network_uuid, created_items else: raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name)) @@ -771,11 +789,12 @@ class vimconnector(vimconn.vimconnector): return filter_dict - def delete_network(self, net_id): + def delete_network(self, net_id, created_items=None): """ - Method Deletes a tenant network from VIM, provide the network id. - - Returns the network identifier or raise an exception + Removes a tenant network from VIM and its associated elements + :param net_id: VIM identifier of the network, provided by method new_network + :param created_items: dictionary with extra items to be deleted. provided by method new_network + Returns the network identifier or raises an exception upon error or when network is not found """ # ############# Stub code for SRIOV ################# diff --git a/requirements.txt b/requirements.txt index 6e1068af..05d96bb2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ python-novaclient python-keystoneclient python-glanceclient python-neutronclient +networking-l2gw python-cinderclient pyvcloud==19.1.1 pyvmomi diff --git a/scripts/install-openmano.sh b/scripts/install-openmano.sh index 3789992f..e4554ae7 100755 --- a/scripts/install-openmano.sh +++ b/scripts/install-openmano.sh @@ -270,10 +270,11 @@ then # install openstack client needed for using openstack as a VIM [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-novaclient python-keystoneclient python-glanceclient "\ - "python-neutronclient python-cinderclient python-openstackclient" + "python-neutronclient python-cinderclient python-openstackclient "\ + "python-networking-l2gw" [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-devel" && easy_install \ python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient \ - python-openstackclient #TODO revise if gcc python-pip is needed + python-openstackclient python-networking-l2gw #TODO revise if gcc python-pip is needed fi # [[ -z "$NO_PACKAGES" ]] if [[ -z $NOCLONE ]]; then diff --git a/setup.py b/setup.py index d5cbe79a..5d78ec3b 100755 --- a/setup.py +++ b/setup.py @@ -31,6 +31,7 @@ _requirements = [ "python-glanceclient", "python-neutronclient", "python-cinderclient", + "networking-l2gw", #"pyvcloud", #"progressbar", "prettytable", diff --git a/stdeb.cfg b/stdeb.cfg index 53abd48e..f0424fb3 100644 --- a/stdeb.cfg +++ b/stdeb.cfg @@ -2,5 +2,5 @@ Suite: xenial XS-Python-Version: >= 2.7 Maintainer: Gerardo Garcia -Depends: python-pip, libmysqlclient-dev, libssl-dev, libffi-dev, python-argcomplete, python-boto, python-bottle, python-jsonschema, python-logutils, python-cinderclient, python-glanceclient, python-keystoneclient, python-neutronclient, python-novaclient, python-openstackclient, python-mysqldb, python-lib-osm-openvim, python-osm-im, python-networkx +Depends: python-pip, libmysqlclient-dev, libssl-dev, libffi-dev, python-argcomplete, python-boto, python-bottle, python-jsonschema, python-logutils, python-cinderclient, python-glanceclient, python-keystoneclient, python-neutronclient, python-networking-l2gw, python-novaclient, python-openstackclient, python-mysqldb, python-lib-osm-openvim, python-osm-im, python-networkx