From: tierno Date: Tue, 7 Feb 2017 12:13:39 +0000 (+0100) Subject: Merge "Merge branch 'v1.1'" X-Git-Tag: v2.0.0~45 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=fb1dce6f90f40705665dc615888a67e82aad798b;hp=96af9f4a92b158d07417cacf2e3573339680895b;p=osm%2FRO.git Merge "Merge branch 'v1.1'" --- diff --git a/console_proxy_thread.py b/console_proxy_thread.py index 67da12c6..460a4aaa 100644 --- a/console_proxy_thread.py +++ b/console_proxy_thread.py @@ -39,6 +39,7 @@ __date__ ="$19-nov-2015 09:07:15$" import socket import select import threading +import logging class ConsoleProxyException(Exception): @@ -50,7 +51,7 @@ class ConsoleProxyThread(threading.Thread): buffer_size = 4096 check_finish = 1 #frequency to check if requested to end in seconds - def __init__(self, host, port, console_host, console_port): + def __init__(self, host, port, console_host, console_port, log_level=None): try: threading.Thread.__init__(self) self.console_host = console_host @@ -69,6 +70,10 @@ class ConsoleProxyThread(threading.Thread): self.input_list = [self.server] self.channel = {} self.terminate = False #put at True from outside to force termination + self.logger = logging.getLogger('openmano.console') + if log_level: + self.logger.setLevel( getattr(logging, log_level) ) + except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: if e is socket.error and e.errno==98: raise ConsoleProxyExceptionPortUsed("socket.error " + str(e)) @@ -79,12 +84,12 @@ class ConsoleProxyThread(threading.Thread): try: inputready, _, _ = select.select(self.input_list, [], [], self.check_finish) except select.error as e: - print self.name, ": Exception on select %s: %s" % (type(e).__name__, str(e) ) + self.logger.error("Exception on select %s: %s", type(e).__name__, str(e) ) self.on_terminate() if self.terminate: self.on_terminate() - print self.name, ": Terminate because commanded" + self.logger.debug("Terminate because commanded") break for sock in inputready: @@ -106,7 +111,7 @@ class ConsoleProxyThread(threading.Thread): try: clientsock, clientaddr = self.server.accept() except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: - print self.name, ": Exception on_accept %s: %s" % (type(e).__name__, str(e) ) + self.logger.error("Exception on_accept %s: %s", type(e).__name__, str(e) ) return False #print self.name, ": Accept new client ", clientaddr @@ -116,7 +121,7 @@ class ConsoleProxyThread(threading.Thread): forward.connect((self.console_host, self.console_port)) name = "%s:%d => (%s:%d => %s:%d) => %s:%d" %\ (clientsock.getpeername() + clientsock.getsockname() + forward.getsockname() + forward.getpeername() ) - print self.name, ": new connection " + name + self.logger.warn("new connection " + name) self.input_list.append(clientsock) self.input_list.append(forward) @@ -128,8 +133,8 @@ class ConsoleProxyThread(threading.Thread): self.channel[forward] = info return True except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: - print self.name, ": Exception on_connect to server %s:%d; %s: %s" % (self.console_host, self.console_port, type(e).__name__, str(e) ) - print self.name, ": Close client side ", clientaddr + self.logger.error("Exception on_connect to server %s:%d; %s: %s Close client side %s", + self.console_host, self.console_port, type(e).__name__, str(e), str(clientaddr) ) clientsock.close() return False @@ -139,18 +144,18 @@ class ConsoleProxyThread(threading.Thread): info = self.channel[sock] #debug info sockname = "client" if sock is info["clientsock"] else "server" - print self.name, ": del connection %s %s at %s side" % (info["name"], cause, sockname) + self.logger.warn("del connection %s %s at %s side", info["name"], str(cause), str(sockname) ) #close sockets try: # close the connection with client info["clientsock"].close() # equivalent to do self.s.close() except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: - print self.name, ": Exception on_close client socket %s: %s" % (type(e).__name__, str(e) ) + self.logger.error("Exception on_close client socket %s: %s", type(e).__name__, str(e) ) try: # close the connection with remote server info["serversock"].close() except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: - print self.name, ": Exception on_close server socket %s: %s" % (type(e).__name__, str(e) ) + self.logger.error("Exception on_close server socket %s: %s", type(e).__name__, str(e) ) #remove objects from input_list self.input_list.remove(info["clientsock"]) diff --git a/httpserver.py b/httpserver.py index 9dd537f2..64eddfa5 100644 --- a/httpserver.py +++ b/httpserver.py @@ -996,14 +996,14 @@ def http_post_scenarios(tenant_id): def http_post_scenario_action(tenant_id, scenario_id): '''take an action over a scenario''' logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url) - #check valid tenant_id + # parse input data + http_content, _ = format_in(scenario_action_schema) + r = utils.remove_extra_items(http_content, scenario_action_schema) + if r: + logger.debug("Remove received extra items %s", str(r)) try: - nfvo.check_tenant(mydb, tenant_id) - #parse input data - http_content,_ = format_in( scenario_action_schema ) - r = utils.remove_extra_items(http_content, scenario_action_schema) - if r: - logger.debug("Remove received extra items %s", str(r)) + # check valid tenant_id + nfvo.check_tenant(mydb, tenant_id) if "start" in http_content: data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \ http_content['start'].get('description',http_content['start']['instance_name']), @@ -1086,10 +1086,11 @@ def http_get_scenario_id(tenant_id, scenario_id): @bottle.route(url_base + '//scenarios/', method='DELETE') def http_delete_scenario_id(tenant_id, scenario_id): '''delete a scenario from database, can use both uuid or name''' + logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url) try: #check valid tenant_id if tenant_id != "any": - nfvo.check_tenant(mydb, tenant_id) + nfvo.check_tenant(mydb, tenant_id) #obtain data data = mydb.delete_scenario(scenario_id, tenant_id) #print json.dumps(data, indent=4) @@ -1126,15 +1127,15 @@ def http_put_scenario_id(tenant_id, scenario_id): def http_post_instances(tenant_id): '''create an instance-scenario''' logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url) + # parse input data + http_content, used_schema = format_in(instance_scenario_create_schema_v01) + r = utils.remove_extra_items(http_content, used_schema) + if r is not None: + logger.warning("http_post_instances: Warning: remove extra items %s", str(r)) try: #check valid tenant_id if tenant_id != "any": nfvo.check_tenant(mydb, tenant_id) - #parse input data - http_content,used_schema = format_in( instance_scenario_create_schema_v01) - r = utils.remove_extra_items(http_content, used_schema) - if r is not None: - logger.warning("http_post_instances: Warning: remove extra items %s", str(r)) data = nfvo.create_instance(mydb, tenant_id, http_content["instance"]) return format_out(data) except (nfvo.NfvoException, db_base_Exception) as e: @@ -1225,16 +1226,16 @@ def http_delete_instance_id(tenant_id, instance_id): def http_post_instance_scenario_action(tenant_id, instance_id): '''take an action over a scenario instance''' logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url) + # parse input data + http_content, _ = format_in(instance_scenario_action_schema) + r = utils.remove_extra_items(http_content, instance_scenario_action_schema) + if r: + logger.debug("Remove received extra items %s", str(r)) try: #check valid tenant_id if tenant_id != "any": nfvo.check_tenant(mydb, tenant_id) - #parse input data - http_content,_ = format_in( instance_scenario_action_schema ) - r = utils.remove_extra_items(http_content, instance_scenario_action_schema) - if r: - logger.debug("Remove received extra items %s", str(r)) #print "http_post_instance_scenario_action input: ", http_content #obtain data instance = mydb.get_instance_scenario(instance_id, tenant_id) diff --git a/nfvo.py b/nfvo.py index 7537c756..c8ef020d 100644 --- a/nfvo.py +++ b/nfvo.py @@ -194,28 +194,30 @@ def rollback(mydb, vims, rollback_list): else: return False," Rollback fails to delete: " + str(undeleted_items) -def check_vnf_descriptor(vnf_descriptor): +def check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1): global global_config #create a dictionary with vnfc-name: vnfc:interface-list key:values pairs vnfc_interfaces={} for vnfc in vnf_descriptor["vnf"]["VNFC"]: - name_list = [] + name_dict = {} #dataplane interfaces for numa in vnfc.get("numas",() ): for interface in numa.get("interfaces",()): - if interface["name"] in name_list: - raise NfvoException("Error at vnf:VNFC[name:'{}']:numas:interfaces:name, interface name '{}' already used in this VNFC"\ - .format(vnfc["name"], interface["name"]), - HTTP_Bad_Request) - name_list.append( interface["name"] ) + if interface["name"] in name_dict: + raise NfvoException( + "Error at vnf:VNFC[name:'{}']:numas:interfaces:name, interface name '{}' already used in this VNFC".format( + vnfc["name"], interface["name"]), + HTTP_Bad_Request) + name_dict[ interface["name"] ] = "underlay" #bridge interfaces for interface in vnfc.get("bridge-ifaces",() ): - if interface["name"] in name_list: - raise NfvoException("Error at vnf:VNFC[name:'{}']:bridge-ifaces:name, interface name '{}' already used in this VNFC"\ - .format(vnfc["name"], interface["name"]), - HTTP_Bad_Request) - name_list.append( interface["name"] ) - vnfc_interfaces[ vnfc["name"] ] = name_list + if interface["name"] in name_dict: + raise NfvoException( + "Error at vnf:VNFC[name:'{}']:bridge-ifaces:name, interface name '{}' already used in this VNFC".format( + vnfc["name"], interface["name"]), + HTTP_Bad_Request) + name_dict[ interface["name"] ] = "overlay" + vnfc_interfaces[ vnfc["name"] ] = name_dict # check bood-data info if "boot-data" in vnfc: # check that user-data is incompatible with users and config-files @@ -228,43 +230,83 @@ def check_vnf_descriptor(vnf_descriptor): name_list=[] for external_connection in vnf_descriptor["vnf"].get("external-connections",() ): if external_connection["name"] in name_list: - raise NfvoException("Error at vnf:external-connections:name, value '{}' already used as an external-connection"\ - .format(external_connection["name"]), - HTTP_Bad_Request) + raise NfvoException( + "Error at vnf:external-connections:name, value '{}' already used as an external-connection".format( + external_connection["name"]), + HTTP_Bad_Request) name_list.append(external_connection["name"]) if external_connection["VNFC"] not in vnfc_interfaces: - raise NfvoException("Error at vnf:external-connections[name:'{}']:VNFC, value '{}' does not match any VNFC"\ - .format(external_connection["name"], external_connection["VNFC"]), - HTTP_Bad_Request) + raise NfvoException( + "Error at vnf:external-connections[name:'{}']:VNFC, value '{}' does not match any VNFC".format( + external_connection["name"], external_connection["VNFC"]), + HTTP_Bad_Request) if external_connection["local_iface_name"] not in vnfc_interfaces[ external_connection["VNFC"] ]: - raise NfvoException("Error at vnf:external-connections[name:'{}']:local_iface_name, value '{}' does not match any interface of this VNFC"\ - .format(external_connection["name"], external_connection["local_iface_name"]), - HTTP_Bad_Request ) + raise NfvoException( + "Error at vnf:external-connections[name:'{}']:local_iface_name, value '{}' does not match any interface of this VNFC".format( + external_connection["name"], + external_connection["local_iface_name"]), + HTTP_Bad_Request ) #check if the info in internal_connections matches with the one in the vnfcs name_list=[] for internal_connection in vnf_descriptor["vnf"].get("internal-connections",() ): if internal_connection["name"] in name_list: - raise NfvoException("Error at vnf:internal-connections:name, value '%s' already used as an internal-connection"\ - .format(internal_connection["name"]), - HTTP_Bad_Request) + raise NfvoException( + "Error at vnf:internal-connections:name, value '%s' already used as an internal-connection".format( + internal_connection["name"]), + HTTP_Bad_Request) name_list.append(internal_connection["name"]) #We should check that internal-connections of type "ptp" have only 2 elements - if len(internal_connection["elements"])>2 and internal_connection["type"] == "ptp": - raise NfvoException("Error at vnf:internal-connections[name:'{}']:elements, size must be 2 for a type:'ptp'"\ - .format(internal_connection["name"]), - HTTP_Bad_Request) + + if len(internal_connection["elements"])>2 and (internal_connection.get("type") == "ptp" or internal_connection.get("type") == "e-line"): + raise NfvoException( + "Error at 'vnf:internal-connections[name:'{}']:elements', size must be 2 for a '{}' type. Consider change it to '{}' type".format( + internal_connection["name"], + 'ptp' if vnf_descriptor_version==1 else 'e-line', + 'data' if vnf_descriptor_version==1 else "e-lan"), + HTTP_Bad_Request) for port in internal_connection["elements"]: - if port["VNFC"] not in vnfc_interfaces: - raise NfvoException("Error at vnf:internal-connections[name:'{}']:elements[]:VNFC, value '{}' does not match any VNFC"\ - .format(internal_connection["name"], port["VNFC"]), - HTTP_Bad_Request) - if port["local_iface_name"] not in vnfc_interfaces[ port["VNFC"] ]: - raise NfvoException("Error at vnf:internal-connections[name:'{}']:elements[]:local_iface_name, value '{}' does not match any interface of this VNFC"\ - .format(internal_connection["name"], port["local_iface_name"]), - HTTP_Bad_Request) - return -HTTP_Bad_Request, + vnf = port["VNFC"] + iface = port["local_iface_name"] + if vnf not in vnfc_interfaces: + raise NfvoException( + "Error at vnf:internal-connections[name:'{}']:elements[]:VNFC, value '{}' does not match any VNFC".format( + internal_connection["name"], vnf), + HTTP_Bad_Request) + if iface not in vnfc_interfaces[ vnf ]: + raise NfvoException( + "Error at vnf:internal-connections[name:'{}']:elements[]:local_iface_name, value '{}' does not match any interface of this VNFC".format( + internal_connection["name"], iface), + HTTP_Bad_Request) + return -HTTP_Bad_Request, + if vnf_descriptor_version==1 and "type" not in internal_connection: + if vnfc_interfaces[vnf][iface] == "overlay": + internal_connection["type"] = "bridge" + else: + internal_connection["type"] = "data" + if vnf_descriptor_version==2 and "implementation" not in internal_connection: + if vnfc_interfaces[vnf][iface] == "overlay": + internal_connection["implementation"] = "overlay" + else: + internal_connection["implementation"] = "underlay" + if (internal_connection.get("type") == "data" or internal_connection.get("type") == "ptp" or \ + internal_connection.get("implementation") == "underlay") and vnfc_interfaces[vnf][iface] == "overlay": + raise NfvoException( + "Error at vnf:internal-connections[name:'{}']:elements[]:{}, interface of type {} connected to an {} network".format( + internal_connection["name"], + iface, 'bridge' if vnf_descriptor_version==1 else 'overlay', + 'data' if vnf_descriptor_version==1 else 'underlay'), + HTTP_Bad_Request) + if (internal_connection.get("type") == "bridge" or internal_connection.get("implementation") == "overlay") and \ + vnfc_interfaces[vnf][iface] == "underlay": + raise NfvoException( + "Error at vnf:internal-connections[name:'{}']:elements[]:{}, interface of type {} connected to an {} network".format( + internal_connection["name"], iface, + 'data' if vnf_descriptor_version==1 else 'underlay', + 'bridge' if vnf_descriptor_version==1 else 'overlay'), + HTTP_Bad_Request) + def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vim=False, return_on_error = None): #look if image exist @@ -322,7 +364,8 @@ def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vi rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"image","uuid":image_vim_id}) image_created="true" else: - raise vimconn.vimconnException("Cannot create image without location") + #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: if return_on_error: logger.error("Error creating image at VIM '%s': %s", vim["name"], str(e)) @@ -465,9 +508,16 @@ def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_ #create flavor at vim logger.debug("nfvo.create_or_use_flavor() adding flavor to VIM %s", vim["name"]) try: - 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" + flavor_vim_id = None + flavor_vim_id=vim.get_flavor_id_from_data(flavor_dict) + flavor_create="false" + 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: if return_on_error: logger.error("Error creating flavor at VIM %s: %s.", vim["name"], str(e)) @@ -496,8 +546,9 @@ def new_vnf(mydb, tenant_id, vnf_descriptor): global global_config # Step 1. Check the VNF descriptor - check_vnf_descriptor(vnf_descriptor) + check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1) # Step 2. Check tenant exist + vims = {} if tenant_id != "any": check_tenant(mydb, tenant_id) if "tenant_id" in vnf_descriptor["vnf"]: @@ -507,9 +558,8 @@ def new_vnf(mydb, tenant_id, vnf_descriptor): else: vnf_descriptor['vnf']['tenant_id'] = tenant_id # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter - vims = get_vim(mydb, tenant_id) - else: - vims={} + if global_config["auto_push_VNF_to_VIMs"]: + vims = get_vim(mydb, tenant_id) # Step 4. Review the descriptor and add missing fields #print vnf_descriptor @@ -519,17 +569,7 @@ def new_vnf(mydb, tenant_id, vnf_descriptor): if "physical" in vnf_descriptor['vnf']: del vnf_descriptor['vnf']['physical'] #print vnf_descriptor - # Step 5. Check internal connections - # TODO: to be moved to step 1???? - internal_connections=vnf_descriptor['vnf'].get('internal_connections',[]) - for ic in internal_connections: - if len(ic['elements'])>2 and ic['type']=='ptp': - raise NfvoException("Mismatch 'type':'ptp' with {} elements at 'vnf':'internal-conections'['name':'{}']. Change 'type' to 'data'".format(len(ic), ic['name']), - HTTP_Bad_Request) - elif len(ic['elements'])==2 and ic['type']=='data': - raise NfvoException("Mismatch 'type':'data' with 2 elements at 'vnf':'internal-conections'['name':'{}']. Change 'type' to 'ptp'".format(ic['name']), - HTTP_Bad_Request) - + # Step 6. For each VNFC in the descriptor, flavors and images are created in the VIM logger.debug('BEGIN creation of VNF "%s"' % vnf_name) logger.debug("VNF %s: consisting of %d VNFC(s)" % (vnf_name,len(vnf_descriptor['vnf']['VNFC']))) @@ -640,8 +680,9 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor): global global_config # Step 1. Check the VNF descriptor - check_vnf_descriptor(vnf_descriptor) + check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=2) # Step 2. Check tenant exist + vims = {} if tenant_id != "any": check_tenant(mydb, tenant_id) if "tenant_id" in vnf_descriptor["vnf"]: @@ -651,9 +692,8 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor): else: vnf_descriptor['vnf']['tenant_id'] = tenant_id # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter - vims = get_vim(mydb, tenant_id) - else: - vims={} + if global_config["auto_push_VNF_to_VIMs"]: + vims = get_vim(mydb, tenant_id) # Step 4. Review the descriptor and add missing fields #print vnf_descriptor @@ -663,14 +703,7 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor): if "physical" in vnf_descriptor['vnf']: del vnf_descriptor['vnf']['physical'] #print vnf_descriptor - # Step 5. Check internal connections - # TODO: to be moved to step 1???? - internal_connections=vnf_descriptor['vnf'].get('internal_connections',[]) - for ic in internal_connections: - if len(ic['elements'])>2 and ic['type']=='e-line': - raise NfvoException("Mismatch 'type':'e-line' with {} elements at 'vnf':'internal-conections'['name':'{}']. Change 'type' to 'e-lan'".format(len(ic), ic['name']), - HTTP_Bad_Request) - + # Step 6. For each VNFC in the descriptor, flavors and images are created in the VIM logger.debug('BEGIN creation of VNF "%s"' % vnf_name) logger.debug("VNF %s: consisting of %d VNFC(s)" % (vnf_name,len(vnf_descriptor['vnf']['VNFC']))) @@ -2759,6 +2792,8 @@ def vim_action_get(mydb, tenant_id, datacenter, item, name): content = myvim.get_network_list(filter_dict=filter_dict) elif item=="tenants": content = myvim.get_tenant_list(filter_dict=filter_dict) + elif item == "images": + content = myvim.get_image_list(filter_dict=filter_dict) else: raise NfvoException(item + "?", HTTP_Method_Not_Allowed) logger.debug("vim_action response %s", content) #update nets Change from VIM format to NFVO format @@ -2796,6 +2831,8 @@ def vim_action_delete(mydb, tenant_id, datacenter, item, name): content = myvim.delete_network(item_id) elif item=="tenants": content = myvim.delete_tenant(item_id) + elif item == "images": + content = myvim.delete_image(item_id) else: raise NfvoException(item + "?", HTTP_Method_Not_Allowed) except vimconn.vimconnException as e: diff --git a/nfvo_db.py b/nfvo_db.py index 11ae450a..50f258c6 100644 --- a/nfvo_db.py +++ b/nfvo_db.py @@ -75,24 +75,13 @@ class nfvo_db(db_base.db_base): #print "Internal vm id in NFVO DB: %s" % vm_id vmDict[vm['name']] = vm_id - #Collect the data interfaces of each VM/VNFC under the 'numas' field - dataifacesDict = {} - for vm in vnf_descriptor['vnf']['VNFC']: - dataifacesDict[vm['name']] = {} - for numa in vm.get('numas', []): - for dataiface in numa.get('interfaces',[]): - db_base._convert_bandwidth(dataiface, logger=self.logger) - dataifacesDict[vm['name']][dataiface['name']] = {} - dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface['vpci'] - dataifacesDict[vm['name']][dataiface['name']]['bw'] = dataiface['bandwidth'] - dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface['dedicated']=="yes" else ("VF" if dataiface['dedicated']=="no" else "VFnotShared") - #Collect the bridge interfaces of each VM/VNFC under the 'bridge-ifaces' field bridgeInterfacesDict = {} for vm in vnf_descriptor['vnf']['VNFC']: if 'bridge-ifaces' in vm: bridgeInterfacesDict[vm['name']] = {} for bridgeiface in vm['bridge-ifaces']: + created_time += 0.00001 if 'port-security' in bridgeiface: bridgeiface['port_security'] = bridgeiface.pop('port-security') if 'floating-ip' in bridgeiface: @@ -107,7 +96,24 @@ class nfvo_db(db_base.db_base): int(bridgeiface.get('port_security', True)) bridgeInterfacesDict[vm['name']][bridgeiface['name']]['floating_ip'] = \ int(bridgeiface.get('floating_ip', False)) - + bridgeInterfacesDict[vm['name']][bridgeiface['name']]['created_time'] = created_time + + # Collect the data interfaces of each VM/VNFC under the 'numas' field + dataifacesDict = {} + for vm in vnf_descriptor['vnf']['VNFC']: + dataifacesDict[vm['name']] = {} + for numa in vm.get('numas', []): + for dataiface in numa.get('interfaces', []): + created_time += 0.00001 + db_base._convert_bandwidth(dataiface, logger=self.logger) + dataifacesDict[vm['name']][dataiface['name']] = {} + dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface['vpci'] + dataifacesDict[vm['name']][dataiface['name']]['bw'] = dataiface['bandwidth'] + dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface[ + 'dedicated'] == "yes" else ( + "VF" if dataiface['dedicated'] == "no" else "VFnotShared") + dataifacesDict[vm['name']][dataiface['name']]['created_time'] = created_time + #For each internal connection, we add it to the interfaceDict and we create the appropriate net in the NFVO database. #print "Adding new nets (VNF internal nets) to the NFVO database (if any)" internalconnList = [] @@ -133,26 +139,27 @@ class nfvo_db(db_base.db_base): ifaceItem["net_id"] = net_id ifaceItem["type"] = net['type'] if ifaceItem ["type"] == "data": - ifaceItem["vpci"] = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['vpci'] - ifaceItem["bw"] = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['bw'] - ifaceItem["model"] = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['model'] + dataiface = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ] + ifaceItem["vpci"] = dataiface['vpci'] + ifaceItem["bw"] = dataiface['bw'] + ifaceItem["model"] = dataiface['model'] + created_time_iface = dataiface['created_time'] else: - ifaceItem["vpci"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['vpci'] - ifaceItem["mac"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['mac_address'] - ifaceItem["bw"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['bw'] - ifaceItem["model"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['model'] - ifaceItem["port_security"] = \ - bridgeInterfacesDict[element['VNFC']][element['local_iface_name']]['port_security'] - ifaceItem["floating_ip"] = \ - bridgeInterfacesDict[element['VNFC']][element['local_iface_name']]['floating_ip'] + bridgeiface = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ] + ifaceItem["vpci"] = bridgeiface['vpci'] + ifaceItem["mac"] = bridgeiface['mac'] + ifaceItem["bw"] = bridgeiface['bw'] + ifaceItem["model"] = bridgeiface['model'] + ifaceItem["port_security"] = bridgeiface['port_security'] + ifaceItem["floating_ip"] = bridgeiface['floating_ip'] + created_time_iface = bridgeiface['created_time'] internalconnList.append(ifaceItem) #print "Internal net id in NFVO DB: %s" % net_id #print "Adding internal interfaces to the NFVO database (if any)" for iface in internalconnList: - print "Iface name: %s" % iface['internal_name'] - created_time += 0.00001 - iface_id = self._new_row_internal('interfaces', iface, add_uuid=True, root_uuid=vnf_id, created_time=created_time) + #print "Iface name: %s" % iface['internal_name'] + iface_id = self._new_row_internal('interfaces', iface, add_uuid=True, root_uuid=vnf_id, created_time = created_time_iface) #print "Iface id in NFVO DB: %s" % iface_id #print "Adding external interfaces to the NFVO database" @@ -165,21 +172,22 @@ class nfvo_db(db_base.db_base): myIfaceDict["external_name"] = iface['name'] myIfaceDict["type"] = iface['type'] if iface["type"] == "data": - myIfaceDict["vpci"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci'] - myIfaceDict["bw"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw'] - myIfaceDict["model"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model'] + dataiface = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ] + myIfaceDict["vpci"] = dataiface['vpci'] + myIfaceDict["bw"] = dataiface['bw'] + myIfaceDict["model"] = dataiface['model'] + created_time_iface = dataiface['created_time'] else: - myIfaceDict["vpci"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci'] - myIfaceDict["bw"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw'] - myIfaceDict["model"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model'] - myIfaceDict["mac"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['mac'] - myIfaceDict["port_security"] = \ - bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['port_security'] - myIfaceDict["floating_ip"] = \ - bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['floating_ip'] - print "Iface name: %s" % iface['name'] - created_time += 0.00001 - iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time) + bridgeiface = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ] + myIfaceDict["vpci"] = bridgeiface['vpci'] + myIfaceDict["bw"] = bridgeiface['bw'] + myIfaceDict["model"] = bridgeiface['model'] + myIfaceDict["mac"] = bridgeiface['mac'] + myIfaceDict["port_security"]= bridgeiface['port_security'] + myIfaceDict["floating_ip"] = bridgeiface['floating_ip'] + created_time_iface = bridgeiface['created_time'] + #print "Iface name: %s" % iface['name'] + iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time = created_time_iface) #print "Iface id in NFVO DB: %s" % iface_id return vnf_id @@ -219,42 +227,46 @@ class nfvo_db(db_base.db_base): #print "Internal vm id in NFVO DB: %s" % vm_id vmDict[vm['name']] = vm_id - #Collect the data interfaces of each VM/VNFC under the 'numas' field - dataifacesDict = {} - for vm in vnf_descriptor['vnf']['VNFC']: - dataifacesDict[vm['name']] = {} - for numa in vm.get('numas', []): - for dataiface in numa.get('interfaces',[]): - db_base._convert_bandwidth(dataiface, logger=self.logger) - dataifacesDict[vm['name']][dataiface['name']] = {} - dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface['vpci'] - dataifacesDict[vm['name']][dataiface['name']]['bw'] = dataiface['bandwidth'] - dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface['dedicated']=="yes" else ("VF" if dataiface['dedicated']=="no" else "VFnotShared") - #Collect the bridge interfaces of each VM/VNFC under the 'bridge-ifaces' field bridgeInterfacesDict = {} for vm in vnf_descriptor['vnf']['VNFC']: if 'bridge-ifaces' in vm: bridgeInterfacesDict[vm['name']] = {} for bridgeiface in vm['bridge-ifaces']: + created_time += 0.00001 db_base._convert_bandwidth(bridgeiface, logger=self.logger) if 'port-security' in bridgeiface: bridgeiface['port_security'] = bridgeiface.pop('port-security') if 'floating-ip' in bridgeiface: bridgeiface['floating_ip'] = bridgeiface.pop('floating-ip') - bridgeInterfacesDict[vm['name']][bridgeiface['name']] = {} - bridgeInterfacesDict[vm['name']][bridgeiface['name']]['vpci'] = bridgeiface.get('vpci',None) - bridgeInterfacesDict[vm['name']][bridgeiface['name']]['mac'] = bridgeiface.get('mac_address',None) - bridgeInterfacesDict[vm['name']][bridgeiface['name']]['bw'] = bridgeiface.get('bandwidth', None) - bridgeInterfacesDict[vm['name']][bridgeiface['name']]['model'] = bridgeiface.get('model', None) - bridgeInterfacesDict[vm['name']][bridgeiface['name']]['port_security'] = \ - int(bridgeiface.get('port_security', True)) - bridgeInterfacesDict[vm['name']][bridgeiface['name']]['floating_ip'] = \ - int(bridgeiface.get('floating_ip', False)) - + ifaceDict = {} + ifaceDict['vpci'] = bridgeiface.get('vpci',None) + ifaceDict['mac'] = bridgeiface.get('mac_address',None) + ifaceDict['bw'] = bridgeiface.get('bandwidth', None) + ifaceDict['model'] = bridgeiface.get('model', None) + ifaceDict['port_security'] = int(bridgeiface.get('port_security', True)) + ifaceDict['floating_ip'] = int(bridgeiface.get('floating_ip', False)) + ifaceDict['created_time'] = created_time + bridgeInterfacesDict[vm['name']][bridgeiface['name']] = ifaceDict + + # Collect the data interfaces of each VM/VNFC under the 'numas' field + dataifacesDict = {} + for vm in vnf_descriptor['vnf']['VNFC']: + dataifacesDict[vm['name']] = {} + for numa in vm.get('numas', []): + for dataiface in numa.get('interfaces', []): + created_time += 0.00001 + db_base._convert_bandwidth(dataiface, logger=self.logger) + ifaceDict = {} + ifaceDict['vpci'] = dataiface['vpci'] + ifaceDict['bw'] = dataiface['bandwidth'] + ifaceDict['model'] = "PF" if dataiface['dedicated'] == "yes" else \ + ("VF" if dataiface['dedicated'] == "no" else "VFnotShared") + ifaceDict['created_time'] = created_time + dataifacesDict[vm['name']][dataiface['name']] = ifaceDict + #For each internal connection, we add it to the interfaceDict and we create the appropriate net in the NFVO database. #print "Adding new nets (VNF internal nets) to the NFVO database (if any)" - internalconnList = [] if 'internal-connections' in vnf_descriptor['vnf']: for net in vnf_descriptor['vnf']['internal-connections']: #print "Net name: %s. Description: %s" % (net['name'], net['description']) @@ -303,27 +315,22 @@ class nfvo_db(db_base.db_base): ifaceItem["type"] = net['type'] ifaceItem["ip_address"] = element.get('ip_address',None) if ifaceItem ["type"] == "data": - ifaceItem["vpci"] = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['vpci'] - ifaceItem["bw"] = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['bw'] - ifaceItem["model"] = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['model'] + ifaceDict = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ] + ifaceItem["vpci"] = ifaceDict['vpci'] + ifaceItem["bw"] = ifaceDict['bw'] + ifaceItem["model"] = ifaceDict['model'] else: - ifaceItem["vpci"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['vpci'] - ifaceItem["mac"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['mac'] - ifaceItem["bw"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['bw'] - ifaceItem["model"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['model'] - ifaceItem["port_security"] = \ - bridgeInterfacesDict[element['VNFC']][element['local_iface_name']]['port_security'] - ifaceItem["floating_ip"] = \ - bridgeInterfacesDict[element['VNFC']][element['local_iface_name']]['floating_ip'] - internalconnList.append(ifaceItem) - #print "Internal net id in NFVO DB: %s" % net_id - - #print "Adding internal interfaces to the NFVO database (if any)" - for iface in internalconnList: - print "Iface name: %s" % iface['internal_name'] - created_time += 0.00001 - iface_id = self._new_row_internal('interfaces', iface, add_uuid=True, root_uuid=vnf_id, created_time=created_time) - #print "Iface id in NFVO DB: %s" % iface_id + ifaceDict = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ] + ifaceItem["vpci"] = ifaceDict['vpci'] + ifaceItem["mac"] = ifaceDict['mac'] + ifaceItem["bw"] = ifaceDict['bw'] + ifaceItem["model"] = ifaceDict['model'] + ifaceItem["port_security"] = ifaceDict['port_security'] + ifaceItem["floating_ip"] = ifaceDict['floating_ip'] + created_time_iface = ifaceDict["created_time"] + #print "Iface name: %s" % iface['internal_name'] + iface_id = self._new_row_internal('interfaces', ifaceItem, add_uuid=True, root_uuid=vnf_id, created_time=created_time_iface) + #print "Iface id in NFVO DB: %s" % iface_id #print "Adding external interfaces to the NFVO database" for iface in vnf_descriptor['vnf']['external-connections']: @@ -338,6 +345,7 @@ class nfvo_db(db_base.db_base): myIfaceDict["vpci"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci'] myIfaceDict["bw"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw'] myIfaceDict["model"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model'] + created_time_iface = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['created_time'] else: myIfaceDict["vpci"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci'] myIfaceDict["bw"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw'] @@ -347,9 +355,9 @@ class nfvo_db(db_base.db_base): bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['port_security'] myIfaceDict["floating_ip"] = \ bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['floating_ip'] - print "Iface name: %s" % iface['name'] - created_time += 0.00001 - iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time) + created_time_iface = bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['created_time'] + #print "Iface name: %s" % iface['name'] + iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time_iface) #print "Iface id in NFVO DB: %s" % iface_id return vnf_id @@ -953,9 +961,10 @@ class nfvo_db(db_base.db_base): for vm in vnf['vms']: vm_manage_iface_list=[] #instance_interfaces - cmd = "SELECT vim_interface_id, instance_net_id, internal_name,external_name, mac_address, ii.ip_address as ip_address, vim_info, i.type as type "\ - " FROM instance_interfaces as ii join interfaces as i on ii.interface_id=i.uuid "\ - " WHERE instance_vm_id='{}' ORDER BY created_at".format(vm['uuid']) + cmd = "SELECT vim_interface_id, instance_net_id, internal_name,external_name, mac_address,"\ + " ii.ip_address as ip_address, vim_info, i.type as type"\ + " FROM instance_interfaces as ii join interfaces as i on ii.interface_id=i.uuid"\ + " WHERE instance_vm_id='{}' ORDER BY created_at".format(vm['uuid']) self.logger.debug(cmd) self.cur.execute(cmd ) vm['interfaces'] = self.cur.fetchall() diff --git a/openmano b/openmano index ab492f6d..c34d831f 100755 --- a/openmano +++ b/openmano @@ -28,7 +28,7 @@ openmano client used to interact with openmano-server (openmanod) ''' __author__="Alfonso Tierno, Gerardo Garcia" __date__ ="$09-oct-2014 09:09:48$" -__version__="0.4.9-r515" +__version__="0.4.11-r517" version_date="Jan 2017" from argcomplete.completers import FilesCompleter @@ -127,20 +127,25 @@ def _print_verbose(mano_response, verbose_level=0): uuid = content['id'] elif "vim_id" in content: uuid = content['vim_id'] - myoutput = "%s %s" %(uuid.ljust(38),content['name'].ljust(20)) - if "status" in content: + name = content.get('name'); + if not uuid: + uuid = "" + if not name: + name = "" + myoutput = "%s %s" %(uuid.ljust(38),name.ljust(20)) + if content.get("status"): myoutput += " " + content['status'].ljust(20) elif "enabled" in content and not content["enabled"]: myoutput += " enabled=False".ljust(20) if verbose_level >=1: - if 'created_at' in content: + if content.get('created_at'): myoutput += " " + content['created_at'].ljust(20) if verbose_level >=2: new_line='\n' - if 'type' in content and content['type']!=None: + if content.get('type'): myoutput += new_line + " Type: " + content['type'].ljust(29) new_line='' - if 'description' in content and content['description']!=None: + if content.get('description'): myoutput += new_line + " Description: " + content['description'].ljust(20) print myoutput else: @@ -357,7 +362,7 @@ def vnf_list(args): print " External interfaces:" for interface in vnf['external-connections']: print " %s %s %s %s" %(interface['external_name'].ljust(20), interface['vm_name'].ljust(20), interface['internal_name'].ljust(20), \ - interface['vpci'].ljust(14)) + interface.get('vpci',"").ljust(14)) else: print content['error']['description'] if args.verbose: @@ -1415,7 +1420,7 @@ if __name__=="__main__": datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") datacenter_action_parser.set_defaults(func=datacenter_netmap_action, action=item) - for item in ("network", "tenant"): + for item in ("network", "tenant", "image"): if item=="network": commnad_name = 'vim-net' else: @@ -1430,17 +1435,18 @@ if __name__=="__main__": vim_item_del_parser.add_argument("--datacenter", action="store", help="specifies the datacenter") vim_item_del_parser.set_defaults(func=vim_action, item=item, action="delete") - vim_item_create_parser = subparsers.add_parser(commnad_name + '-create', parents=[parent_parser], help="create a "+item+" at vim") - vim_item_create_parser.add_argument("file", nargs='?', help="descriptor of the %s. Must be a file or yaml/json text" % item).completer = FilesCompleter - vim_item_create_parser.add_argument("--name", action="store", help="name of the %s" % item ) - vim_item_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter") - if item=="network": - vim_item_create_parser.add_argument("--type", action="store", help="type of network, data, ptp, bridge") - vim_item_create_parser.add_argument("--shared", action="store_true", help="Private or shared") - vim_item_create_parser.add_argument("--bind-net", action="store", help="For openvim datacenter type, net to be bind to, for vlan type, use sufix ':'") - else: - vim_item_create_parser.add_argument("--description", action="store", help="description of the %s" % item) - vim_item_create_parser.set_defaults(func=vim_action, item=item, action="create") + if item == "network" or item == "tenant": + vim_item_create_parser = subparsers.add_parser(commnad_name + '-create', parents=[parent_parser], help="create a "+item+" at vim") + vim_item_create_parser.add_argument("file", nargs='?', help="descriptor of the %s. Must be a file or yaml/json text" % item).completer = FilesCompleter + vim_item_create_parser.add_argument("--name", action="store", help="name of the %s" % item ) + vim_item_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter") + if item=="network": + vim_item_create_parser.add_argument("--type", action="store", help="type of network, data, ptp, bridge") + vim_item_create_parser.add_argument("--shared", action="store_true", help="Private or shared") + vim_item_create_parser.add_argument("--bind-net", action="store", help="For openvim datacenter type, net to be bind to, for vlan type, use sufix ':'") + else: + vim_item_create_parser.add_argument("--description", action="store", help="description of the %s" % item) + vim_item_create_parser.set_defaults(func=vim_action, item=item, action="create") argcomplete.autocomplete(main_parser) diff --git a/openmano_schemas.py b/openmano_schemas.py index c325b00d..1ea64f63 100644 --- a/openmano_schemas.py +++ b/openmano_schemas.py @@ -80,6 +80,7 @@ config_schema = { "http_port": port_schema, "http_admin_port": port_schema, "http_host": nameshort_schema, + "auto_push_VNF_to_VIMs": {"type":"boolean"}, "vnf_repository": path_schema, "db_host": nameshort_schema, "db_user": nameshort_schema, @@ -107,10 +108,12 @@ config_schema = { "log_level_vim": log_level_schema, "log_level_nfvo": log_level_schema, "log_level_http": log_level_schema, + "log_level_console": log_level_schema, "log_file_db": path_schema, "log_file_vim": path_schema, "log_file_nfvo": path_schema, "log_file_http": path_schema, + "log_file_console": path_schema, "log_socket_host": nameshort_schema, "log_socket_port": port_schema, "log_file": path_schema, diff --git a/openmanod.cfg b/openmanod.cfg index 2d2a9cc6..ebcee2b6 100644 --- a/openmanod.cfg +++ b/openmanod.cfg @@ -48,8 +48,11 @@ db_name: mano_db # Name of the MANO DB #other MANO parameters # Folder where the VNF descriptors will be stored # The folder will be created in the execution folder if it does not exist -#vnf_repository: "./vnfrepo" # Use an absolute path to avoid misunderstandings +#vnf_repository: "./vnfrepo" # Use an absolute path to avoid misunderstandings +# Indicates if at VNF onboarding, flavors and images are loaded at all related VIMs, +# in order to speed up the later instantiation. +auto_push_VNF_to_VIMs: False # by default True #general logging parameters #choose among: DEBUG, INFO, WARNING, ERROR, CRITICAL @@ -63,9 +66,11 @@ log_level: DEBUG #general log levels for internal logging #log_level_vim: DEBUG #VIM connection log levels #log_file_vim: /opt/openmano/logs/openmano_vimconn.log #log_level_nfvo: DEBUG #Main engine log levels -#log_file_nfvo: /opt/openmano/logs/openmano_nfvo.log +#log_file_nfvo: /opt/openmano/logs/openmano_nfvo.log #log_level_http: DEBUG #Main engine log levels -#log_file_http: /opt/openmano/logs/openmano_http.log +#log_file_http: /opt/openmano/logs/openmano_http.log +#log_level_console: DEBUG #proxy console log levels +#log_file_console: /opt/openmano/logs/openmano_console.log #Uncomment to send logs via IP to an external host #log_socket_host: localhost diff --git a/openmanod.py b/openmanod.py index 3d0feb45..5ec3650b 100755 --- a/openmanod.py +++ b/openmanod.py @@ -33,7 +33,7 @@ It loads the configuration file and launches the http_server thread that will li ''' __author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes" __date__ ="$26-aug-2014 11:09:29$" -__version__="0.5.5-r514" +__version__="0.5.8-r518" version_date="Jan 2017" database_version="0.19" #expected database schema version @@ -64,6 +64,7 @@ def load_configuration(configuration_file): 'http_console_host': None, 'log_level': 'DEBUG', 'log_socket_port': 9022, + 'auto_push_VNF_to_VIMs': True } try: #Check config file exists @@ -240,7 +241,7 @@ if __name__=="__main__": logger.critical("Starting openmano server version: '%s %s' command: '%s'", __version__, version_date, " ".join(sys.argv)) - for log_module in ("nfvo", "http", "vim", "db"): + for log_module in ("nfvo", "http", "vim", "db", "console"): log_level_module = "log_level_" + log_module log_file_module = "log_file_" + log_module logger_module = logging.getLogger('openmano.' + log_module) diff --git a/vimconn.py b/vimconn.py index 814be65a..ad06dc8b 100644 --- a/vimconn.py +++ b/vimconn.py @@ -242,7 +242,13 @@ class vimconnector(): Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete ''' raise vimconnNotImplemented( "Should have implemented this" ) - + + def get_flavor_id_from_data(self, flavor_dict): + """Obtain flavor id that match the flavor description + Returns the flavor_id or raises a vimconnNotFoundException + """ + raise vimconnNotImplemented( "Should have implemented this" ) + def new_flavor(self, flavor_data): '''Adds a tenant flavor to VIM flavor_data contains a dictionary with information, keys: @@ -287,7 +293,9 @@ class vimconnector(): raise vimconnNotImplemented( "Should have implemented this" ) def get_image_id_from_path(self, path): - '''Get the image id from image path in the VIM database. Returns the image_id''' + """Get the image id from image path in the VIM database. + Returns the image_id or raises a vimconnNotFoundException + """ raise vimconnNotImplemented( "Should have implemented this" ) def get_image_list(self, filter_dict={}): diff --git a/vimconn_openstack.py b/vimconn_openstack.py index a1146413..7ba280b0 100644 --- a/vimconn_openstack.py +++ b/vimconn_openstack.py @@ -34,6 +34,7 @@ import logging import netaddr import time import yaml +import random from novaclient import client as nClient_v2, exceptions as nvExceptions from novaclient import api_versions @@ -79,6 +80,9 @@ class vimconnector(vimconn.vimconnector): self.k_creds={} self.n_creds={} + if self.config.get("insecure"): + self.k_creds["insecure"] = True + self.n_creds["insecure"] = True if not url: raise TypeError, 'url param can not be NoneType' self.k_creds['auth_url'] = url @@ -303,8 +307,9 @@ class vimconnector(vimconn.vimconnector): if not ip_profile: ip_profile = {} if 'subnet_address' not in ip_profile: - #Fake subnet is required - ip_profile['subnet_address'] = "192.168.111.0/24" + #Fake subnet is required + subnet_rand = random.randint(0, 255) + 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", @@ -460,6 +465,38 @@ class vimconnector(vimconn.vimconnector): except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e: self._format_exception(e) + def get_flavor_id_from_data(self, flavor_dict): + """Obtain flavor id that match the flavor description + Returns the flavor_id or raises a vimconnNotFoundException + """ + try: + self._reload_connection() + numa=None + numas = flavor_dict.get("extended",{}).get("numas") + if numas: + #TODO + raise vimconn.vimconnNotFoundException("Flavor with EPA still not implemted") + # if len(numas) > 1: + # raise vimconn.vimconnNotFoundException("Cannot find any flavor with more than one numa") + # numa=numas[0] + # numas = extended.get("numas") + for flavor in self.nova.flavors.list(): + epa = flavor.get_keys() + if epa: + continue + #TODO + if flavor.ram != flavor_dict["ram"]: + continue + if flavor.vcpus != flavor_dict["vcpus"]: + continue + if flavor.disk != flavor_dict["disk"]: + continue + return flavor.id + raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict))) + except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e: + self._format_exception(e) + + def new_flavor(self, flavor_data, change_name_if_used=True): '''Adds a tenant flavor to openstack VIM if change_name_if_used is True, it will change name in case of conflict, because it is not supported name repetition @@ -657,9 +694,9 @@ class vimconnector(vimconn.vimconnector): #Then we filter by the rest of filter fields: checksum filtered_list = [] for image in image_list: - image_dict=self.glance.images.get(image.id) - if 'checksum' not in filter_dict or image_dict['checksum']==filter_dict.get('checksum'): - filtered_list.append(image_dict) + image_class=self.glance.images.get(image.id) + if 'checksum' not in filter_dict or image_class['checksum']==filter_dict.get('checksum'): + filtered_list.append(image_class.copy()) return filtered_list except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: self._format_exception(e) @@ -682,7 +719,7 @@ class vimconnector(vimconn.vimconnector): #TODO ip, security groups Returns the instance identifier ''' - self.logger.debug("Creating VM image '%s' flavor '%s' nics='%s'",image_id, flavor_id,str(net_list)) + self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'",image_id, flavor_id,str(net_list)) try: metadata={} net_list_vim=[] @@ -722,8 +759,12 @@ class vimconnector(vimconn.vimconnector): self.logger.warn("new_vminstance: Warning, can not connect a passthrough interface ") #TODO insert this when openstack consider passthrough ports as openstack neutron ports if net.get('floating_ip', False): + net['exit_on_floating_ip_error'] = True external_network.append(net) - + elif net['use'] == 'mgmt' and self.config.get('use_floating_ip'): + net['exit_on_floating_ip_error'] = False + external_network.append(net) + if metadata_vpci: metadata = {"pci_assignement": json.dumps(metadata_vpci)} if len(metadata["pci_assignement"]) >255: @@ -755,7 +796,7 @@ class vimconnector(vimconn.vimconnector): userdata_dict["ssh-authorized-keys"] = cloud_config["key-pairs"] userdata_dict["users"] = [{"default": None, "ssh-authorized-keys": cloud_config["key-pairs"] }] if cloud_config.get("users"): - if "users" not in cloud_config: + if "users" not in userdata_dict: userdata_dict["users"] = [ "default" ] for user in cloud_config["users"]: user_info = { @@ -841,71 +882,72 @@ class vimconnector(vimconn.vimconnector): pool_id = None floating_ips = self.neutron.list_floatingips().get("floatingips", ()) for floating_network in external_network: - # wait until vm is active - elapsed_time = 0 - while elapsed_time < server_timeout: - status = self.nova.servers.get(server.id).status - if status == 'ACTIVE': - break - time.sleep(1) - elapsed_time += 1 + try: + # wait until vm is active + elapsed_time = 0 + while elapsed_time < server_timeout: + status = self.nova.servers.get(server.id).status + if status == 'ACTIVE': + break + time.sleep(1) + elapsed_time += 1 - #if we exceeded the timeout rollback - if elapsed_time >= server_timeout: - self.delete_vminstance(server.id) - raise vimconn.vimconnException('Timeout creating instance ' + name, - http_code=vimconn.HTTP_Request_Timeout) + #if we exceeded the timeout rollback + if elapsed_time >= server_timeout: + raise vimconn.vimconnException('Timeout creating instance ' + name, + http_code=vimconn.HTTP_Request_Timeout) + + assigned = False + while(assigned == False): + if floating_ips: + ip = floating_ips.pop(0) + if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id: + free_floating_ip = ip.get("floating_ip_address") + try: + fix_ip = floating_network.get('ip') + server.add_floating_ip(free_floating_ip, fix_ip) + assigned = True + except Exception as e: + raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict) + else: + #Find the external network + external_nets = list() + for net in self.neutron.list_networks()['networks']: + if net['router:external']: + external_nets.append(net) - assigned = False - while(assigned == False): - if floating_ips: - ip = floating_ips.pop(0) - if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id: - free_floating_ip = ip.get("floating_ip_address") + if len(external_nets) == 0: + raise vimconn.vimconnException("Cannot create floating_ip automatically since no external " + "network is present", + http_code=vimconn.HTTP_Conflict) + if len(external_nets) > 1: + raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple " + "external networks are present", + http_code=vimconn.HTTP_Conflict) + + pool_id = external_nets[0].get('id') + param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}} try: + #self.logger.debug("Creating floating IP") + new_floating_ip = self.neutron.create_floatingip(param) + free_floating_ip = new_floating_ip['floatingip']['floating_ip_address'] fix_ip = floating_network.get('ip') server.add_floating_ip(free_floating_ip, fix_ip) - assigned = True + assigned=True except Exception as e: - self.delete_vminstance(server.id) - raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict) - else: - #Find the external network - external_nets = list() - for net in self.neutron.list_networks()['networks']: - if net['router:external']: - external_nets.append(net) - - if len(external_nets) == 0: - self.delete_vminstance(server.id) - raise vimconn.vimconnException("Cannot create floating_ip automatically since no external " - "network is present", - http_code=vimconn.HTTP_Conflict) - if len(external_nets) > 1: - self.delete_vminstance(server.id) - raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple " - "external networks are present", - http_code=vimconn.HTTP_Conflict) + raise vimconn.vimconnException(type(e).__name__ + ": Cannot assign floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict) + except Exception as e: + if not floating_network['exit_on_floating_ip_error']: + self.logger.warn("Cannot create floating_ip. %s", str(e)) + continue + self.delete_vminstance(server.id) + raise - pool_id = external_nets[0].get('id') - param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}} - try: - #self.logger.debug("Creating floating IP") - new_floating_ip = self.neutron.create_floatingip(param) - free_floating_ip = new_floating_ip['floatingip']['floating_ip_address'] - fix_ip = floating_network.get('ip') - server.add_floating_ip(free_floating_ip, fix_ip) - assigned=True - except Exception as e: - self.delete_vminstance(server.id) - raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict) - return server.id # except nvExceptions.NotFound as e: # error_value=-vimconn.HTTP_Not_Found # error_text= "vm instance %s not found" % vm_id - except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError - ) as e: + except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e: # delete the volumes we just created if block_device_mapping != None: for volume_id in block_device_mapping.itervalues(): diff --git a/vimconn_openvim.py b/vimconn_openvim.py index bd963c7d..fb59eccc 100644 --- a/vimconn_openvim.py +++ b/vimconn_openvim.py @@ -795,6 +795,7 @@ class vimconnector(vimconn.vimconnector): #TODO ip, security groups Returns the instance identifier ''' + self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'", image_id, flavor_id, str(net_list)) try: self._get_my_tenant() # net_list = [] diff --git a/vimconn_vmware.py b/vimconn_vmware.py index 19382d67..3e81f513 100644 --- a/vimconn_vmware.py +++ b/vimconn_vmware.py @@ -177,6 +177,9 @@ class vimconnector(vimconn.vimconnector): self.admin_password = None self.admin_user = None self.org_name = "" + self.nsx_manager = None + self.nsx_user = None + self.nsx_password = None if tenant_name is not None: orgnameandtenant = tenant_name.split(":") @@ -197,6 +200,13 @@ class vimconnector(vimconn.vimconnector): except KeyError: raise vimconn.vimconnException(message="Error admin username or admin password is empty.") + try: + self.nsx_manager = config['nsx_manager'] + self.nsx_user = config['nsx_user'] + self.nsx_password = config['nsx_password'] + except KeyError: + raise vimconn.vimconnException(message="Error: nsx manager or nsx user or nsx password is empty in Config") + self.org_uuid = None self.vca = None @@ -1072,6 +1082,43 @@ class vimconnector(vimconn.vimconnector): return self.get_catalogid(catalog_md5_name, vca.get_catalogs()) + def get_image_list(self, filter_dict={}): + '''Obtain tenant images from VIM + Filter_dict can be: + name: image name + id: image uuid + checksum: image checksum + location: image path + Returns the image list of dictionaries: + [{}, ...] + List can be empty + ''' + vca = self.connect() + if not vca: + raise vimconn.vimconnConnectionException("self.connect() is failed.") + try: + image_list = [] + catalogs = vca.get_catalogs() + if len(catalogs) == 0: + return image_list + else: + for catalog in catalogs: + catalog_uuid = catalog.get_id().split(":")[3] + name = catalog.name + filtered_dict = {} + if filter_dict.get("name") and filter_dict["name"] != name: + continue + if filter_dict.get("id") and filter_dict["id"] != catalog_uuid: + continue + filtered_dict ["name"] = name + filtered_dict ["id"] = catalog_uuid + image_list.append(filtered_dict) + + self.logger.debug("List of already created catalog items: {}".format(image_list)) + return image_list + except Exception as exp: + raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp)) + def get_vappid(self, vdc=None, vapp_name=None): """ Method takes vdc object and vApp name and returns vapp uuid or None @@ -1615,6 +1662,40 @@ class vimconnector(vimconn.vimconnector): """ self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list)) + + mac_ip_addr={} + rheaders = {'Content-Type': 'application/xml'} + iso_edges = ['edge-2','edge-3','edge-6','edge-7','edge-8','edge-9','edge-10'] + + try: + for edge in iso_edges: + nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo' + self.logger.debug("refresh_vms_status: NSX Manager url: {}".format(nsx_api_url)) + + resp = requests.get(self.nsx_manager + nsx_api_url, + auth = (self.nsx_user, self.nsx_password), + verify = False, headers = rheaders) + + if resp.status_code == requests.codes.ok: + dhcp_leases = XmlElementTree.fromstring(resp.text) + for child in dhcp_leases: + if child.tag == 'dhcpLeaseInfo': + dhcpLeaseInfo = child + for leaseInfo in dhcpLeaseInfo: + for elem in leaseInfo: + if (elem.tag)=='macAddress': + mac_addr = elem.text + if (elem.tag)=='ipAddress': + ip_addr = elem.text + if (mac_addr) is not None: + mac_ip_addr[mac_addr]= ip_addr + self.logger.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr)) + else: + self.logger.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp.content)) + except KeyError: + self.logger.debug("Error in response from NSX Manager {}".format(KeyError.message)) + self.logger.debug(traceback.format_exc()) + vca = self.connect() if not vca: raise vimconn.vimconnConnectionException("self.connect() is failed.") @@ -1644,6 +1725,10 @@ class vimconnector(vimconn.vimconnector): for vapp_network in vm_app_networks: for vm_network in vapp_network: if vm_network['name'] == vmname: + #Assign IP Address based on MAC Address in NSX DHCP lease info + for mac_adres,ip_adres in mac_ip_addr.iteritems(): + if mac_adres == vm_network['mac']: + vm_network['ip']=ip_adres interface = {"mac_address": vm_network['mac'], "vim_net_id": self.get_network_id_by_name(vm_network['network_name']), "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']), @@ -1685,15 +1770,30 @@ class vimconnector(vimconn.vimconnector): the_vapp = vca.get_vapp(vdc, vapp_name) # TODO fix all status if "start" in action_dict: - if action_dict["start"] == "rebuild": - the_vapp.deploy(powerOn=True) + vm_info = the_vapp.get_vms_details() + vm_status = vm_info[0]['status'] + self.logger.info("Power on vApp: vm_status:{} {}".format(type(vm_status),vm_status)) + if vm_status == "Suspended" or vm_status == "Powered off": + power_on_task = the_vapp.poweron() + if power_on_task is not None and type(power_on_task) is GenericTask: + result = vca.block_until_completed(power_on_task) + if result: + self.logger.info("action_vminstance: Powered on vApp: {}".format(vapp_name)) + else: + self.logger.info("action_vminstance: Failed to power on vApp: {}".format(vapp_name)) + else: + self.logger.info("action_vminstance: Wait for vApp {} to power on".format(vapp_name)) + elif "rebuild" in action_dict: + self.logger.info("action_vminstance: Rebuilding vApp: {}".format(vapp_name)) + power_on_task = the_vapp.deploy(powerOn=True) + if type(power_on_task) is GenericTask: + result = vca.block_until_completed(power_on_task) + if result: + self.logger.info("action_vminstance: Rebuilt vApp: {}".format(vapp_name)) + else: + self.logger.info("action_vminstance: Failed to rebuild vApp: {}".format(vapp_name)) else: - vm_info = the_vapp.get_vms_details() - vm_status = vm_info[0]['status'] - if vm_status == "Suspended": - the_vapp.poweron() - elif vm_status.status == "Powered off": - the_vapp.poweron() + self.logger.info("action_vminstance: Wait for vApp rebuild {} to power on".format(vapp_name)) elif "pause" in action_dict: pass ## server.pause() @@ -1701,7 +1801,15 @@ class vimconnector(vimconn.vimconnector): pass ## server.resume() elif "shutoff" in action_dict or "shutdown" in action_dict: - the_vapp.shutdown() + power_off_task = the_vapp.undeploy(action='powerOff') + if type(power_off_task) is GenericTask: + result = vca.block_until_completed(power_off_task) + if result: + self.logger.info("action_vminstance: Powered off vApp: {}".format(vapp_name)) + else: + self.logger.info("action_vminstance: Failed to power off vApp: {}".format(vapp_name)) + else: + self.logger.info("action_vminstance: Wait for vApp {} to power off".format(vapp_name)) elif "forceOff" in action_dict: the_vapp.reset() elif "terminate" in action_dict: diff --git a/vnfs/examples/linux-cloud-init.yaml b/vnfs/examples/linux-cloud-init.yaml index ce8c5de1..92e14fb2 100644 --- a/vnfs/examples/linux-cloud-init.yaml +++ b/vnfs/examples/linux-cloud-init.yaml @@ -26,7 +26,7 @@ vnf: description: Single-VM VNF with a traditional cloud VM based on generic Linux OS external-connections: - name: eth0 - type: bridge + type: mgmt description: General purpose interface VNFC: linux-VM local_iface_name: eth0