X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=RO-VIM-openstack%2Fosm_rovim_openstack%2Fvimconn_openstack.py;h=0645e545b840d09617a0e0f1ec25ce4a9f9f343f;hb=239714b6df49d5d88a099e57006f1ac6e58a57a1;hp=289c8278e5669e662e91db4958082a8e4be28fd3;hpb=1ec592d80c7f07874b08a14984deb21fddb31441;p=osm%2FRO.git diff --git a/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py b/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py index 289c8278..0645e545 100644 --- a/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py +++ b/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py @@ -410,13 +410,16 @@ class vimconnector(vimconn.VimConnector): """Transform a keystone, nova, neutron exception into a vimconn exception discovering the cause""" message_error = str(exception) + tip = "" if isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound, ksExceptions.NotFound, gl1Exceptions.HTTPNotFound)): raise vimconn.VimConnNotFoundException(type(exception).__name__ + ": " + message_error) elif isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed)): - raise vimconn.VimConnConnectionException(type(exception).__name__ + ": " + message_error) + if type(exception).__name__ == "SSLError": + tip = " (maybe option 'insecure' must be added to the VIM)" + raise vimconn.VimConnConnectionException("Invalid URL or credentials{}: {}".format(tip, message_error)) elif isinstance(exception, (KeyError, nvExceptions.BadRequest, ksExceptions.BadRequest)): raise vimconn.VimConnException(type(exception).__name__ + ": " + message_error) elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException, @@ -566,9 +569,10 @@ class vimconnector(vimconn.VimConnector): provider_physical_network = provider_physical_network[0] if not provider_physical_network: - raise vimconn.VimConnConflictException("You must provide a 'dataplane_physical_net' at VIM_config " - "for creating underlay networks. or use the NS instantiation" - " parameter provider-network:physical-network for the VLD") + raise vimconn.VimConnConflictException( + "missing information needed for underlay networks. Provide 'dataplane_physical_net' " + "configuration at VIM or use the NS instantiation parameter 'provider-network.physical-network'" + " for the VLD") if not self.config.get('multisegment_support'): network_dict["provider:physical_network"] = provider_physical_network @@ -1221,20 +1225,21 @@ class vimconnector(vimconn.VimConnector): type: 'virtual', 'PCI-PASSTHROUGH'('PF'), 'SR-IOV'('VF'), 'VFnotShared' vim_id: filled/added by this function floating_ip: True/False (or it can be None) + port_security: True/False 'cloud_config': (optional) dictionary with: - 'key-pairs': (optional) list of strings with the public key to be inserted to the default user - 'users': (optional) list of users to be inserted, each item is a dict with: - 'name': (mandatory) user name, - 'key-pairs': (optional) list of strings with the public key to be inserted to the user - 'user-data': (optional) string is a text script to be passed directly to cloud-init - 'config-files': (optional). List of files to be transferred. Each item is a dict with: - 'dest': (mandatory) string with the destination absolute path - 'encoding': (optional, by default text). Can be one of: - 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64' - 'content' (mandatory): string with the content of the file - 'permissions': (optional) string with file permissions, typically octal notation '0644' - 'owner': (optional) file owner, string with the format 'owner:group' - 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk) + 'key-pairs': (optional) list of strings with the public key to be inserted to the default user + 'users': (optional) list of users to be inserted, each item is a dict with: + 'name': (mandatory) user name, + 'key-pairs': (optional) list of strings with the public key to be inserted to the user + 'user-data': (optional) string is a text script to be passed directly to cloud-init + 'config-files': (optional). List of files to be transferred. Each item is a dict with: + 'dest': (mandatory) string with the destination absolute path + 'encoding': (optional, by default text). Can be one of: + 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64' + 'content' (mandatory): string with the content of the file + 'permissions': (optional) string with file permissions, typically octal notation '0644' + 'owner': (optional) file owner, string with the format 'owner:group' + 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk) 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with: 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted 'size': (mandatory) string with the size of the disk in GB @@ -1334,7 +1339,7 @@ class vimconnector(vimconn.VimConnector): # is dropped. # As a workaround we wait until the VM is active and then disable the port-security if net.get("port_security") is False and not self.config.get("no_port_security_extension"): - no_secured_ports.append(new_port["port"]["id"]) + no_secured_ports.append((new_port["port"]["id"], net.get("port_security_disable_strategy"))) # if metadata_vpci: # metadata = {"pci_assignement": json.dumps(metadata_vpci)} @@ -1408,30 +1413,53 @@ class vimconnector(vimconn.VimConnector): if no_secured_ports: self.__wait_for_vm(server.id, 'ACTIVE') - for port_id in no_secured_ports: + for port in no_secured_ports: + port_update = { + "port": { + "port_security_enabled": False, + "security_groups": None + } + } + + if port[1] == "allow-address-pairs": + port_update = { + "port": { + "allowed_address_pairs": [ + { + "ip_address": "0.0.0.0/0" + } + ] + } + } + try: - self.neutron.update_port(port_id, - {"port": {"port_security_enabled": False, "security_groups": None}}) + self.neutron.update_port(port[0], port_update) except Exception: - raise vimconn.VimConnException("It was not possible to disable port security for port {}".format( - port_id)) + raise vimconn.VimConnException( + "It was not possible to disable port security for port {}" + .format(port[0]) + ) + # print "DONE :-)", server # pool_id = None - if external_network: - floating_ips = self.neutron.list_floatingips().get("floatingips", ()) for floating_network in external_network: try: assigned = False + floating_ip_retries = 3 + # In case of RO in HA there can be conflicts, two RO trying to assign same floating IP, so retry + # several times while not assigned: - if floating_ips: - ip = floating_ips.pop(0) - if ip.get("port_id", False) or ip.get('tenant_id') != server.tenant_id: + floating_ips = self.neutron.list_floatingips().get("floatingips", ()) + random.shuffle(floating_ips) # randomize + for fip in floating_ips: + if fip.get("port_id") or fip.get('tenant_id') != server.tenant_id: continue if isinstance(floating_network['floating_ip'], str): - if ip.get("floating_network_id") != floating_network['floating_ip']: + if fip.get("floating_network_id") != floating_network['floating_ip']: continue - free_floating_ip = ip["id"] + free_floating_ip = fip["id"] + break else: if isinstance(floating_network['floating_ip'], str) and \ floating_network['floating_ip'].lower() != "true": @@ -1458,31 +1486,44 @@ class vimconnector(vimconn.VimConnector): # self.logger.debug("Creating floating IP") new_floating_ip = self.neutron.create_floatingip(param) free_floating_ip = new_floating_ip['floatingip']['id'] + created_items["floating_ip:" + str(free_floating_ip)] = True except Exception as e: raise vimconn.VimConnException(type(e).__name__ + ": Cannot create new floating_ip " + str(e), http_code=vimconn.HTTP_Conflict) - while not assigned: - try: - # the vim_id key contains the neutron.port_id - self.neutron.update_floatingip(free_floating_ip, - {"floatingip": {"port_id": floating_network["vim_id"]}}) - # Using nove is deprecated on nova client 10.0 - assigned = True - except Exception as e: - # openstack need some time after VM creation to asign an IP. So retry if fails - vm_status = self.nova.servers.get(server.id).status - if vm_status != 'ACTIVE' and vm_status != 'ERROR': - if time.time() - vm_start_time < server_timeout: - time.sleep(5) - continue - raise vimconn.VimConnException( - "Cannot create floating_ip: {} {}".format(type(e).__name__, e), - http_code=vimconn.HTTP_Conflict) + try: + # for race condition ensure not already assigned + fip = self.neutron.show_floatingip(free_floating_ip) + if fip['floatingip']['port_id']: + continue + # the vim_id key contains the neutron.port_id + self.neutron.update_floatingip(free_floating_ip, + {"floatingip": {"port_id": floating_network["vim_id"]}}) + # for race condition ensure not re-assigned to other VM after 5 seconds + time.sleep(5) + fip = self.neutron.show_floatingip(free_floating_ip) + if fip['floatingip']['port_id'] != floating_network["vim_id"]: + self.logger.error("floating_ip {} re-assigned to other port".format(free_floating_ip)) + continue + self.logger.debug("Assigned floating_ip {} to VM {}".format(free_floating_ip, server.id)) + assigned = True + except Exception as e: + # openstack need some time after VM creation to assign an IP. So retry if fails + vm_status = self.nova.servers.get(server.id).status + if vm_status not in ('ACTIVE', 'ERROR'): + if time.time() - vm_start_time < server_timeout: + time.sleep(5) + continue + elif floating_ip_retries > 0: + floating_ip_retries -= 1 + continue + raise vimconn.VimConnException( + "Cannot create floating_ip: {} {}".format(type(e).__name__, e), + http_code=vimconn.HTTP_Conflict) except Exception as e: if not floating_network['exit_on_floating_ip_error']: - self.logger.warning("Cannot create floating_ip. %s", str(e)) + self.logger.error("Cannot create floating_ip. %s", str(e)) continue raise @@ -1614,8 +1655,13 @@ class vimconnector(vimconn.VimConnector): keep_waiting = True else: self.cinder.volumes.delete(k_id) + created_items[k] = None + elif k_item == "floating_ip": # floating ip + self.neutron.delete_floatingip(k_id) + created_items[k] = None + except Exception as e: - self.logger.error("Error deleting volume: {}: {}".format(type(e).__name__, e)) + self.logger.error("Error deleting {}: {}".format(k, e)) if keep_waiting: time.sleep(1) elapsed_time += 1 @@ -1661,7 +1707,8 @@ class vimconnector(vimconn.VimConnector): else: vm['status'] = "OTHER" vm['error_msg'] = "VIM status reported " + vm_vim['status'] - + vm_vim.pop("OS-EXT-SRV-ATTR:user_data", None) + vm_vim.pop("user_data", None) vm['vim_info'] = self.serialize(vm_vim) vm["interfaces"] = []