X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=osm_ro%2Fvimconn_openstack.py;h=cfeeae0ddc898219b6569d01724ba870e050b1d0;hb=264c0cf3aea0dc1a0d539c5f535cde562cd47b16;hp=57e22575d4dae9baee765a67d09b78bfa183053d;hpb=c76a3eee8272c5f3c66f43b8eafe2c48273ee0c7;p=osm%2FRO.git diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index 57e22575..cfeeae0d 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -44,6 +44,8 @@ import yaml import random import re import copy +from pprint import pformat +from types import StringTypes from novaclient import client as nClient, exceptions as nvExceptions from keystoneauth1.identity import v2, v3 @@ -77,6 +79,18 @@ supportedClassificationTypes = ['legacy_flow_classifier'] volume_timeout = 600 server_timeout = 600 + +class SafeDumper(yaml.SafeDumper): + def represent_data(self, data): + # Openstack APIs use custom subclasses of dict and YAML safe dumper + # is designed to not handle that (reference issue 142 of pyyaml) + if isinstance(data, dict) and data.__class__ != dict: + # A simple solution is to convert those items back to dicts + data = dict(data.items()) + + return super(SafeDumper, self).represent_data(data) + + class vimconnector(vimconn.vimconnector): def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None, config={}, persistent_info={}): @@ -95,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) @@ -113,6 +131,7 @@ class vimconnector(vimconn.vimconnector): self.persistent_info = persistent_info self.availability_zone = persistent_info.get('availability_zone', None) self.session = persistent_info.get('session', {'reload_client': True}) + self.my_tenant_id = self.session.get('my_tenant_id') self.nova = self.session.get('nova') self.neutron = self.session.get('neutron') self.cinder = self.session.get('cinder') @@ -130,6 +149,11 @@ class vimconnector(vimconn.vimconnector): self.logger = logging.getLogger('openmano.vim.openstack') + # allow security_groups to be a list or a single string + if isinstance(self.config.get('security_groups'), str): + self.config['security_groups'] = [self.config['security_groups']] + self.security_groups_id = None + ####### VIO Specific Changes ######### if self.vim_type == "VIO": self.logger = logging.getLogger('openmano.vim.vio') @@ -158,11 +182,30 @@ class vimconnector(vimconn.vimconnector): vimconn.vimconnector.__setitem__(self, index, value) self.session['reload_client'] = True + def serialize(self, value): + """Serialization of python basic types. + + In the case value is not serializable a message will be logged and a + simple representation of the data that cannot be converted back to + python is returned. + """ + if isinstance(value, StringTypes): + return value + + try: + return yaml.dump(value, Dumper=SafeDumper, + default_flow_style=True, width=256) + except yaml.representer.RepresenterError: + self.logger.debug( + 'The following entity cannot be serialized in YAML:' + '\n\n%s\n\n', pformat(value), exc_info=True) + return str(value) + def _reload_connection(self): '''Called before any operation, it check if credentials has changed Throw keystoneclient.apiclient.exceptions.AuthorizationFailure ''' - #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-) + #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-) if self.session['reload_client']: if self.config.get('APIversion'): self.api_version3 = self.config['APIversion'] == 'v3.3' or self.config['APIversion'] == '3' @@ -194,8 +237,10 @@ class vimconnector(vimconn.vimconnector): tenant_name=self.tenant_name, tenant_id=self.tenant_id) sess = session.Session(auth=auth, verify=self.verify) + # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River Titanium cloud and StarlingX + region_name = self.config.get('region_name') if self.api_version3: - self.keystone = ksClient_v3.Client(session=sess, endpoint_type=self.endpoint_type) + self.keystone = ksClient_v3.Client(session=sess, endpoint_type=self.endpoint_type, region_name=region_name) else: self.keystone = ksClient_v2.Client(session=sess, endpoint_type=self.endpoint_type) self.session['keystone'] = self.keystone @@ -208,16 +253,21 @@ class vimconnector(vimconn.vimconnector): version = self.config.get("microversion") if not version: version = "2.1" - self.nova = self.session['nova'] = nClient.Client(str(version), session=sess, endpoint_type=self.endpoint_type) - self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess, endpoint_type=self.endpoint_type) - self.cinder = self.session['cinder'] = cClient.Client(2, session=sess, endpoint_type=self.endpoint_type) + # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River Titanium cloud and StarlingX + self.nova = self.session['nova'] = nClient.Client(str(version), session=sess, endpoint_type=self.endpoint_type, region_name=region_name) + self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess, endpoint_type=self.endpoint_type, region_name=region_name) + self.cinder = self.session['cinder'] = cClient.Client(2, session=sess, endpoint_type=self.endpoint_type, region_name=region_name) + try: + self.my_tenant_id = self.session['my_tenant_id'] = sess.get_project_id() + except Exception as e: + self.logger.error("Cannot get project_id from session", exc_info=True) if self.endpoint_type == "internalURL": glance_service_id = self.keystone.services.list(name="glance")[0].id glance_endpoint = self.keystone.endpoints.list(glance_service_id, interface="internal")[0].url else: glance_endpoint = None self.glance = self.session['glance'] = glClient.Client(2, session=sess, endpoint=glance_endpoint) - #using version 1 of glance client in new_image() + # using version 1 of glance client in new_image() # self.glancev1 = self.session['glancev1'] = glClient.Client('1', session=sess, # endpoint=glance_endpoint) self.session['reload_client'] = False @@ -225,6 +275,7 @@ class vimconnector(vimconn.vimconnector): # add availablity zone info inside self.persistent_info self._set_availablity_zones() self.persistent_info['availability_zone'] = self.availability_zone + self.security_groups_id = None # force to get again security_groups_ids next time they are needed def __net_os2mano(self, net_list_dict): '''Transform the net openstack format to mano format @@ -347,13 +398,12 @@ class vimconnector(vimconn.vimconnector): def _format_exception(self, exception): '''Transform a keystone, nova, neutron exception into a vimconn exception''' - if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, - ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed - )): - raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception)) - elif isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound)): + if isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound, ksExceptions.NotFound, gl1Exceptions.HTTPNotFound)): raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + str(exception)) - elif isinstance(exception, (KeyError, nvExceptions.BadRequest)): + elif isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, + ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed)): + raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception)) + elif isinstance(exception, (KeyError, nvExceptions.BadRequest, ksExceptions.BadRequest)): raise vimconn.vimconnException(type(exception).__name__ + ": " + str(exception)) elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException, neExceptions.NeutronException)): @@ -366,6 +416,30 @@ class vimconnector(vimconn.vimconnector): self.logger.error("General Exception " + str(exception), exc_info=True) raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception)) + def _get_ids_from_name(self): + """ + Obtain ids from name of tenant and security_groups. Store at self .security_groups_id" + :return: None + """ + # get tenant_id if only tenant_name is supplied + self._reload_connection() + if not self.my_tenant_id: + raise vimconn.vimconnConnectionException("Error getting tenant information from name={} id={}". + format(self.tenant_name, self.tenant_id)) + if self.config.get('security_groups') and not self.security_groups_id: + # convert from name to id + neutron_sg_list = self.neutron.list_security_groups(tenant_id=self.my_tenant_id)["security_groups"] + + self.security_groups_id = [] + for sg in self.config.get('security_groups'): + for neutron_sg in neutron_sg_list: + if sg in (neutron_sg["id"], neutron_sg["name"]): + self.security_groups_id.append(neutron_sg["id"]) + break + else: + self.security_groups_id = None + raise vimconn.vimconnConnectionException("Not found security group {} for this tenant".format(sg)) + def get_tenant_list(self, filter_dict={}): '''Obtain tenants of VIM filter_dict can contain the following keys: @@ -401,7 +475,7 @@ class vimconnector(vimconn.vimconnector): else: project = self.keystone.tenants.create(tenant_name, tenant_description) return project.id - except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e: + except (ksExceptions.ConnectionError, ksExceptions.ClientException, ksExceptions.BadRequest, ConnectionError) as e: self._format_exception(e) def delete_tenant(self, tenant_id): @@ -414,24 +488,67 @@ class vimconnector(vimconn.vimconnector): else: self.keystone.tenants.delete(tenant_id) return tenant_id - except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e: + except (ksExceptions.ConnectionError, ksExceptions.ClientException, ksExceptions.NotFound, ConnectionError) as e: 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": @@ -443,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'): @@ -457,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'] @@ -485,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) @@ -536,14 +674,33 @@ class vimconnector(vimconn.vimconnector): subnets.append(subnet) net["subnets"] = subnets net["encapsulation"] = net.get('provider:network_type') + net["encapsulation_type"] = net.get('provider:network_type') net["segmentation_id"] = net.get('provider:segmentation_id') + 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']: @@ -564,13 +721,13 @@ class vimconnector(vimconn.vimconnector): net_id: #VIM id of this network status: #Mandatory. Text with one of: # DELETED (not found at vim) - # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) + # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) # OTHER (Vim reported other status not understood) # ERROR (VIM indicates an ERROR status) - # ACTIVE, INACTIVE, DOWN (admin down), + # ACTIVE, INACTIVE, DOWN (admin down), # BUILD (on building process) # - error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR + error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR vim_info: #Text with plain information obtained from vim (yaml.safe_dump) ''' @@ -587,10 +744,9 @@ class vimconnector(vimconn.vimconnector): if net['status'] == "ACTIVE" and not net_vim['admin_state_up']: net['status'] = 'DOWN' - try: - net['vim_info'] = yaml.safe_dump(net_vim, default_flow_style=True, width=256) - except yaml.representer.RepresenterError: - net['vim_info'] = str(net_vim) + + net['vim_info'] = self.serialize(net_vim) + if net_vim.get('fault'): #TODO net['error_msg'] = str(net_vim['fault']) except vimconn.vimconnNotFoundException as e: @@ -793,7 +949,14 @@ class vimconnector(vimconn.vimconnector): else: disk_format="raw" self.logger.debug("new_image: '%s' loading from '%s'", image_dict['name'], image_dict['location']) - new_image = self.glance.images.create(name=image_dict['name']) + if self.vim_type == "VIO": + container_format = "bare" + if 'container_format' in image_dict: + container_format = image_dict['container_format'] + new_image = self.glance.images.create(name=image_dict['name'], container_format=container_format, + disk_format=disk_format) + else: + new_image = self.glance.images.create(name=image_dict['name']) if image_dict['location'].startswith("http"): # TODO there is not a method to direct download. It must be downloaded locally with requests raise vimconn.vimconnNotImplemented("Cannot create image from URL") @@ -803,8 +966,11 @@ class vimconnector(vimconn.vimconnector): #new_image = self.glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes", # container_format="bare", data=fimage, disk_format=disk_format) metadata_to_load = image_dict.get('metadata') - #TODO location is a reserved word for current openstack versions. Use another word - metadata_to_load['location'] = image_dict['location'] + # TODO location is a reserved word for current openstack versions. fixed for VIO please check for openstack + if self.vim_type == "VIO": + metadata_to_load['upload_location'] = image_dict['location'] + else: + metadata_to_load['location'] = image_dict['location'] self.glance.images.update(new_image.id, **metadata_to_load) return new_image.id except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e: @@ -824,7 +990,7 @@ class vimconnector(vimconn.vimconnector): self._reload_connection() self.glance.images.delete(image_id) return image_id - except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: #TODO remove + except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, gl1Exceptions.HTTPNotFound, ConnectionError) as e: #TODO remove self._format_exception(e) def get_image_id_from_path(self, path): @@ -1004,15 +1170,22 @@ class vimconnector(vimconn.vimconnector): self._reload_connection() # metadata_vpci = {} # For a specific neutron plugin block_device_mapping = None + for net in net_list: if not net.get("net_id"): # skip non connected iface continue - port_dict={ + port_dict = { "network_id": net["net_id"], "name": net.get("name"), "admin_state_up": True } + if self.config.get("security_groups") and net.get("port_security") is not False and \ + not self.config.get("no_port_security_extension"): + if not self.security_groups_id: + self._get_ids_from_name() + port_dict["security_groups"] = self.security_groups_id + if net["type"]=="virtual": pass # if "vpci" in net: @@ -1089,9 +1262,6 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s'", name, image_id, flavor_id, str(net_list_vim), description) - security_groups = self.config.get('security_groups') - if type(security_groups) is str: - security_groups = ( security_groups, ) # cloud config config_drive, userdata = self._create_user_data(cloud_config) @@ -1135,10 +1305,12 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("nova.servers.create({}, {}, {}, nics={}, security_groups={}, " "availability_zone={}, key_name={}, userdata={}, config_drive={}, " "block_device_mapping={})".format(name, image_id, flavor_id, net_list_vim, - security_groups, vm_av_zone, self.config.get('keypair'), - userdata, config_drive, block_device_mapping)) + self.config.get("security_groups"), vm_av_zone, + self.config.get('keypair'), userdata, config_drive, + block_device_mapping)) server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim, - security_groups=security_groups, + security_groups=self.config.get("security_groups"), + # TODO remove security_groups in future versions. Already at neutron port availability_zone=vm_av_zone, key_name=self.config.get('keypair'), userdata=userdata, @@ -1262,13 +1434,13 @@ class vimconnector(vimconn.vimconnector): Params: vm_id: uuid of the VM console_type, can be: - "novnc" (by default), "xvpvnc" for VNC types, + "novnc" (by default), "xvpvnc" for VNC types, "rdp-html5" for RDP types, "spice-html5" for SPICE types Returns dict with the console parameters: protocol: ssh, ftp, http, https, ... - server: usually ip address - port: the http, ssh, ... port - suffix: extra text, e.g. the http path and query string + server: usually ip address + port: the http, ssh, ... port + suffix: extra text, e.g. the http path and query string ''' self.logger.debug("Getting VM CONSOLE from VIM") try: @@ -1368,14 +1540,14 @@ class vimconnector(vimconn.vimconnector): vm_id: #VIM id of this Virtual Machine status: #Mandatory. Text with one of: # DELETED (not found at vim) - # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) + # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) # OTHER (Vim reported other status not understood) # ERROR (VIM indicates an ERROR status) - # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running), + # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running), # CREATING (on building process), ERROR # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address # - error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR + error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR vim_info: #Text with plain information obtained from vim (yaml.safe_dump) interfaces: - vim_info: #Text with plain information obtained from vim (yaml.safe_dump) @@ -1398,10 +1570,9 @@ class vimconnector(vimconn.vimconnector): else: vm['status'] = "OTHER" vm['error_msg'] = "VIM status reported " + vm_vim['status'] - try: - vm['vim_info'] = yaml.safe_dump(vm_vim, default_flow_style=True, width=256) - except yaml.representer.RepresenterError: - vm['vim_info'] = str(vm_vim) + + vm['vim_info'] = self.serialize(vm_vim) + vm["interfaces"] = [] if vm_vim.get('fault'): vm['error_msg'] = str(vm_vim['fault']) @@ -1411,20 +1582,17 @@ class vimconnector(vimconn.vimconnector): port_dict = self.neutron.list_ports(device_id=vm_id) for port in port_dict["ports"]: interface={} - try: - interface['vim_info'] = yaml.safe_dump(port, default_flow_style=True, width=256) - except yaml.representer.RepresenterError: - interface['vim_info'] = str(port) + interface['vim_info'] = self.serialize(port) interface["mac_address"] = port.get("mac_address") interface["vim_net_id"] = port["network_id"] interface["vim_interface_id"] = port["id"] - # check if OS-EXT-SRV-ATTR:host is there, + # check if OS-EXT-SRV-ATTR:host is there, # in case of non-admin credentials, it will be missing if vm_vim.get('OS-EXT-SRV-ATTR:host'): interface["compute_node"] = vm_vim['OS-EXT-SRV-ATTR:host'] interface["pci"] = None - # check if binding:profile is there, + # check if binding:profile is there, # in case of non-admin credentials, it will be missing if port.get('binding:profile'): if port['binding:profile'].get('pci_slot'): @@ -1542,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: @@ -1572,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 @@ -1937,7 +2139,7 @@ class vimconnector(vimconn.vimconnector): self._reload_connection() # In networking-sfc the MPLS encapsulation is legacy # should be used when no full SFC Encapsulation is intended - sfc_encap = 'mpls' + correlation = 'mpls' if sfc_encap: correlation = 'nsh' sfp_dict = {'name': name,