From: tierno Date: Fri, 8 Jul 2016 10:30:39 +0000 (+0200) Subject: openmano v0.4.39: logging at vimconnector. Exception generation upon error instead... X-Git-Tag: v1.0.0~71 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=refs%2Fchanges%2F25%2F225%2F1;p=osm%2FRO.git openmano v0.4.39: logging at vimconnector. Exception generation upon error instead of returning status Signed-off-by: tierno --- diff --git a/nfvo.py b/nfvo.py index d528f857..5289ba9c 100644 --- a/nfvo.py +++ b/nfvo.py @@ -34,16 +34,20 @@ import utils from nfvo_db import HTTP_Unauthorized, HTTP_Bad_Request, HTTP_Internal_Server_Error, HTTP_Not_Found,\ HTTP_Conflict, HTTP_Method_Not_Allowed import console_proxy_thread as cli +import vimconn +import logging global global_config global vimconn_imported + vimconn_imported={} #dictionary with VIM type as key, loaded module as value +logger = logging.getLogger('mano.nfvo') class NfvoException(Exception): - def __init__(self, error_code, text_message): - self.error_code = error_code - Exception.__init__(self, text_message) + def __init__(self, message, http_code): + self.http_code = http_code + Exception.__init__(self, message) def get_flavorlist(mydb, vnf_id, nfvo_tenant=None): @@ -162,44 +166,35 @@ def rollback(mydb, vims, rollback_list): if item["vim_id"] not in vims: continue vim=vims[ item["vim_id"] ] - if item["what"]=="image": - result, message = vim.delete_tenant_image(item["uuid"]) - if result < 0: - print "Error in rollback. Not possible to delete VIM image '%s'. Message: %s" % (item["uuid"],message) - undeleted_items.append("image %s from VIM %s" % (item["uuid"],vim["name"])) - else: + try: + if item["what"]=="image": + vim.delete_image(item["uuid"]) result, message = mydb.delete_row_by_dict(FROM="datacenters_images", WEHRE={"datacenter_id": vim["id"], "vim_id":item["uuid"]}) if result < 0: - print "Error in rollback. Not possible to delete image '%s' from DB.dacenters_images. Message: %s" % (item["uuid"],message) - elif item["what"]=="flavor": - result, message = vim.delete_tenant_flavor(item["uuid"]) - if result < 0: - print "Error in rollback. Not possible to delete VIM flavor '%s'. Message: %s" % (item["uuid"],message) - undeleted_items.append("flavor %s from VIM %s" % (item["uuid"],vim["name"])) - else: + logger.error("Error in rollback. Not possible to delete image '%s' from DB.dacenters_images. Message: %s", item["uuid"],message) + elif item["what"]=="flavor": + vim.delete_flavor(item["uuid"]) result, message = mydb.delete_row_by_dict(FROM="datacenters_flavos", WEHRE={"datacenter_id": vim["id"], "vim_id":item["uuid"]}) if result < 0: - print "Error in rollback. Not possible to delete flavor '%s' from DB.dacenters_flavors. Message: %s" % (item["uuid"],message) - elif item["what"]=="network": - result, message = vim.delete_tenant_network(item["uuid"]) - if result < 0: - print "Error in rollback. Not possible to delete VIM network '%s'. Message: %s" % (item["uuid"],message) - undeleted_items.append("network %s from VIM %s" % (item["uuid"],vim["name"])) - elif item["what"]=="vm": - result, message = vim.delete_tenant_vminstance(item["uuid"]) - if result < 0: - print "Error in rollback. Not possible to delete VIM VM '%s'. Message: %s" % (item["uuid"],message) - undeleted_items.append("VM %s from VIM %s" % (item["uuid"],vim["name"])) + logger.error("Error in rollback. Not possible to delete flavor '%s' from DB.dacenters_flavors. Message: %s", item["uuid"],message) + elif item["what"]=="network": + vim.delete_network(item["uuid"]) + elif item["what"]=="vm": + vim.delete_vminstance(item["uuid"]) + except vimconn.vimconnException as e: + logger.error("Error in rollback. Not possible to delete VIM %s '%s'. Message: %s", item['what'], item["uuid"], str(e)) + undeleted_items.append("{} {} from VIM {}".format(item['what'], item["uuid"], vim["name"])) + else: # where==mano if item["what"]=="image": result, message = mydb.delete_row_by_dict(FROM="images", WEHRE={"uuid": item["uuid"]}) if result < 0: - print "Error in rollback. Not possible to delete image '%s' from DB.images. Message: %s" % (item["uuid"],message) + logger.error("Error in rollback. Not possible to delete image '%s' from DB.images. Message: %s", item["uuid"], message) undeleted_items.append("image %s" % (item["uuid"])) elif item["what"]=="flavor": result, message = mydb.delete_row_by_dict(FROM="flavors", WEHRE={"uuid": item["uuid"]}) if result < 0: - print "Error in rollback. Not possible to delete flavor '%s' from DB.flavors. Message: %s" % (item["uuid"],message) + logger.error("Error in rollback. Not possible to delete flavor '%s' from DB.flavors. Message: %s", item["uuid"], message) undeleted_items.append("flavor %s" % (item["uuid"])) if len(undeleted_items)==0: return True," Rollback successful." @@ -283,22 +278,25 @@ def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vi if res_db<0: return res_db, image_db #look at VIM if this image exist - res_vim, image_vim_id = vim.get_image_id_from_path(image_dict['location']) - if res_vim < 0: - print "Error contacting VIM to know if the image %s existed previously." %image_vim_id - continue - elif res_vim==0: + try: + image_vim_id = vim.get_image_id_from_path(image_dict['location']) + except vimconn.vimconnNotFoundException as e: #Create the image in VIM - result, image_vim_id = vim.new_tenant_image(image_dict) - if result < 0: - print "Error creating image at VIM: %s." %image_vim_id - if return_on_error: - return result, image_vim_id - continue - else: + try: + image_vim_id = vim.new_image(image_dict) rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"image","uuid":image_vim_id}) image_created="true" - + except vimconn.vimconnException as e: + if return_on_error: + logger.error("Error creating image at VIM: %s", str(e)) + return -e.http_code, str(e) + image_vim_id = str(e) + logger.warn("Error creating image at VIM: %s", str(e)) + continue + except vimconn.vimconnException as e: + logger.warn("Error contacting VIM to know if the image exist at VIM: %s", str(e)) + image_vim_id = str(e) + continue #if reach here the image has been create or exist if res_db==0: #add new vim_id at datacenters_images @@ -421,22 +419,23 @@ def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_ if res_db>0: #check that this vim_id exist in VIM, if not create flavor_vim_id=flavor_db[0]["vim_id"] - result, _ = vim.get_tenant_flavor(flavor_vim_id) - if result>=0: #flavor exist - continue + try: + vim.get_flavor(flavor_vim_id) + continue #flavor exist + except vimconn.vimconnException: + pass #create flavor at vim - print "nfvo.create_or_use_flavor() adding flavor to VIM %s" % vim["name"] - result, flavor_vim_id = vim.new_tenant_flavor(flavor_dict) - - if result < 0: - print "Error creating flavor at VIM %s: %s." %(vim["name"], flavor_vim_id) - if return_on_error: - return result, flavor_vim_id - continue - else: + 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" - + except vimconn.vimconnException as e: + if return_on_error: + logger.error("Error creating flavor at VIM %s: %s.", vim["name"], str(e)) + return -e.http_code, str(e) + logger.warn("Error creating flavor at VIM %s: %s.", vim["name"], str(e)) + continue #if reach here the flavor has been create or exist if res_db==0: #add new vim_id at datacenters_flavors @@ -727,11 +726,14 @@ def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None): if flavor_vim['created']=='false': #skip this flavor because not created by openmano continue myvim=vims[ flavor_vim["datacenter_id"] ] - result, message = myvim.delete_tenant_flavor(flavor_vim["vim_id"]) - if result < 0: - print 'delete_vnf_error. Not possible to delete VIM flavor "%s". Message: %s' % (flavor,message) - if result != -HTTP_Not_Found: - undeletedItems.append("flavor %s from VIM %s" % (flavor_vim["vim_id"], flavor_vim["datacenter_id"] )) + try: + myvim.delete_flavor(flavor_vim["vim_id"]) + except vimconn.vimconnNotFoundException as e: + logger.warn("VIM flavor %s not exist at datacenter %s", flavor_vim["vim_id"], flavor_vim["datacenter_id"] ) + except vimconn.vimconnException as e: + logger.error("Not possible to delete VIM flavor %s from datacenter %s: %s %s", + flavor_vim["vim_id"], flavor_vim["datacenter_id"], type(e).__name__, str(e)) + undeletedItems.append("flavor {} from VIM {}".format(flavor_vim["vim_id"], flavor_vim["datacenter_id"] )) #delete flavor from Database, using table flavors and with cascade foreign key also at datacenters_flavors result, content = mydb.delete_row('flavors', flavor) if result <0: @@ -756,11 +758,14 @@ def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None): if image_vim['created']=='false': #skip this image because not created by openmano continue myvim=vims[ image_vim["datacenter_id"] ] - result, message = myvim.delete_tenant_image(image_vim["vim_id"]) - if result < 0: - print 'delete_vnf_error. Not possible to delete VIM image "%s". Message: %s' % (image,message) - if result != -HTTP_Not_Found: - undeletedItems.append("image %s from VIM %s" % (image_vim["vim_id"], image_vim["datacenter_id"] )) + try: + myvim.delete_image(image_vim["vim_id"]) + except vimconn.vimconnNotFoundException as e: + logger.warn("VIM image %s not exist at datacenter %s", image_vim["vim_id"], image_vim["datacenter_id"] ) + except vimconn.vimconnException as e: + logger.error("Not possible to delete VIM image %s from datacenter %s: %s %s", + image_vim["vim_id"], image_vim["datacenter_id"], type(e).__name__, str(e)) + undeletedItems.append("image {} from VIM {}".format(image_vim["vim_id"], image_vim["datacenter_id"] )) #delete image from Database, using table images and with cascade foreign key also at datacenters_images result, content = mydb.delete_row('images', image) if result <0: @@ -1197,7 +1202,7 @@ def edit_scenario(mydb, tenant_id, scenario_id, data): return r,c def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instance_scenario_description, datacenter=None,vim_tenant=None, startvms=True): - print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id" + #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id" datacenter_id = None datacenter_name=None if datacenter != None: @@ -1207,12 +1212,12 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc datacenter_name = datacenter result, vims = get_vim(mydb, tenant_id, datacenter_id, datacenter_name, vim_tenant) if result < 0: - print "start_scenario error. Datacenter not found" + logger.error("start_scenario error. Datacenter not found") return result, vims elif result == 0: return -HTTP_Not_Found, "datacenter '%s' not found" % str(datacenter) elif result > 1: - print "start_scenario error. Several datacenters available, you must concrete" + logger.error("start_scenario error. Several datacenters available, you must concrete") return -HTTP_Bad_Request, "Several datacenters available, you must concrete" myvim = vims.values()[0] myvim_tenant = myvim['tenant_id'] @@ -1220,227 +1225,215 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc datacenter_name = myvim['name'] datacenter_tenant_id = myvim['config']['datacenter_tenant_id'] rollbackList=[] - - print "Checking that the scenario_id exists and getting the scenario dictionary" - result, scenarioDict = mydb.get_scenario(scenario_id, tenant_id, datacenter_id) - if result < 0: - print "start_scenario error. Error interacting with NFVO DB" - return result, scenarioDict - elif result == 0: - print "start_scenario error. Scenario not found" - return result, scenarioDict - - scenarioDict['datacenter_tenant_id'] = datacenter_tenant_id - scenarioDict['datacenter_id'] = datacenter_id - print '================scenarioDict=======================' - #print json.dumps(scenarioDict, indent=4) - print 'BEGIN launching instance scenario "%s" based on "%s"' % (instance_scenario_name,scenarioDict['name']) - - print "Scenario %s: consisting of %d VNF(s)" % (scenarioDict['name'],len(scenarioDict['vnfs'])) - print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) + try: + #print "Checking that the scenario_id exists and getting the scenario dictionary" + result, scenarioDict = mydb.get_scenario(scenario_id, tenant_id, datacenter_id) + if result < 0: + logger.error("start_scenario error. Error interacting with NFVO DB") + return result, scenarioDict + elif result == 0: + logger.error("start_scenario error. Scenario not found") + return result, scenarioDict - auxNetDict = {} #Auxiliar dictionary. First key:'scenario' or sce_vnf uuid. Second Key: uuid of the net/sce_net. Value: vim_net_id - auxNetDict['scenario'] = {} + scenarioDict['datacenter_tenant_id'] = datacenter_tenant_id + scenarioDict['datacenter_id'] = datacenter_id + #print '================scenarioDict=======================' + #print json.dumps(scenarioDict, indent=4) + #print 'BEGIN launching instance scenario "%s" based on "%s"' % (instance_scenario_name,scenarioDict['name']) - print "1. Creating new nets (sce_nets) in the VIM" - for sce_net in scenarioDict['nets']: - print "Net name: %s. Description: %s" % (sce_net["name"], sce_net["description"]) + logger.debug("start_scenario Scenario %s: consisting of %d VNF(s)", scenarioDict['name'],len(scenarioDict['vnfs'])) + #print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) - myNetName = "%s.%s" % (instance_scenario_name, sce_net['name']) - myNetName = myNetName[0:255] #limit length - myNetType = sce_net['type'] - myNetDict = {} - myNetDict["name"] = myNetName - myNetDict["type"] = myNetType - myNetDict["tenant_id"] = myvim_tenant - #TODO: - #We should use the dictionary as input parameter for new_tenant_network - print myNetDict - if not sce_net["external"]: - result, network_id = myvim.new_tenant_network(myNetName, myNetType) - if result < 0: - print "Error creating network: %s." %network_id - _, message = rollback(mydb, vims, rollbackList) - return result, "Error creating network: "+ network_id + "."+message - - print "New VIM network created for scenario %s. Network id: %s" % (scenarioDict['name'],network_id) - sce_net['vim_id'] = network_id - auxNetDict['scenario'][sce_net['uuid']] = network_id - rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id}) - else: - if sce_net['vim_id'] == None: - error_text = "Error, datacenter '%s' does not have external network '%s'." % (datacenter_name, sce_net['name']) - _, message = rollback(mydb, vims, rollbackList) - print "nfvo.start_scenario: " + error_text - return -HTTP_Bad_Request, error_text - print "Using existent VIM network for scenario %s. Network id %s" % (scenarioDict['name'],sce_net['vim_id']) - auxNetDict['scenario'][sce_net['uuid']] = sce_net['vim_id'] - - print "2. Creating new nets (vnf internal nets) in the VIM" - #For each vnf net, we create it and we add it to instanceNetlist. - for sce_vnf in scenarioDict['vnfs']: - for net in sce_vnf['nets']: - print "Net name: %s. Description: %s" % (net["name"], net["description"]) + auxNetDict = {} #Auxiliar dictionary. First key:'scenario' or sce_vnf uuid. Second Key: uuid of the net/sce_net. Value: vim_net_id + auxNetDict['scenario'] = {} + + logger.debug("start_scenario 1. Creating new nets (sce_nets) in the VIM") + for sce_net in scenarioDict['nets']: + #print "Net name: %s. Description: %s" % (sce_net["name"], sce_net["description"]) - myNetName = "%s.%s" % (instance_scenario_name,net['name']) + myNetName = "%s.%s" % (instance_scenario_name, sce_net['name']) myNetName = myNetName[0:255] #limit length - myNetType = net['type'] + myNetType = sce_net['type'] myNetDict = {} myNetDict["name"] = myNetName myNetDict["type"] = myNetType myNetDict["tenant_id"] = myvim_tenant - print myNetDict #TODO: - #We should use the dictionary as input parameter for new_tenant_network - result, network_id = myvim.new_tenant_network(myNetName, myNetType) - if result < 0: - error_text="Error creating network: %s." % network_id - _, message = rollback(mydb, vims, rollbackList) - error_text += message - print "start_scenario: " + error_text - return result, error_text - print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id) - net['vim_id'] = network_id - if sce_vnf['uuid'] not in auxNetDict: - auxNetDict[sce_vnf['uuid']] = {} - auxNetDict[sce_vnf['uuid']][net['uuid']] = network_id - rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id}) - - print "auxNetDict:" - print yaml.safe_dump(auxNetDict, indent=4, default_flow_style=False) + #We should use the dictionary as input parameter for new_network + print myNetDict + if not sce_net["external"]: + network_id = myvim.new_network(myNetName, myNetType) + #print "New VIM network created for scenario %s. Network id: %s" % (scenarioDict['name'],network_id) + sce_net['vim_id'] = network_id + auxNetDict['scenario'][sce_net['uuid']] = network_id + rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id}) + else: + if sce_net['vim_id'] == None: + error_text = "Error, datacenter '%s' does not have external network '%s'." % (datacenter_name, sce_net['name']) + _, message = rollback(mydb, vims, rollbackList) + logger.error("nfvo.start_scenario: %s", error_text) + return -HTTP_Bad_Request, error_text + logger.debug("Using existent VIM network for scenario %s. Network id %s", scenarioDict['name'],sce_net['vim_id']) + auxNetDict['scenario'][sce_net['uuid']] = sce_net['vim_id'] + + logger.debug("start_scenario 2. Creating new nets (vnf internal nets) in the VIM") + #For each vnf net, we create it and we add it to instanceNetlist. + for sce_vnf in scenarioDict['vnfs']: + for net in sce_vnf['nets']: + #print "Net name: %s. Description: %s" % (net["name"], net["description"]) + + myNetName = "%s.%s" % (instance_scenario_name,net['name']) + myNetName = myNetName[0:255] #limit length + myNetType = net['type'] + myNetDict = {} + myNetDict["name"] = myNetName + myNetDict["type"] = myNetType + myNetDict["tenant_id"] = myvim_tenant + #print myNetDict + #TODO: + #We should use the dictionary as input parameter for new_network + result, network_id = myvim.new_network(myNetName, myNetType) + #print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id) + net['vim_id'] = network_id + if sce_vnf['uuid'] not in auxNetDict: + auxNetDict[sce_vnf['uuid']] = {} + auxNetDict[sce_vnf['uuid']][net['uuid']] = network_id + rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id}) - print "3. Creating new vm instances in the VIM" - #myvim.new_tenant_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict) - i = 0 - for sce_vnf in scenarioDict['vnfs']: - for vm in sce_vnf['vms']: - i += 1 - myVMDict = {} - #myVMDict['name'] = "%s-%s-%s" % (scenarioDict['name'],sce_vnf['name'], vm['name']) - myVMDict['name'] = "%s.%s.%d" % (instance_scenario_name,sce_vnf['name'],i) - #myVMDict['description'] = vm['description'] - myVMDict['description'] = myVMDict['name'][0:99] - if not startvms: - myVMDict['start'] = "no" - myVMDict['name'] = myVMDict['name'][0:255] #limit name length - print "VM name: %s. Description: %s" % (myVMDict['name'], myVMDict['name']) - - #create image at vim in case it not exist - res, image_dict = mydb.get_table_by_uuid_name("images", vm['image_id']) - if res<0: - print "start_scenario error getting image", image_dict - return res, image_dict - res, image_id = create_or_use_image(mydb, vims, image_dict, [], True) - if res < 0: - print "start_scenario error adding image to VIM", image_dict - return res, image_id - vm['vim_image_id'] = image_id + #print "auxNetDict:" + #print yaml.safe_dump(auxNetDict, indent=4, default_flow_style=False) + + logger.debug("start_scenario 3. Creating new vm instances in the VIM") + #myvim.new_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict) + i = 0 + for sce_vnf in scenarioDict['vnfs']: + for vm in sce_vnf['vms']: + i += 1 + myVMDict = {} + #myVMDict['name'] = "%s-%s-%s" % (scenarioDict['name'],sce_vnf['name'], vm['name']) + myVMDict['name'] = "%s.%s.%d" % (instance_scenario_name,sce_vnf['name'],i) + #myVMDict['description'] = vm['description'] + myVMDict['description'] = myVMDict['name'][0:99] + if not startvms: + myVMDict['start'] = "no" + myVMDict['name'] = myVMDict['name'][0:255] #limit name length + #print "VM name: %s. Description: %s" % (myVMDict['name'], myVMDict['name']) - #create flavor at vim in case it not exist - res, flavor_dict = mydb.get_table_by_uuid_name("flavors", vm['flavor_id']) - if res<0: - print "start_scenario error getting flavor", flavor_dict - return res, flavor_dict - if flavor_dict['extended']!=None: - flavor_dict['extended']= yaml.load(flavor_dict['extended']) - res, flavor_id = create_or_use_flavor(mydb, vims, flavor_dict, [], True) - if res < 0: - print "start_scenario error adding flavor to VIM", flavor_dict - return res, flavor_id - vm['vim_flavor_id'] = flavor_id - - - myVMDict['imageRef'] = vm['vim_image_id'] - myVMDict['flavorRef'] = vm['vim_flavor_id'] - myVMDict['networks'] = [] - for iface in vm['interfaces']: - netDict = {} - if iface['type']=="data": - netDict['type'] = iface['model'] - elif "model" in iface and iface["model"]!=None: - netDict['model']=iface['model'] - #TODO in future, remove this because mac_address will not be set, and the type of PV,VF is obtained from iterface table model - #discover type of interface looking at flavor - for numa in flavor_dict.get('extended',{}).get('numas',[]): - for flavor_iface in numa.get('interfaces',[]): - if flavor_iface.get('name') == iface['internal_name']: - if flavor_iface['dedicated'] == 'yes': - netDict['type']="PF" #passthrough - elif flavor_iface['dedicated'] == 'no': - netDict['type']="VF" #siov - elif flavor_iface['dedicated'] == 'yes:sriov': - netDict['type']="VFnotShared" #sriov but only one sriov on the PF - netDict["mac_address"] = flavor_iface.get("mac_address") - break; - netDict["use"]=iface['type'] - if netDict["use"]=="data" and not netDict.get("type"): - #print "netDict", netDict - #print "iface", iface - e_text = "Cannot determine the interface type PF or VF of VNF '%s' VM '%s' iface '%s'" %(sce_vnf['name'], vm['name'], iface['internal_name']) - if flavor_dict.get('extended')==None: - return -HTTP_Conflict, e_text + "After database migration some information is not available. \ - Try to delete and create the scenarios and VNFs again" - else: - return -HTTP_Internal_Server_Error, e_text - if netDict["use"]=="mgmt" or netDict["use"]=="bridge": - netDict["type"]="virtual" - if "vpci" in iface and iface["vpci"] is not None: - netDict['vpci'] = iface['vpci'] - if "mac" in iface and iface["mac"] is not None: - netDict['mac_address'] = iface['mac'] - netDict['name'] = iface['internal_name'] - if iface['net_id'] is None: - for vnf_iface in sce_vnf["interfaces"]: - print iface - print vnf_iface - if vnf_iface['interface_id']==iface['uuid']: - netDict['net_id'] = auxNetDict['scenario'][ vnf_iface['sce_net_id'] ] - break - else: - netDict['net_id'] = auxNetDict[ sce_vnf['uuid'] ][ iface['net_id'] ] - #skip bridge ifaces not connected to any net - #if 'net_id' not in netDict or netDict['net_id']==None: - # continue - myVMDict['networks'].append(netDict) - print ">>>>>>>>>>>>>>>>>>>>>>>>>>>" - print myVMDict['name'] - print "networks", yaml.safe_dump(myVMDict['networks'], indent=4, default_flow_style=False) - print "interfaces", yaml.safe_dump(vm['interfaces'], indent=4, default_flow_style=False) - print ">>>>>>>>>>>>>>>>>>>>>>>>>>>" - result, vm_id = myvim.new_tenant_vminstance(myVMDict['name'],myVMDict['description'],myVMDict.get('start', None), - myVMDict['imageRef'],myVMDict['flavorRef'],myVMDict['networks']) - if result < 0: - error_text = "Error creating vm instance: %s." % vm_id - _, message = rollback(mydb, vims, rollbackList) - error_text += message - print "start_scenario: " + error_text - return result, error_text - print "VIM vm instance id (server id) for scenario %s: %s" % (scenarioDict['name'],vm_id) - vm['vim_id'] = vm_id - rollbackList.append({'what':'vm','where':'vim','vim_id':datacenter_id,'uuid':vm_id}) - #put interface uuid back to scenario[vnfs][vms[[interfaces] - for net in myVMDict['networks']: - if "vim_id" in net: - for iface in vm['interfaces']: - if net["name"]==iface["internal_name"]: - iface["vim_id"]=net["vim_id"] - break + #create image at vim in case it not exist + res, image_dict = mydb.get_table_by_uuid_name("images", vm['image_id']) + if res<0: + logger.error("start_scenario error getting image %s", str(image_dict)) + return res, image_dict + res, image_id = create_or_use_image(mydb, vims, image_dict, [], True) + if res < 0: + logger.error("start_scenario error adding image to VIM: %s", str(image_dict)) + return res, image_id + vm['vim_image_id'] = image_id + + #create flavor at vim in case it not exist + res, flavor_dict = mydb.get_table_by_uuid_name("flavors", vm['flavor_id']) + if res<0: + logger.error("start_scenario error getting flavor: %s", str(flavor_dict)) + return res, flavor_dict + if flavor_dict['extended']!=None: + flavor_dict['extended']= yaml.load(flavor_dict['extended']) + res, flavor_id = create_or_use_flavor(mydb, vims, flavor_dict, [], True) + if res < 0: + logger.error("start_scenario error adding flavor to VIM: ", str(flavor_dict)) + return res, flavor_id + vm['vim_flavor_id'] = flavor_id - print "==================Deployment done==========" - print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) - #r,c = mydb.new_instance_scenario_as_a_whole(nfvo_tenant,scenarioDict['name'],scenarioDict) - result,c = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_scenario_name, instance_scenario_description, scenarioDict) - if result <0: - error_text = c + "." - _, message = rollback(mydb, vims, rollbackList) - error_text += message - print "start_scenario: " + error_text - return result, error_text + + myVMDict['imageRef'] = vm['vim_image_id'] + myVMDict['flavorRef'] = vm['vim_flavor_id'] + myVMDict['networks'] = [] + for iface in vm['interfaces']: + netDict = {} + if iface['type']=="data": + netDict['type'] = iface['model'] + elif "model" in iface and iface["model"]!=None: + netDict['model']=iface['model'] + #TODO in future, remove this because mac_address will not be set, and the type of PV,VF is obtained from iterface table model + #discover type of interface looking at flavor + for numa in flavor_dict.get('extended',{}).get('numas',[]): + for flavor_iface in numa.get('interfaces',[]): + if flavor_iface.get('name') == iface['internal_name']: + if flavor_iface['dedicated'] == 'yes': + netDict['type']="PF" #passthrough + elif flavor_iface['dedicated'] == 'no': + netDict['type']="VF" #siov + elif flavor_iface['dedicated'] == 'yes:sriov': + netDict['type']="VFnotShared" #sriov but only one sriov on the PF + netDict["mac_address"] = flavor_iface.get("mac_address") + break; + netDict["use"]=iface['type'] + if netDict["use"]=="data" and not netDict.get("type"): + #print "netDict", netDict + #print "iface", iface + e_text = "Cannot determine the interface type PF or VF of VNF '%s' VM '%s' iface '%s'" %(sce_vnf['name'], vm['name'], iface['internal_name']) + if flavor_dict.get('extended')==None: + return -HTTP_Conflict, e_text + "After database migration some information is not available. \ + Try to delete and create the scenarios and VNFs again" + else: + return -HTTP_Internal_Server_Error, e_text + if netDict["use"]=="mgmt" or netDict["use"]=="bridge": + netDict["type"]="virtual" + if "vpci" in iface and iface["vpci"] is not None: + netDict['vpci'] = iface['vpci'] + if "mac" in iface and iface["mac"] is not None: + netDict['mac_address'] = iface['mac'] + netDict['name'] = iface['internal_name'] + if iface['net_id'] is None: + for vnf_iface in sce_vnf["interfaces"]: + print iface + print vnf_iface + if vnf_iface['interface_id']==iface['uuid']: + netDict['net_id'] = auxNetDict['scenario'][ vnf_iface['sce_net_id'] ] + break + else: + netDict['net_id'] = auxNetDict[ sce_vnf['uuid'] ][ iface['net_id'] ] + #skip bridge ifaces not connected to any net + #if 'net_id' not in netDict or netDict['net_id']==None: + # continue + myVMDict['networks'].append(netDict) + #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>" + #print myVMDict['name'] + #print "networks", yaml.safe_dump(myVMDict['networks'], indent=4, default_flow_style=False) + #print "interfaces", yaml.safe_dump(vm['interfaces'], indent=4, default_flow_style=False) + #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>" + vm_id = myvim.new_vminstance(myVMDict['name'],myVMDict['description'],myVMDict.get('start', None), + myVMDict['imageRef'],myVMDict['flavorRef'],myVMDict['networks']) + #print "VIM vm instance id (server id) for scenario %s: %s" % (scenarioDict['name'],vm_id) + vm['vim_id'] = vm_id + rollbackList.append({'what':'vm','where':'vim','vim_id':datacenter_id,'uuid':vm_id}) + #put interface uuid back to scenario[vnfs][vms[[interfaces] + for net in myVMDict['networks']: + if "vim_id" in net: + for iface in vm['interfaces']: + if net["name"]==iface["internal_name"]: + iface["vim_id"]=net["vim_id"] + break - return mydb.get_instance_scenario(c) + logger.debug("start scenario Deployment done") + #print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) + #r,c = mydb.new_instance_scenario_as_a_whole(nfvo_tenant,scenarioDict['name'],scenarioDict) + result,c = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_scenario_name, instance_scenario_description, scenarioDict) + if result <0: + error_text = c + "." + _, message = rollback(mydb, vims, rollbackList) + error_text += message + print "start_scenario: " + error_text + return result, error_text + + return mydb.get_instance_scenario(c) + except vimconn.vimconnException as e: + _, message = rollback(mydb, vims, rollbackList) + error_text = "VIM exception {} {}. {}".format(type(e).__name__, str(e), message) + logger.error("start_scenario %s", error_text) + return -e.http_code, error_text def create_instance(mydb, tenant_id, instance_dict): - print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id" + #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id" scenario = instance_dict["scenario"] datacenter_id = None datacenter_name=None @@ -1452,12 +1445,12 @@ def create_instance(mydb, tenant_id, instance_dict): datacenter_name = datacenter result, vims = get_vim(mydb, tenant_id, datacenter_id, datacenter_name, vim_tenant=None) if result < 0: - print "start_scenario error. Datacenter not found" + logger.error("create_instance error. Datacenter not found") return result, vims elif result == 0: return -HTTP_Not_Found, "datacenter '%s' not found" % str(datacenter) elif result > 1: - print "start_scenario error. Several datacenters available, you must concrete" + logger.error("create_instance error. Several datacenters available, you must concrete") return -HTTP_Bad_Request, "Several datacenters available, you must concrete" myvim = vims.values()[0] #myvim_tenant = myvim['tenant_id'] @@ -1465,14 +1458,14 @@ def create_instance(mydb, tenant_id, instance_dict): datacenter_name = myvim['name'] datacenter_tenant_id = myvim['config']['datacenter_tenant_id'] rollbackList=[] - - print "Checking that the scenario exists and getting the scenario dictionary" + + #print "Checking that the scenario exists and getting the scenario dictionary" result, scenarioDict = mydb.get_scenario(scenario, tenant_id, datacenter_id) if result < 0: - print "start_scenario error. Error interacting with NFVO DB" + logger.error("create_instance error. Error interacting with NFVO DB") return result, scenarioDict elif result == 0: - print "start_scenario error. Scenario not found" + logger.error("create_instance error. Scenario not found") return result, scenarioDict scenarioDict['datacenter_tenant_id'] = datacenter_tenant_id @@ -1481,7 +1474,7 @@ def create_instance(mydb, tenant_id, instance_dict): auxNetDict = {} #Auxiliar dictionary. First key:'scenario' or sce_vnf uuid. Second Key: uuid of the net/sce_net. Value: vim_net_id auxNetDict['scenario'] = {} - print "scenario dict: ",yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) + #print "scenario dict: ",yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) instance_name = instance_dict["name"] instance_description = instance_dict.get("description") try: @@ -1493,7 +1486,7 @@ def create_instance(mydb, tenant_id, instance_dict): found = True break if not found: - raise NfvoException(-HTTP_Bad_Request, "Invalid scenario network name '%s' at instance:networks" % descriptor_net ) + raise NfvoException("Invalid scenario network name '{}' at instance:networks".format(descriptor_net), HTTP_Bad_Request) for descriptor_vnf in instance_dict.get("vnfs",{}).keys(): found=False for scenario_vnf in scenarioDict['vnfs']: @@ -1501,7 +1494,7 @@ def create_instance(mydb, tenant_id, instance_dict): found = True break if not found: - raise NfvoException(-HTTP_Bad_Request, "Invalid vnf name '%s' at instance:vnfs" % descriptor_vnf ) + raise NfvoException("Invalid vnf name '{}' at instance:vnfs".format(descriptor_vnf), HTTP_Bad_Request) #1. Creating new nets (sce_nets) in the VIM" for sce_net in scenarioDict['nets']: @@ -1552,23 +1545,19 @@ def create_instance(mydb, tenant_id, instance_dict): lookfor_network = False if lookfor_network: - result, vim_nets = myvim.get_network_list(filter_dict=lookfor_filter) - if result < 0: - raise NfvoException(result, "Not possible to get vim network list " + vim_nets) - elif len(vim_nets) > 1: - raise NfvoException(-HTTP_Bad_Request, "More than one candidate VIM network found for " + filter_text ) + vim_nets = myvim.get_network_list(filter_dict=lookfor_filter) + if len(vim_nets) > 1: + raise NfvoException("More than one candidate VIM network found for " + filter_text, HTTP_Bad_Request ) elif len(vim_nets) == 0: if not create_network: - raise NfvoException(-HTTP_Bad_Request, "No candidate VIM network found for " + filter_text ) + raise NfvoException("No candidate VIM network found for " + filter_text, HTTP_Bad_Request ) else: sce_net['vim_id'] = vim_nets[0]['id'] auxNetDict['scenario'][sce_net['uuid']] = vim_nets[0]['id'] create_network = False if create_network: #if network is not external - result, network_id = myvim.new_tenant_network(net_vim_name, net_type) - if result < 0: - raise NfvoException(result, "Error creating vim network " + network_id) + network_id = myvim.new_network(net_vim_name, net_type) sce_net['vim_id'] = network_id auxNetDict['scenario'][sce_net['uuid']] = network_id rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id}) @@ -1583,20 +1572,18 @@ def create_instance(mydb, tenant_id, instance_dict): net_name = "%s.%s" %(instance_name, net["name"]) net_name = net_name[:255] #limit length net_type = net['type'] - result, network_id = myvim.new_tenant_network(net_name, net_type) - if result < 0: - raise NfvoException(result, "Error creating vim network " + network_id) + network_id = myvim.new_network(net_name, net_type) net['vim_id'] = network_id if sce_vnf['uuid'] not in auxNetDict: auxNetDict[sce_vnf['uuid']] = {} auxNetDict[sce_vnf['uuid']][net['uuid']] = network_id rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id}) - print "auxNetDict:" - print yaml.safe_dump(auxNetDict, indent=4, default_flow_style=False) + #print "auxNetDict:" + #print yaml.safe_dump(auxNetDict, indent=4, default_flow_style=False) #3. Creating new vm instances in the VIM - #myvim.new_tenant_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict) + #myvim.new_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict) for sce_vnf in scenarioDict['vnfs']: i = 0 for vm in sce_vnf['vms']: @@ -1610,21 +1597,21 @@ def create_instance(mydb, tenant_id, instance_dict): #create image at vim in case it not exist res, image_dict = mydb.get_table_by_uuid_name("images", vm['image_id']) if res<0: - raise NfvoException(result, "Error getting VIM image "+ image_dict) + raise NfvoException("Error getting VIM image "+ image_dict, -result) res, image_id = create_or_use_image(mydb, vims, image_dict, [], True) if res < 0: - raise NfvoException(result, "Error adding image to VIM " + image_dict) + raise NfvoException("Error adding image to VIM " + image_dict, -result) vm['vim_image_id'] = image_id #create flavor at vim in case it not exist res, flavor_dict = mydb.get_table_by_uuid_name("flavors", vm['flavor_id']) if res<0: - raise NfvoException(result, "Error getting VIM flavor "+ flavor_dict) + raise NfvoException("Error getting VIM flavor "+ flavor_dict, -result) if flavor_dict['extended']!=None: flavor_dict['extended']= yaml.load(flavor_dict['extended']) res, flavor_id = create_or_use_flavor(mydb, vims, flavor_dict, [], True) if res < 0: - raise NfvoException(result, "Error adding flavor to VIM" + flavor_dict) + raise NfvoException("Error adding flavor to VIM" + flavor_dict, -result) vm['vim_flavor_id'] = flavor_id myVMDict['imageRef'] = vm['vim_image_id'] @@ -1656,10 +1643,10 @@ def create_instance(mydb, tenant_id, instance_dict): #print "iface", iface e_text = "Cannot determine the interface type PF or VF of VNF '%s' VM '%s' iface '%s'" %(sce_vnf['name'], vm['name'], iface['internal_name']) if flavor_dict.get('extended')==None: - raise NfvoException(-HTTP_Conflict, e_text + "After database migration some information is not available. \ - Try to delete and create the scenarios and VNFs again") + raise NfvoException(e_text + "After database migration some information is not available. \ + Try to delete and create the scenarios and VNFs again", HTTP_Conflict) else: - raise NfvoException(-HTTP_Internal_Server_Error, e_text) + raise NfvoException(e_text, HTTP_Internal_Server_Error) if netDict["use"]=="mgmt" or netDict["use"]=="bridge": netDict["type"]="virtual" if "vpci" in iface and iface["vpci"] is not None: @@ -1680,15 +1667,13 @@ def create_instance(mydb, tenant_id, instance_dict): #if 'net_id' not in netDict or netDict['net_id']==None: # continue myVMDict['networks'].append(netDict) - print ">>>>>>>>>>>>>>>>>>>>>>>>>>>" - print myVMDict['name'] - print "networks", yaml.safe_dump(myVMDict['networks'], indent=4, default_flow_style=False) - print "interfaces", yaml.safe_dump(vm['interfaces'], indent=4, default_flow_style=False) - print ">>>>>>>>>>>>>>>>>>>>>>>>>>>" - result, vm_id = myvim.new_tenant_vminstance(myVMDict['name'],myVMDict['description'],myVMDict.get('start', None), + #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>" + #print myVMDict['name'] + #print "networks", yaml.safe_dump(myVMDict['networks'], indent=4, default_flow_style=False) + #print "interfaces", yaml.safe_dump(vm['interfaces'], indent=4, default_flow_style=False) + #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>" + vm_id = myvim.new_vminstance(myVMDict['name'],myVMDict['description'],myVMDict.get('start', None), myVMDict['imageRef'],myVMDict['flavorRef'],myVMDict['networks']) - if result < 0: - raise NfvoException(result, "Error creating VIM instance" + vm_id) vm['vim_id'] = vm_id rollbackList.append({'what':'vm','where':'vim','vim_id':datacenter_id,'uuid':vm_id}) #put interface uuid back to scenario[vnfs][vms[[interfaces] @@ -1698,39 +1683,43 @@ def create_instance(mydb, tenant_id, instance_dict): if net["name"]==iface["internal_name"]: iface["vim_id"]=net["vim_id"] break - print "==================Deployment done==========" - print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) + logger.debug("create_instance Deployment done") + #print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) #r,c = mydb.new_instance_scenario_as_a_whole(nfvo_tenant,scenarioDict['name'],scenarioDict) result,c = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_name, instance_description, scenarioDict) if result <0: - raise NfvoException(result, c) + raise NfvoException(c, -result) return mydb.get_instance_scenario(c) except NfvoException as e: - error_text = str(e) + ". " _, message = rollback(mydb, vims, rollbackList) - error_text += message - print "create_instance: " + error_text - return e.error_code, error_text - + error_text = "{} {}. {}".format(type(e).__name__, str(e), message) + logger.error("create_instance: %s", error_text) + return -e.http_code, error_text + except vimconn.vimconnException as e: + _, message = rollback(mydb, vims, rollbackList) + error_text = "VIM exception {} {}. {}".format(type(e).__name__, str(e), message) + logger.error("create_instance: %s", error_text) + return -e.http_code, error_text + def delete_instance(mydb, tenant_id, instance_id): - print "Checking that the instance_id exists and getting the instance dictionary" + #print "Checking that the instance_id exists and getting the instance dictionary" result, instanceDict = mydb.get_instance_scenario(instance_id, tenant_id) if result < 0: - print "nfvo.delete_instance() error. Error getting info from database" + logger.error("nfvo.delete_instance() error. Error getting info from database") return result, instanceDict elif result == 0: - print "delete_instance error. Instance not found" + logger.error("delete_instance error. Instance not found") return result, instanceDict - print yaml.safe_dump(instanceDict, indent=4, default_flow_style=False) + #print yaml.safe_dump(instanceDict, indent=4, default_flow_style=False) tenant_id = instanceDict["tenant_id"] - print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id" + #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id" result, vims = get_vim(mydb, tenant_id, instanceDict['datacenter_id']) if result < 0: - print "nfvo.delete_instance() error. Datacenter not found" + logger.error("nfvo.delete_instance() error. Datacenter error %s %s", result, vims) return result, vims elif result == 0: - print "!!!!!! nfvo.delete_instance() datacenter not found!!!!" + logger.error("!!!!!! nfvo.delete_instance() datacenter not found!!!!") myvim = None else: myvim = vims.values()[0] @@ -1755,11 +1744,16 @@ def delete_instance(mydb, tenant_id, instance_id): if not myvim: continue for vm in sce_vnf['vms']: - result, vm_id = myvim.delete_tenant_vminstance(vm['vim_vm_id']) - if result < 0: - error_msg+="\n Error: " + str(-result) + " VM id=" + vm['vim_vm_id'] - #if result != -HTTP_Not_Found: vm_fail_list.append(vm) - print "Error " + str(-result) + " deleting VM instance '" + vm['name'] + "', uuid '" + vm['uuid'] + "', VIM id '" + vm['vim_vm_id'] + "', from VNF_id '" + sce_vnf['vnf_id'] + "':" + vm_id + try: + myvim.delete_vminstance(vm['vim_vm_id']) + except vimconn.vimconnNotFoundException as e: + error_msg+="\n VM id={} not found at VIM".format(vm['vim_vm_id']) + logger.warn("VM instance '%s'uuid '%s', VIM id '%s', from VNF_id '%s' not found", + vm['name'], vm['uuid'], vm['vim_vm_id'], sce_vnf['vnf_id']) + except vimconn.vimconnException as e: + error_msg+="\n Error: " + e.http_code + " VM id=" + vm['vim_vm_id'] + logger.error("Error %d deleting VM instance '%s'uuid '%s', VIM id '%s', from VNF_id '%s': %s", + e.http_code, vm['name'], vm['uuid'], vm['vim_vm_id'], sce_vnf['vnf_id'], str(e)) #2.2 deleting NETS #net_fail_list=[] @@ -1768,12 +1762,16 @@ def delete_instance(mydb, tenant_id, instance_id): continue #skip not created nets if not myvim: continue - result, net_id = myvim.delete_tenant_network(net['vim_net_id']) - if result < 0: - error_msg += "\n Error: " + str(-result) + " NET id=" + net['vim_net_id'] - #if result == -HTTP_Not_Found: net_fail_list.append(net) - print "Error " + str(-result) + " deleting NET uuid '" + net['uuid'] + "', VIM id '" + net['vim_net_id'] + "':" + net_id - + try: + myvim.delete_network(net['vim_net_id']) + except vimconn.vimconnNotFoundException as e: + error_msg+="\n NET id={} not found at VIM".format(net['vim_net_id']) + logger.warn("NET '%s', VIM id '%s', from VNF_id '%s' not found", + net['uuid'], vm['vim_net_id'], sce_vnf['vnf_id']) + except vimconn.vimconnException as e: + error_msg+="\n Error: " + e.http_code + " Net id=" + net['vim_vm_id'] + logger.error("Error %d deleting NET '%s', VIM id '%s', from VNF_id '%s': %s", + e.http_code, net['uuid'], net['vim_net_id'], sce_vnf['vnf_id'], str(e)) if len(error_msg)>0: return 1, 'instance ' + instance_id + ' deleted but some elements could not be deleted, or already deleted (error: 404) from VIM: ' + error_msg else: @@ -1786,125 +1784,129 @@ def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenan - error_msg ''' # Assumption: nfvo_tenant and instance_id were checked before entering into this function - print "nfvo.refresh_instance begins" + #print "nfvo.refresh_instance begins" #print json.dumps(instanceDict, indent=4) - print "Getting the VIM URL and the VIM tenant_id" + #print "Getting the VIM URL and the VIM tenant_id" result, vims = get_vim(mydb, nfvo_tenant, instanceDict['datacenter_id']) if result < 0: - print "nfvo.refresh_instance() error. Datacenter not found" + logger.error("nfvo.refresh_instance() error. Datacenter not found") return result, vims elif result == 0: return -HTTP_Not_Found, "datacenter '%s' not found" % str(instanceDict['datacenter_id']) myvim = vims.values()[0] - # 1. Getting the status of all VMs - vmDict = {} - netDict = {} - for sce_vnf in instanceDict['vnfs']: - for vm in sce_vnf['vms']: - vmDict[vm['vim_vm_id']]=None - print "VACA vm", vm - - # 2. Getting the status of all nets - # TODO: update nets inside a vnf - for net in instanceDict['nets']: - #if net['external']: - # continue #skip not created nets - netDict[net['vim_net_id']]=None - - # 3. Refresh the status of VMs and nets from VIM. IT updates vmDict and netDict - result, refresh_message = myvim.refresh_tenant_vms_and_nets(vmDict, netDict) - if result < 0: - return result, refresh_message - - # 4. Update the status of VMs in the instanceDict, while collects the VMs whose status changed + # 1. Getting VIM vm and net list vms_updated = [] #List of VM instance uuids in openmano that were updated vms_notupdated=[] + vm_list = [] for sce_vnf in instanceDict['vnfs']: for vm in sce_vnf['vms']: - vm_id = vm['vim_vm_id'] - interfaces = vmDict[vm_id].pop('interfaces', []) - #4.0 look if contain manamgement interface, and if not change status from ACTIVE:NoMgmtIP to ACTIVE - has_mgmt_iface = False - for iface in vm["interfaces"]: - if iface["type"]=="mgmt": - has_mgmt_iface = True - if vmDict[vm_id]['status'] == "ACTIVE:NoMgmtIP" and not has_mgmt_iface: - vmDict[vm_id]['status'] = "ACTIVE" - if vm['status'] != vmDict[vm_id]['status'] or vm.get('error_msg')!=vmDict[vm_id].get('error_msg') or vm.get('vim_info')!=vmDict[vm_id].get('vim_info'): - vm['status'] = vmDict[vm_id]['status'] - vm['error_msg'] = vmDict[vm_id].get('error_msg') - vm['vim_info'] = vmDict[vm_id].get('vim_info') - # 4.1. Update in openmano DB the VMs whose status changed - result2, _ = mydb.update_rows('instance_vms', UPDATE=vmDict[vm_id], WHERE={'uuid':vm["uuid"]}) - if result2<0: - vms_notupdated.append(vm["uuid"]) - elif result2>0: - vms_updated.append(vm["uuid"]) - # 4.2. Update in openmano DB the interface VMs - for interface in interfaces: - #translate from vim_net_id to instance_net_id - network_id=None - for net in instanceDict['nets']: - if net["vim_net_id"] == interface["vim_net_id"]: - network_id = net["uuid"] - break - if not network_id: - continue - del interface["vim_net_id"] - result2, _ = mydb.update_rows('instance_interfaces', UPDATE=interface, WHERE={'instance_vm_id':vm["uuid"], "instance_net_id":network_id}) - if result2<0: - print "nfvo.refresh_instance error with vm=%s, interface_net_id=%s" % (vm["uuid"], network_id) - # 5. Update the status of nets in the instanceDict, while collects the nets whose status changed - nets_updated = [] #List of net instance uuids in openmano that were updated + vm_list.append(vm['vim_vm_id']) + vms_notupdated.append(vm["uuid"]) + + nets_updated = [] #List of VM instance uuids in openmano that were updated nets_notupdated=[] - # TODO: update nets inside a vnf + net_list=[] for net in instanceDict['nets']: - net_id = net['vim_net_id'] - if net['status'] != netDict[net_id]['status'] or net.get('error_msg')!=netDict[net_id].get('error_msg') or net.get('vim_info')!=netDict[net_id].get('vim_info'): - net['status'] = netDict[net_id]['status'] - net['error_msg'] = netDict[net_id].get('error_msg') - net['vim_info'] = netDict[net_id].get('vim_info') - # 5.1. Update in openmano DB the nets whose status changed - result2, _ = mydb.update_rows('instance_nets', UPDATE=netDict[net_id], WHERE={'uuid':net["uuid"]}) - if result2<0: - nets_notupdated.append(net["uuid"]) - elif result2>0: - nets_updated.append(net["uuid"]) + net_list.append(net['vim_net_id']) + nets_notupdated.append(net["uuid"]) + + try: + # 1. Getting the status of all VMs + vm_dict = myvim.refresh_vms_status(vm_list) + + # 2. Update the status of VMs in the instanceDict, while collects the VMs whose status changed + for sce_vnf in instanceDict['vnfs']: + for vm in sce_vnf['vms']: + vm_id = vm['vim_vm_id'] + interfaces = vm_dict[vm_id].pop('interfaces', []) + #2.0 look if contain manamgement interface, and if not change status from ACTIVE:NoMgmtIP to ACTIVE + has_mgmt_iface = False + for iface in vm["interfaces"]: + if iface["type"]=="mgmt": + has_mgmt_iface = True + if vm_dict[vm_id]['status'] == "ACTIVE:NoMgmtIP" and not has_mgmt_iface: + vm_dict[vm_id]['status'] = "ACTIVE" + if vm['status'] != vm_dict[vm_id]['status'] or vm.get('error_msg')!=vm_dict[vm_id].get('error_msg') or vm.get('vim_info')!=vm_dict[vm_id].get('vim_info'): + vm['status'] = vm_dict[vm_id]['status'] + vm['error_msg'] = vm_dict[vm_id].get('error_msg') + vm['vim_info'] = vm_dict[vm_id].get('vim_info') + # 2.1. Update in openmano DB the VMs whose status changed + result2, _ = mydb.update_rows('instance_vms', UPDATE=vm_dict[vm_id], WHERE={'uuid':vm["uuid"]}) + if result2<0: + logger.error("nfvo.refresh_instance error database update: %s", result2) + else: + vms_notupdated.remove(vm["uuid"]) + if result2>0: + vms_updated.append(vm["uuid"]) + # 2.2. Update in openmano DB the interface VMs + for interface in interfaces: + #translate from vim_net_id to instance_net_id + network_id=None + for net in instanceDict['nets']: + if net["vim_net_id"] == interface["vim_net_id"]: + network_id = net["uuid"] + break + if not network_id: + continue + del interface["vim_net_id"] + result2, _ = mydb.update_rows('instance_interfaces', UPDATE=interface, WHERE={'instance_vm_id':vm["uuid"], "instance_net_id":network_id}) + if result2<0: + logger.error( "nfvo.refresh_instance error with vm=%s, interface_net_id=%s", vm["uuid"], network_id) + + # 3. Getting the status of all nets + net_dict = myvim.refresh_nets_status(net_list) + + # 4. Update the status of nets in the instanceDict, while collects the nets whose status changed + # TODO: update nets inside a vnf + for net in instanceDict['nets']: + net_id = net['vim_net_id'] + if net['status'] != net_dict[net_id]['status'] or net.get('error_msg')!=net_dict[net_id].get('error_msg') or net.get('vim_info')!=net_dict[net_id].get('vim_info'): + net['status'] = net_dict[net_id]['status'] + net['error_msg'] = net_dict[net_id].get('error_msg') + net['vim_info'] = net_dict[net_id].get('vim_info') + # 5.1. Update in openmano DB the nets whose status changed + result2, _ = mydb.update_rows('instance_nets', UPDATE=net_dict[net_id], WHERE={'uuid':net["uuid"]}) + if result2<0: + logger.error("nfvo.refresh_instance error database update: %s", result2) + else: + nets_notupdated.remove(net["uuid"]) + if result2>0: + nets_updated.append(net["uuid"]) + except vimconn.vimconnException as e: + logger.error("VIM exception %s %s", type(e).__name__, str(e)) + return -e.http_code, str(e) # Returns appropriate output - print "nfvo.refresh_instance finishes" - print "VMs updated in the database: %s; nets updated in the database %s; VMs not updated: %s; nets not updated: %s" \ - % (str(vms_updated), str(nets_updated), str(vms_notupdated), str(nets_notupdated)) + #print "nfvo.refresh_instance finishes" + logger.debug("VMs updated in the database: %s; nets updated in the database %s; VMs not updated: %s; nets not updated: %s", + str(vms_updated), str(nets_updated), str(vms_notupdated), str(nets_notupdated)) instance_id = instanceDict['uuid'] - error_msg=refresh_message if len(vms_notupdated)+len(nets_notupdated)>0: - if len(refresh_message)>0: - error_msg += "; " - error_msg += "VMs not updated: " + str(vms_notupdated) + "; nets not updated: " + str(nets_notupdated) + error_msg = "VMs not updated: " + str(vms_notupdated) + "; nets not updated: " + str(nets_notupdated) return len(vms_notupdated)+len(nets_notupdated), 'Scenario instance ' + instance_id + ' refreshed but some elements could not be updated in the database: ' + error_msg - return 0, 'Scenario instance ' + instance_id + ' refreshed. ' + error_msg + return 0, 'Scenario instance ' + instance_id + ' refreshed.' def instance_action(mydb,nfvo_tenant,instance_id, action_dict): - print "Checking that the instance_id exists and getting the instance dictionary" + #print "Checking that the instance_id exists and getting the instance dictionary" result, instanceDict = mydb.get_instance_scenario(instance_id, nfvo_tenant) if result < 0: - print "nfvo.instance_action() error. Error getting info from database" + logger.error("nfvo.instance_action() error. Error getting info from database") return result, instanceDict elif result == 0: - print "instance_action error. Instance not found" - return -HTTP_Not_Found, "instance %s not found" % instance_id + logger.error("instance_action error. Instance not found") + return -HTTP_Not_Found, "instance {} not found".format(instance_id) #print yaml.safe_dump(instanceDict, indent=4, default_flow_style=False) - print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id" + #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id" result, vims = get_vim(mydb, nfvo_tenant, instanceDict['datacenter_id']) if result < 0: - print "nfvo.instance_action() error. Datacenter not found" + logger.error("nfvo.instance_action() error. Datacenter not found") return result, vims elif result == 0: - return -HTTP_Not_Found, "datacenter '%s' not found" % str(instanceDict['datacenter_id']) + return -HTTP_Not_Found, "datacenter '{}' not found".format(str(instanceDict['datacenter_id'])) myvim = vims.values()[0] @@ -1920,11 +1922,8 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict): if sce_vnf['uuid'] not in input_vnfs and sce_vnf['vnf_name'] not in input_vnfs and \ vm['uuid'] not in input_vms and vm['name'] not in input_vms: continue - result, data = myvim.action_tenant_vminstance(vm['vim_vm_id'], action_dict) - if result < 0: - vm_result[ vm['uuid'] ] = {"vim_result": -result, "name":vm['name'], "description": data} - vm_error+=1 - else: + try: + data = myvim.action_vminstance(vm['vim_vm_id'], action_dict) if "console" in action_dict: if data["server"]=="127.0.0.1" or data["server"]=="localhost": vm_result[ vm['uuid'] ] = {"vim_result": -HTTP_Unauthorized, @@ -1946,6 +1945,9 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict): else: vm_result[ vm['uuid'] ] = {"vim_result": result, "description": "ok", "name":vm['name']} vm_ok +=1 + except vimconn.vimconnException as e: + vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)} + vm_error+=1 if vm_ok==0: #all goes wrong return 1, vm_result @@ -2105,9 +2107,10 @@ def associate_datacenter_to_tenant(mydb, nfvo_tenant, datacenter, vim_tenant_id= #insert at table datacenter_tenants else: #if vim_tenant_id==None: #create tenant at VIM if not provided - res, vim_tenant_id = myvim.new_tenant(vim_tenant_name, "created by openmano for datacenter "+datacenter_name) - if res < 0: - return res, "Not possible to create vim_tenant in VIM " + vim_tenant_id + try: + vim_tenant_id = myvim.new_tenant(vim_tenant_name, "created by openmano for datacenter "+datacenter_name) + except vimconn.vimconnException as e: + return -HTTP_Internal_Server_Error, "Not possible to create vim_tenant {} at VIM: {}".format(vim_tenant_id, str(e)) datacenter_tenants_dict = {} datacenter_tenants_dict["created"]="true" @@ -2182,10 +2185,11 @@ def deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter, vim_tenant_id= pass #the error will be caused because dependencies, vim_tenant can not be deleted elif vim_tenant_dict['created']=='true': #delete tenant at VIM if created by NFVO - res, vim_tenant_id = myvim.delete_tenant(vim_tenant_dict['vim_tenant_id']) - if res < 0: - warning = " Not possible to delete vim_tenant id %s name %s from VIM: %s " % (vim_tenant_dict['vim_tenant_id'], vim_tenant_dict['vim_tenant_name'], vim_tenant_id) - print res, warning + try: + myvim.delete_tenant(vim_tenant_dict['vim_tenant_id']) + except vimconn.vimconnException as e: + warning = "Not possible to delete vim_tenant_id {} from VIM: {} ".format(vim_tenant_dict['vim_tenant_id'], str(e)) + logger.warn(warning) return 200, "datacenter %s detached.%s" %(datacenter_id, warning) @@ -2197,22 +2201,23 @@ def datacenter_action(mydb, tenant_id, datacenter, action_dict): else: result, vims = get_vim(mydb, nfvo_tenant=tenant_id, datacenter_name=datacenter) if result < 0: - print "nfvo.datacenter_action() error. Datacenter not found" + logger.error("nfvo.datacenter_action() error. Datacenter not found") return result, vims elif result == 0: return -HTTP_Not_Found, "datacenter '%s' not found" % str(datacenter) elif result>1: - print "nfvo.datacenter_action() error. Several datacenters found" + logger.error("nfvo.datacenter_action() error. Several datacenters found") return -HTTP_Conflict, "More than one datacenters found, try to identify with uuid" datacenter_id=vims.keys()[0] myvim=vims[datacenter_id] if 'net-update' in action_dict: - result, content = myvim.get_network_list(filter_dict={'shared': True, 'admin_state_up': True, 'status': 'ACTIVE'}) - print content - if result < 0: - print " Not possible to get_network_list from VIM: %s " % (content) - return -HTTP_Internal_Server_Error, content + try: + content = myvim.get_network_list(filter_dict={'shared': True, 'admin_state_up': True, 'status': 'ACTIVE'}) + #print content + except vimconn.vimconnException as e: + logger.error("nfvo.datacenter_action() Not possible to get_network_list from VIM: %s ", str(e)) + return -HTTP_Internal_Server_Error, str(e) #update nets Change from VIM format to NFVO format net_list=[] for net in content: @@ -2227,7 +2232,7 @@ def datacenter_action(mydb, tenant_id, datacenter, action_dict): result, content = mydb.update_datacenter_nets(datacenter_id, net_list) if result < 0: return -HTTP_Internal_Server_Error, content - print "Inserted %d nets, deleted %d old nets" % (result, content) + logger.info("Inserted %d nets, deleted %d old nets", result, content) return 200, result elif 'net-edit' in action_dict: @@ -2274,12 +2279,12 @@ def datacenter_new_netmap(mydb, tenant_id, datacenter, action_dict=None): else: result, vims = get_vim(mydb, nfvo_tenant=tenant_id, datacenter_name=datacenter) if result < 0: - print "nfvo.datacenter_new_netmap() error. Datacenter not found" + logger.error("nfvo.datacenter_new_netmap() error. Datacenter not found") return result, vims elif result == 0: return -HTTP_Not_Found, "datacenter '%s' not found" % str(datacenter) elif result>1: - print "nfvo.datacenter_new_netmap() error. Several datacenters found" + logger.error("nfvo.datacenter_new_netmap() error. Several datacenters found") return -HTTP_Conflict, "More than one datacenters found, try to identify with uuid" datacenter_id=vims.keys()[0] myvim=vims[datacenter_id] @@ -2293,12 +2298,12 @@ def datacenter_new_netmap(mydb, tenant_id, datacenter, action_dict=None): else: filter_dict["shared"] = True - result, content = myvim.get_network_list(filter_dict=filter_dict) - print result, content - if result <0: - print " Not possible to get_network_list from VIM: %s " % (content) - return -HTTP_Internal_Server_Error, content - elif len(content)>1 and action_dict: + try: + content = myvim.get_network_list(filter_dict=filter_dict) + except vimconn.vimconnException as e: + logger.error("nfvo.datacenter_new_netmap() Not possible to get_network_list from VIM: %s ", str(e)) + return -HTTP_Internal_Server_Error, str(e) + if len(content)>1 and action_dict: return -HTTP_Conflict, "more than two networks found, specify with vim_id" elif len(content)==0: # and action_dict: return -HTTP_Not_Found, "Not found a network at VIM with " + str(filter_dict) @@ -2347,23 +2352,24 @@ def vim_action_get(mydb, tenant_id, datacenter, item, name): filter_dict["id"] = name else: filter_dict["name"] = name - if item=="networks": - #filter_dict['tenant_id'] = myvim['tenant_id'] - result, content = myvim.get_network_list(filter_dict=filter_dict) - elif item=="tenants": - result, content = myvim.get_tenant_list(filter_dict=filter_dict) - else: - return -HTTP_Method_Not_Allowed, item + "?" - if result < 0: - print "vim_action Not possible to get_%s_list from VIM: %s " % (item, content) - return -HTTP_Internal_Server_Error, content - print "vim_action response ", content #update nets Change from VIM format to NFVO format - if name and len(content)==1: - return 200, {item[:-1]: content[0]} - elif name and len(content)==0: - return -HTTP_Not_Found, "No %s found with %s" % (item[:-1], " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), filter_dict.iteritems()))) - else: - return 200, {item: content} + try: + if item=="networks": + #filter_dict['tenant_id'] = myvim['tenant_id'] + content = myvim.get_network_list(filter_dict=filter_dict) + elif item=="tenants": + content = myvim.get_tenant_list(filter_dict=filter_dict) + else: + return -HTTP_Method_Not_Allowed, item + "?" + print "vim_action response ", content #update nets Change from VIM format to NFVO format + if name and len(content)==1: + return 200, {item[:-1]: content[0]} + elif name and len(content)==0: + return -HTTP_Not_Found, "No %s found with %s" % (item[:-1], " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), filter_dict.iteritems()))) + else: + return 200, {item: content} + except vimconn.vimconnException as e: + print "vim_action Not possible to get_%s_list from VIM: %s " % (item, str(e)) + return -e.http_code, "Not possible to get_{}_list from VIM: {}".format(item, str(e)) def vim_action_delete(mydb, tenant_id, datacenter, item, name): #get datacenter info @@ -2398,16 +2404,21 @@ def vim_action_delete(mydb, tenant_id, datacenter, item, name): item_id = items["id"] item_name = str(items.get("name")) - if item=="networks": - result, content = myvim.delete_tenant_network(item_id) - elif item=="tenants": - result, content = myvim.delete_tenant(item_id) - else: - return -HTTP_Method_Not_Allowed, item + "?" + try: + if item=="networks": + content = myvim.delete_network(item_id) + elif item=="tenants": + content = myvim.delete_tenant(item_id) + else: + return -HTTP_Method_Not_Allowed, item + "?" + except vimconn.vimconnException as e: + print "vim_action Not possible to delete_{} {}from VIM: {} ".format(item, name, str(e)) + return -e.http_code, "Not possible to delete_{} {} from VIM: {}".format(item, name, str(e)) + if result < 0: print "vim_action Not possible to delete %s %s from VIM: %s " % (item, name, content) return result, content - return 200, "%s %s %s deleted" %( item[:-1], item_id,item_name) + return 200, "{} {} {} deleted".format(item[:-1], item_id,item_name) def vim_action_create(mydb, tenant_id, datacenter, item, descriptor): #get datacenter info @@ -2430,20 +2441,22 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor): datacenter_id=vims.keys()[0] myvim=vims[datacenter_id] - if item=="networks": - net = descriptor["network"] - net_name = net.pop("name") - net_type = net.pop("type", "bridge") - net_public=net.pop("shared", False) - result, content = myvim.new_tenant_network(net_name, net_type, net_public, **net) - elif item=="tenants": - tenant = descriptor["tenant"] - result, content = myvim.new_tenant(tenant["name"], tenant.get("description")) - else: - return -HTTP_Method_Not_Allowed, item + "?" - if result < 0: - print "vim_action Not possible to create %s at VIM: %s " % (item, content) - return result, content + try: + if item=="networks": + net = descriptor["network"] + net_name = net.pop("name") + net_type = net.pop("type", "bridge") + net_public=net.pop("shared", False) + content = myvim.new_network(net_name, net_type, net_public, **net) + elif item=="tenants": + tenant = descriptor["tenant"] + content = myvim.new_tenant(tenant["name"], tenant.get("description")) + else: + return -HTTP_Method_Not_Allowed, item + "?" + except vimconn.vimconnException as e: + print "vim_action Not possible to create {} at VIM: {} ".format(item, str(e)) + return -e.http_code, "Not possible to create {} at VIM: {}".format(item, str(e)) + return vim_action_get(mydb, tenant_id, datacenter, item, content) diff --git a/openmano b/openmano index b207d291..93c5d6bb 100755 --- a/openmano +++ b/openmano @@ -1018,7 +1018,7 @@ def datacenter_netmap_action(args): elif args.all: force_text="Delete all default netmaps from datacenter '%s' (y/N)? " % (datacenter) else: - print "you must a netmap name or the option --all" + print "you must specify a netmap name or the option --all" return 1 if not args.force: r = raw_input(force_text) diff --git a/openmano_schemas.py b/openmano_schemas.py index a563b1a9..4ba38b56 100644 --- a/openmano_schemas.py +++ b/openmano_schemas.py @@ -51,6 +51,7 @@ ip_schema={"type":"string","pattern":"^([0-9]{1,3}.){3}[0-9]{1,3}$"} port_schema={"type":"integer","minimum":1,"maximum":65534} object_schema={"type":"object"} schema_version_2={"type":"integer","minimum":2,"maximum":2} +log_level_schema={"type":"string", "enum":["DEBUG", "INFO", "WARNING","ERROR","CRITICAL"]} metadata_schema={ "type":"object", @@ -94,6 +95,9 @@ config_schema = { {"type":"object", "properties":{"from": port_schema, "to": port_schema}, "required": ["from","to"]} ]} }, + "log_level": log_level_schema, + "log_level_db": log_level_schema, + "log_level_vimconn": log_level_schema }, "required": ['db_host', 'db_user', 'db_passwd', 'db_name'], "additionalProperties": False diff --git a/openmanod.cfg b/openmanod.cfg index 8670fe45..e8aba162 100644 --- a/openmanod.cfg +++ b/openmanod.cfg @@ -44,3 +44,8 @@ db_name: mano_db # Name of the MANO DB # The folder will be created in the execution folder if it does not exist vnf_repository: "./vnfrepo" # Use an absolute path to avoid misunderstandings + +#logging parameters # DEBUG, INFO, WARNING, ERROR, CRITICAL +log_level: DEBUG +log_level_db: DEBUG +log_level_vimconn: DEBUG diff --git a/openmanod.py b/openmanod.py index 01ac0f5f..bf3c3aa9 100755 --- a/openmanod.py +++ b/openmanod.py @@ -33,8 +33,8 @@ 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.4.38-r470" -version_date="Apr 2016" +__version__="0.4.39-r472" +version_date="May 2016" database_version="0.10" #expected database schema version import httpserver @@ -48,20 +48,30 @@ from jsonschema import validate as js_v, exceptions as js_e import utils from openmano_schemas import config_schema import nfvo +import logging global global_config +logger = logging.getLogger('mano') + +class LoadConfigurationException(Exception): + pass def load_configuration(configuration_file): - default_tokens ={'http_port':9090, 'http_host':'localhost'} + default_tokens ={'http_port':9090, + 'http_host':'localhost', + 'log_level': 'DEBUG', + 'log_level_db': 'ERROR', + 'log_level_vimconn': 'DEBUG', + } try: #Check config file exists if not os.path.isfile(configuration_file): - return (False, "Error: Configuration file '"+configuration_file+"' does not exists.") + raise LoadConfigurationException("Error: Configuration file '"+configuration_file+"' does not exist.") #Read file (return_status, code) = utils.read_file(configuration_file) if not return_status: - return (return_status, "Error loading configuration file '"+configuration_file+"': "+code) + raise LoadConfigurationException("Error loading configuration file '"+configuration_file+"': "+code) #Parse configuration file try: config = yaml.load(code) @@ -70,7 +80,7 @@ def load_configuration(configuration_file): if hasattr(exc, 'problem_mark'): mark = exc.problem_mark error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1) - return (False, "Error loading configuration file '"+configuration_file+"'"+error_pos+": content format error: Failed to parse yaml format") + raise LoadConfigurationException("Error loading configuration file '"+configuration_file+"'"+error_pos+": content format error: Failed to parse yaml format") #Validate configuration file with the config_schema try: @@ -78,16 +88,16 @@ def load_configuration(configuration_file): except js_e.ValidationError, exc: error_pos = "" if len(exc.path)>0: error_pos=" at '" + ":".join(map(str, exc.path))+"'" - return False, "Error loading configuration file '"+configuration_file+"'"+error_pos+": "+exc.message + raise LoadConfigurationException("Error loading configuration file '"+configuration_file+"'"+error_pos+": "+exc.message) #Check default values tokens for k,v in default_tokens.items(): if k not in config: config[k]=v except Exception,e: - return (False, "Error loading configuration file '"+configuration_file+"': "+str(e)) + raise LoadConfigurationException("Error loading configuration file '"+configuration_file+"': "+str(e)) - return (True, config) + return config def console_port_iterator(): '''this iterator deals with the http_console_ports @@ -96,69 +106,66 @@ def console_port_iterator(): index = 0 while index < len(global_config["http_console_ports"]): port = global_config["http_console_ports"][index] - #print "ports -> ", port + #print("ports -> ", port) if type(port) is int: yield port else: #this is dictionary with from to keys port2 = port["from"] - #print "ports -> ", port, port2 + #print("ports -> ", port, port2) while port2 <= port["to"]: - print "ports -> ", port, port2 + #print("ports -> ", port, port2) yield port2 port2 += 1 index += 1 def usage(): - print "Usage: ", sys.argv[0], "[options]" - print " -v|--version: prints current version" - print " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)" - print " -h|--help: shows this help" - print " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)" - print " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)" - print " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file" + print("Usage: ", sys.argv[0], "[options]") + print( " -v|--version: prints current version") + print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)") + print( " -h|--help: shows this help") + print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)") + print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)") + print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file") return if __name__=="__main__": + #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" + streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s" + logging.basicConfig(format=streamformat, level= logging.DEBUG) + logger.setLevel(logging.DEBUG) # Read parameters and configuration file try: opts, args = getopt.getopt(sys.argv[1:], "hvc:V:p:P:", ["config", "help", "version", "port", "vnf-repository", "adminport"]) - except getopt.GetoptError as err: - # print help information and exit: - print "Error:", err # will print something like "option -a not recognized" - usage() - sys.exit(2) - port=None - port_admin = None - config_file = 'openmanod.cfg' - vnf_repository = None + port=None + port_admin = None + config_file = 'openmanod.cfg' + vnf_repository = None + + for o, a in opts: + if o in ("-v", "--version"): + print "openmanod version", __version__, version_date + print "(c) Copyright Telefonica" + sys.exit() + elif o in ("-h", "--help"): + usage() + sys.exit() + elif o in ("-V", "--vnf-repository"): + vnf_repository = a + elif o in ("-c", "--config"): + config_file = a + elif o in ("-p", "--port"): + port = a + elif o in ("-P", "--adminport"): + port_admin = a + else: + assert False, "Unhandled option" - for o, a in opts: - if o in ("-v", "--version"): - print "openmanod version", __version__, version_date - print "(c) Copyright Telefonica" - sys.exit() - elif o in ("-h", "--help"): - usage() - sys.exit() - elif o in ("-V", "--vnf-repository"): - vnf_repository = a - elif o in ("-c", "--config"): - config_file = a - elif o in ("-p", "--port"): - port = a - elif o in ("-P", "--adminport"): - port_admin = a - else: - assert False, "Unhandled option" - - try: - r, global_config = load_configuration(config_file) + global_config = load_configuration(config_file) #print global_config - if not r: - print global_config - exit(-1) + logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug"))) + logger.setLevel(getattr(logging, global_config['log_level'])) # Override parameters obtained by command line if port is not None: global_config['http_port'] = port if port_admin is not None: global_config['http_admin_port'] = port_admin @@ -166,16 +173,16 @@ if __name__=="__main__": global_config['vnf_repository'] = vnf_repository else: if not 'vnf_repository' in global_config: - print os.getcwd() + logger.error( os.getcwd() ) global_config['vnf_repository'] = os.getcwd()+'/vnfrepo' #print global_config if not os.path.exists(global_config['vnf_repository']): - print "Creating folder vnf_repository folder: '%s'." % global_config['vnf_repository'] + logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository']) try: os.makedirs(global_config['vnf_repository']) - except Exception,e: - print "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s" %(e.args[1], config_file) + except Exception as e: + logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file) exit(-1) global_config["console_port_iterator"] = console_port_iterator @@ -184,14 +191,14 @@ if __name__=="__main__": # Initialize DB connection mydb = nfvo_db.nfvo_db(); if mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name']) == -1: - print "Error connecting to database", global_config['db_name'], "at", global_config['db_user'], "@", global_config['db_host'] + logger.error("Error connecting to database %s at %s@%s", global_config['db_name'], global_config['db_user'], global_config['db_host']) exit(-1) r = mydb.get_db_version() if r[0]<0: - print "Error DATABASE is not a MANO one or it is a '0.0' version. Try to upgrade to version '%s' with './database_utils/migrate_mano_db.sh'" % database_version + logger.error("Error DATABASE is not a MANO one or it is a '0.0' version. Try to upgrade to version '%s' with './database_utils/migrate_mano_db.sh'", database_version) exit(-1) elif r[1]!=database_version: - print "Error DATABASE wrong version '%s'. Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'" % (r[1], database_version) + logger.error("Error DATABASE wrong version '%s'. Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'", r[1], database_version) exit(-1) nfvo.global_config=global_config @@ -203,9 +210,9 @@ if __name__=="__main__": httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port']) httpthreadadmin.start() time.sleep(1) - print 'Waiting for http clients' - print 'openmanod ready' - print '====================' + logger.info('Waiting for http clients') + print('openmanod ready') + print('====================') time.sleep(20) sys.stdout.flush() @@ -220,7 +227,12 @@ if __name__=="__main__": thread.terminate = True except (KeyboardInterrupt, SystemExit): - print 'Exiting openmanod' - exit() - + logger.info('Exiting openmanod') + except getopt.GetoptError as e: + logger.error("Error:", e) # will print something like "option -a not recognized" + #usage() + exit(-1) + except LoadConfigurationException as e: + logger.error("Error:", e) + exit(-1) diff --git a/utils.py b/utils.py index 81027739..a5a7858e 100755 --- a/utils.py +++ b/utils.py @@ -167,9 +167,15 @@ def convert_str2boolean(data, items): def check_valid_uuid(uuid): id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"} + id_schema2 = {"type" : "string", "pattern": "^[a-fA-F0-9]{32}$"} try: js_v(uuid, id_schema) return True except js_e.ValidationError: - return False + try: + js_v(uuid, id_schema2) + return True + except js_e.ValidationError: + return False + return False diff --git a/vimconn.py b/vimconn.py index 7ae33592..b06f04ea 100644 --- a/vimconn.py +++ b/vimconn.py @@ -28,6 +28,8 @@ vimconn implement an Abstract class for the vim connector plugins __author__="Alfonso Tierno" __date__ ="$16-oct-2015 11:09:29$" +import logging + #Error variables HTTP_Bad_Request = 400 HTTP_Unauthorized = 401 @@ -35,18 +37,52 @@ HTTP_Not_Found = 404 HTTP_Method_Not_Allowed = 405 HTTP_Request_Timeout = 408 HTTP_Conflict = 409 +HTTP_Not_Implemented = 501 HTTP_Service_Unavailable = 503 HTTP_Internal_Server_Error = 500 -class vimconnectorException(Exception): - pass +class vimconnException(Exception): + '''Common and base class Exception for all vimconnector exceptions''' + def __init__(self, message, http_code=HTTP_Bad_Request): + Exception.__init__(self, message) + self.http_code = http_code + +class vimconnConnectionException(vimconnException): + '''Connectivity error with the VIM''' + def __init__(self, message, http_code=HTTP_Service_Unavailable): + vimconnException.__init__(self, message, http_code) + +class vimconnUnexpectedResponse(vimconnException): + '''Get an wrong response from VIM''' + def __init__(self, message, http_code=HTTP_Service_Unavailable): + vimconnException.__init__(self, message, http_code) + +class vimconnAuthException(vimconnException): + '''Invalid credentials or authorization to perform this action over the VIM''' + def __init__(self, message, http_code=HTTP_Unauthorized): + vimconnException.__init__(self, message, http_code) + +class vimconnNotFoundException(vimconnException): + '''The item is not found at VIM''' + def __init__(self, message, http_code=HTTP_Not_Found): + vimconnException.__init__(self, message, http_code) + +class vimconnConflictException(vimconnException): + '''There is a conflict, e.g. more item found than one''' + def __init__(self, message, http_code=HTTP_Conflict): + vimconnException.__init__(self, message, http_code) + +class vimconnNotImplemented(vimconnException): + '''The method is not implemented by the connected''' + def __init__(self, message, http_code=HTTP_Not_Implemented): + vimconnException.__init__(self, message, http_code) class vimconnector(): '''Abstract base class for all the VIM connector plugins - These plugins must implement a vimconnector class deribed from this + These plugins must implement a vimconnector class derived from this and all these methods ''' - def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,debug=True,config={}): + def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level="ERROR", config={}): self.id = uuid self.name = name self.url = url @@ -56,7 +92,10 @@ class vimconnector(): self.user = user self.passwd = passwd self.config = config - self.debug = debug + self.logger = logging.getLogger('mano.vim') + self.logger.setLevel( getattr(logging, log_level) ) + if not self.url_admin: #try to use normal url + self.url_admin = self.url def __getitem__(self,index): if index=='tenant_id': @@ -99,41 +138,34 @@ class vimconnector(): self.url_admin = value else: raise KeyError("Invalid key '%s'" %str(index)) - - def new_host(self, host_data): - '''Adds a new host to VIM''' - '''Returns status code of the VIM response''' - raise NotImplementedError( "Should have implemented this" ) - - def new_external_port(self, port_data): - '''Adds a external port to VIM''' - '''Returns the port identifier''' - raise NotImplementedError( "Should have implemented this" ) - - def new_external_network(self,net_name,net_type): - '''Adds a external network to VIM (shared)''' - '''Returns the network identifier''' - raise NotImplementedError( "Should have implemented this" ) - - def connect_port_network(self, port_id, network_id, admin=False): - '''Connects a external port to a network''' - '''Returns status code of the VIM response''' - raise NotImplementedError( "Should have implemented this" ) def new_tenant(self,tenant_name,tenant_description): - '''Adds a new tenant to VIM''' - '''Returns the tenant identifier''' - raise NotImplementedError( "Should have implemented this" ) + '''Adds a new tenant to VIM with this name and description, + returns the tenant identifier''' + raise vimconnNotImplemented( "Should have implemented this" ) def delete_tenant(self,tenant_id,): '''Delete a tenant from VIM''' '''Returns the tenant identifier''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) - def new_tenant_network(self,net_name,net_type): - '''Adds a tenant network to VIM''' - '''Returns the network identifier''' - raise NotImplementedError( "Should have implemented this" ) + def get_tenant_list(self, filter_dict={}): + '''Obtain tenants of VIM + filter_dict can contain the following keys: + name: filter by tenant name + id: filter by tenant uuid/id + + Returns the tenant list of dictionaries: + [{'name':', 'id':', ...}, ...] + ''' + raise vimconnNotImplemented( "Should have implemented this" ) + + def new_network(self,net_name, net_type, shared=False): + '''Adds a tenant network to VIM + net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised + shared is a boolean + Returns the network identifier''' + raise vimconnNotImplemented( "Should have implemented this" ) def get_network_list(self, filter_dict={}): '''Obtain tenant networks of VIM @@ -144,62 +176,100 @@ class vimconnector(): tenant_id: tenant admin_state_up: boolean status: 'ACTIVE' - Returns the network list of dictionaries + Returns the network list of dictionaries: + [{}, ...] + List can be empty ''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) - def get_tenant_network(self, net_id, tenant_id=None): - '''Obtain tenant networks of VIM''' - '''Returns the network information from a network id''' - raise NotImplementedError( "Should have implemented this" ) + def get_network(self, net_id): + '''Obtain network details of net_id VIM network' + Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]''' + raise vimconnNotImplemented( "Should have implemented this" ) - def delete_tenant_network(self, net_id): - '''Deletes a tenant network from VIM''' - '''Returns the network identifier''' - raise NotImplementedError( "Should have implemented this" ) + def delete_network(self, net_id): + '''Deletes a tenant network from VIM, provide the network id. + Returns the network identifier or raise an exception''' + raise vimconnNotImplemented( "Should have implemented this" ) - def refresh_tenant_network(self, net_id): - '''Refreshes the status of the tenant network''' - '''Returns: 0 if no error, - <0 if error''' - raise NotImplementedError( "Should have implemented this" ) + def refresh_nets_status(self, net_list): + '''Get the status of the networks + Params: the list of network identifiers + Returns a dictionary with: + 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, ...) + # OTHER (Vim reported other status not understood) + # ERROR (VIM indicates an ERROR status) + # ACTIVE, INACTIVE, DOWN (admin down), + # BUILD (on building process) + # + 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) - def get_tenant_flavor(self, flavor_id): + ''' + raise vimconnNotImplemented( "Should have implemented this" ) + + def get_flavor(self, flavor_id): '''Obtain flavor details from the VIM - Returns the flavor dict details + Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete ''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) - def new_tenant_flavor(self, flavor_data): - '''Adds a tenant flavor to VIM''' - '''Returns the flavor identifier''' - raise NotImplementedError( "Should have implemented this" ) + def new_flavor(self, flavor_data): + '''Adds a tenant flavor to VIM + flavor_data contains a dictionary with information, keys: + name: flavor name + ram: memory (cloud type) in MBytes + vpcus: cpus (cloud type) + extended: EPA parameters + - numas: #items requested in same NUMA + memory: number of 1G huge pages memory + paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads + interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa + - name: interface name + dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC + bandwidth: X Gbps; requested guarantee bandwidth + vpci: requested virtual PCI address + disk: disk size + is_public: + + + + #TODO to concrete + Returns the flavor identifier''' + raise vimconnNotImplemented( "Should have implemented this" ) - def delete_tenant_flavor(self,flavor_id): - '''Deletes a tenant flavor from VIM''' - '''Returns the HTTP response code and a message indicating details of the success or fail''' - raise NotImplementedError( "Should have implemented this" ) + def delete_flavor(self, flavor_id): + '''Deletes a tenant flavor from VIM identify by its id + Returns the used id or raise an exception''' + raise vimconnNotImplemented( "Should have implemented this" ) - def new_tenant_image(self,image_dict): + def new_image(self,image_dict): ''' Adds a tenant image to VIM Returns: 200, image-id if the image is created <0, message if there is an error ''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) - def delete_tenant_image(self, image_id): + def delete_image(self, image_id): '''Deletes a tenant image from VIM''' '''Returns the HTTP response code and a message indicating details of the success or fail''' - raise NotImplementedError( "Should have implemented this" ) - - def new_tenant_vminstancefromJSON(self, vm_data): - '''Adds a VM instance to VIM''' - '''Returns the instance identifier''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) - def new_tenant_vminstance(self,name,description,start,image_id,flavor_id,net_list): + def get_image_id_from_path(self, path): + '''Get the image id from image path in the VIM database''' + '''Returns: + 0,"Image not found" if there are no images with that path + 1,image-id if there is one image with that path + <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.) + ''' + raise vimconnNotImplemented( "Should have implemented this" ) + + def new_vminstance(self,name,description,start,image_id,flavor_id,net_list): '''Adds a VM instance to VIM Params: start: indicates if VM must start or boot in pause mode. Ignored @@ -217,60 +287,107 @@ class vimconnector(): Returns >=0, the instance identifier <0, error_text ''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) - def get_tenant_vminstance(self,vm_id): + def get_vminstance(self,vm_id): '''Returns the VM instance information from VIM''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) - def delete_tenant_vminstance(self, vm_id): + def delete_vminstance(self, vm_id): '''Removes a VM instance from VIM''' '''Returns the instance identifier''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) - def refresh_tenant_vms_and_nets(self, vmDict, netDict): - '''Refreshes the status of the dictionaries of VM instances and nets passed as arguments. It modifies the dictionaries''' - '''Returns: - - result: 0 if all elements could be refreshed (even if its status didn't change) - n>0, the number of elements that couldn't be refreshed, - <0 if error (foreseen) - - error_msg: text with reference to possible errors + def refresh_vms_status(self, vm_list): + '''Get the status of the virtual machines and their interfaces/ports + Params: the list of VM identifiers + Returns a dictionary with: + 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, ...) + # OTHER (Vim reported other status not understood) + # ERROR (VIM indicates an ERROR status) + # 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 + 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) + mac_address: #Text format XX:XX:XX:XX:XX:XX + vim_net_id: #network id where this interface is connected + vim_interface_id: #interface/port VIM id + ip_address: #null, or text with IPv4, IPv6 address ''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) + + def action_vminstance(self, vm_id, action_dict): + '''Send and action over a VM instance from VIM + Returns the vm_id if the action was successfully sent to the VIM''' + raise vimconnNotImplemented( "Should have implemented this" ) - def action_tenant_vminstance(self, vm_id, action_dict): - '''Send and action over a VM instance from VIM''' - '''Returns the status''' - raise NotImplementedError( "Should have implemented this" ) + def get_vminstance_console(self,vm_id, console_type="vnc"): + ''' + Get a console for the virtual machine + Params: + vm_id: uuid of the VM + console_type, can be: + "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 + ''' + raise vimconnNotImplemented( "Should have implemented this" ) +#NOT USED METHODS in current version + def host_vim2gui(self, host, server_dict): '''Transform host dictionary from VIM format to GUI format, and append to the server_dict ''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) def get_hosts_info(self): '''Get the information of deployed hosts Returns the hosts content''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) def get_hosts(self, vim_tenant): '''Get the hosts and deployed instances Returns the hosts content''' - raise NotImplementedError( "Should have implemented this" ) + raise vimconnNotImplemented( "Should have implemented this" ) def get_processor_rankings(self): '''Get the processor rankings in the VIM database''' - raise NotImplementedError( "Should have implemented this" ) + 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: - 0,"Image not found" if there are no images with that path - 1,image-id if there is one image with that path - <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.) - ''' - raise NotImplementedError( "Should have implemented this" ) - + def new_host(self, host_data): + '''Adds a new host to VIM''' + '''Returns status code of the VIM response''' + raise vimconnNotImplemented( "Should have implemented this" ) + + def new_external_port(self, port_data): + '''Adds a external port to VIM''' + '''Returns the port identifier''' + raise vimconnNotImplemented( "Should have implemented this" ) + def new_external_network(self,net_name,net_type): + '''Adds a external network to VIM (shared)''' + '''Returns the network identifier''' + raise vimconnNotImplemented( "Should have implemented this" ) + + def connect_port_network(self, port_id, network_id, admin=False): + '''Connects a external port to a network''' + '''Returns status code of the VIM response''' + raise vimconnNotImplemented( "Should have implemented this" ) + + def new_vminstancefromJSON(self, vm_data): + '''Adds a VM instance to VIM''' + '''Returns the instance identifier''' + raise vimconnNotImplemented( "Should have implemented this" ) diff --git a/vimconn_openstack.py b/vimconn_openstack.py index 242acb75..76e7e4c5 100644 --- a/vimconn_openstack.py +++ b/vimconn_openstack.py @@ -30,6 +30,7 @@ __date__ ="$22-jun-2014 11:19:29$" import vimconn import json import yaml +import logging from novaclient import client as nClient, exceptions as nvExceptions import keystoneclient.v2_0.client as ksClient @@ -54,12 +55,12 @@ netStatus2manoFormat={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE', } class vimconnector(vimconn.vimconnector): - def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, debug=True, config={}): + def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level="DEBUG", config={}): '''using common constructor parameters. In this case 'url' is the keystone authorization url, 'url_admin' is not use ''' - vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, debug, config) + vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config) self.k_creds={} self.n_creds={} @@ -80,6 +81,7 @@ class vimconnector(vimconn.vimconnector): self.k_creds['password'] = passwd self.n_creds['api_key'] = passwd self.reload_client = True + self.logger = logging.getLogger('mano.vim.openstack') def __setitem__(self,index, value): '''Set individuals parameters @@ -148,109 +150,7 @@ class vimconnector(vimconn.vimconnector): self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL') self.neutron = neClient.Client('2.0', endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds) self.reload_client = False - - def new_external_port(self, port_data): - #TODO openstack if needed - '''Adds a external port to VIM''' - '''Returns the port identifier''' - return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented" - - def connect_port_network(self, port_id, network_id, admin=False): - #TODO openstack if needed - '''Connects a external port to a network''' - '''Returns status code of the VIM response''' - return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented" - - def new_user(self, user_name, user_passwd, tenant_id=None): - '''Adds a new user to openstack VIM''' - '''Returns the user identifier''' - if self.debug: - print "osconnector: Adding a new user to VIM" - try: - self._reload_connection() - user=self.keystone.users.create(user_name, user_passwd, tenant_id=tenant_id) - #self.keystone.tenants.add_user(self.k_creds["username"], #role) - return 1, user.id - except ksExceptions.ConnectionError as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except ksExceptions.ClientException as e: #TODO remove - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "new_tenant " + error_text - return error_value, error_text - - def delete_user(self, user_id): - '''Delete a user from openstack VIM''' - '''Returns the user identifier''' - if self.debug: - print "osconnector: Deleting a user from VIM" - try: - self._reload_connection() - self.keystone.users.delete(user_id) - return 1, user_id - except ksExceptions.ConnectionError as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except ksExceptions.NotFound as e: - error_value=-vimconn.HTTP_Not_Found - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except ksExceptions.ClientException as e: #TODO remove - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "delete_tenant " + error_text - return error_value, error_text - def new_tenant(self,tenant_name,tenant_description): - '''Adds a new tenant to openstack VIM''' - '''Returns the tenant identifier''' - if self.debug: - print "osconnector: Adding a new tenant to VIM" - try: - self._reload_connection() - tenant=self.keystone.tenants.create(tenant_name, tenant_description) - #self.keystone.tenants.add_user(self.k_creds["username"], #role) - return 1, tenant.id - except ksExceptions.ConnectionError as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except ksExceptions.ClientException as e: #TODO remove - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "new_tenant " + error_text - return error_value, error_text - - def delete_tenant(self,tenant_id): - '''Delete a tenant from openstack VIM''' - '''Returns the tenant identifier''' - if self.debug: - print "osconnector: Deleting a tenant from VIM" - try: - self._reload_connection() - self.keystone.tenants.delete(tenant_id) - #self.keystone.tenants.add_user(self.k_creds["username"], #role) - return 1, tenant_id - except ksExceptions.ConnectionError as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except ksExceptions.ClientException as e: #TODO remove - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "delete_tenant " + error_text - return error_value, error_text - def __net_os2mano(self, net_list_dict): '''Transform the net openstack format to mano format net_list_dict can be a list of dict or a single dict''' @@ -265,24 +165,78 @@ class vimconnector(vimconn.vimconnector): net['type']='data' else: net['type']='bridge' + + + + 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, + neClient.exceptions.ConnectionFailed)): + raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception)) + elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException, + neExceptions.NeutronException, nvExceptions.BadRequest)): + raise vimconn.vimconnUnexpectedResponse(type(exception).__name__ + ": " + str(exception)) + elif isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound)): + raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + str(exception)) + elif isinstance(exception, nvExceptions.Conflict): + raise vimconn.vimconnConflictException(type(exception).__name__ + ": " + str(exception)) + else: # () + raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception)) + + def get_tenant_list(self, filter_dict={}): + '''Obtain tenants of VIM + filter_dict can contain the following keys: + name: filter by tenant name + id: filter by tenant uuid/id + + Returns the tenant list of dictionaries: [{'name':', 'id':', ...}, ...] + ''' + self.logger.debug("Getting tenant from VIM filter: '%s'", str(filter_dict)) + try: + self._reload_connection() + tenant_class_list=self.keystone.tenants.findall(**filter_dict) + tenant_list=[] + for tenant in tenant_class_list: + tenant_list.append(tenant.to_dict()) + return tenant_list + except (ksExceptions.ConnectionError, ksExceptions.ClientException) as e: + self._format_exception(e) + + def new_tenant(self, tenant_name, tenant_description): + '''Adds a new tenant to openstack VIM. Returns the tenant identifier''' + self.logger.debug("Adding a new tenant name: %s", tenant_name) + try: + self._reload_connection() + tenant=self.keystone.tenants.create(tenant_name, tenant_description) + return tenant.id + except (ksExceptions.ConnectionError, ksExceptions.ClientException) as e: + self._format_exception(e) + + def delete_tenant(self, tenant_id): + '''Delete a tenant from openstack VIM. Returns the old tenant identifier''' + self.logger.debug("Deleting tenant %s from VIM", tenant_id) + try: + self._reload_connection() + self.keystone.tenants.delete(tenant_id) + return tenant_id + except (ksExceptions.ConnectionError, ksExceptions.ClientException) as e: + self._format_exception(e) - def new_tenant_network(self,net_name,net_type,public=False,cidr=None,vlan=None): - '''Adds a tenant network to VIM''' - '''Returns the network identifier''' - if self.debug: - print "osconnector: Adding a new tenant network to VIM (tenant: " + str(self.tenant_name) + ", type: " + net_type + "): "+ net_name + def new_network(self,net_name,net_type, shared=False, cidr=None, vlan=None): + '''Adds a tenant network to VIM. Returns the network identifier''' + self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type) try: 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: - return -vimconn.HTTP_Bad_Request, "You must provide a 'dataplane_physical_net' at config value before creating sriov network " - + 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 - network_dict["shared"]=public + network_dict["shared"]=shared new_net=self.neutron.create_network({'network':network_dict}) #print new_net #create fake subnetwork @@ -294,18 +248,9 @@ class vimconnector(vimconn.vimconnector): "cidr": cidr } self.neutron.create_subnet({"subnet": subnet} ) - return 1, new_net["network"]["id"] - except neExceptions.ConnectionFailed as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except (ksExceptions.ClientException, neExceptions.NeutronException) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "new_tenant_network " + error_text - return error_value, error_text + return new_net["network"]["id"] + except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e: + self._format_exception(e) def get_network_list(self, filter_dict={}): '''Obtain tenant networks of VIM @@ -318,60 +263,41 @@ class vimconnector(vimconn.vimconnector): status: 'ACTIVE' Returns the network list of dictionaries ''' - if self.debug: - print "osconnector.get_network_list(): Getting network from VIM (filter: " + str(filter_dict) + "): " + self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict)) try: self._reload_connection() net_dict=self.neutron.list_networks(**filter_dict) net_list=net_dict["networks"] self.__net_os2mano(net_list) - return 1, net_list - except neClient.exceptions.ConnectionFailed as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except (ksExceptions.ClientException, neExceptions.NeutronException) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "get_network_list " + error_text - return error_value, error_text + return net_list + except (neExceptions.ConnectionFailed, neClient.exceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e: + self._format_exception(e) - def get_tenant_network(self, net_id, tenant_id=None): - '''Obtain tenant networks of VIM''' - '''Returns the network information from a network id''' - if self.debug: - print "osconnector.get_tenant_network(): Getting tenant network %s from VIM" % net_id + def get_network(self, net_id): + '''Obtain details of network from VIM + Returns the network information from a network id''' + self.logger.debug(" Getting tenant network %s from VIM", net_id) filter_dict={"id": net_id} - if tenant_id: - filter_dict["tenant_id"] = tenant_id - r, net_list = self.get_network_list(filter_dict) - if r<0: - return r, net_list + net_list = self.get_network_list(filter_dict) if len(net_list)==0: - return -vimconn.HTTP_Not_Found, "Network '%s' not found" % net_id + raise vimconn.vimconnNotFoundException("Network '{}' not found".format(net_id)) elif len(net_list)>1: - return -vimconn.HTTP_Conflict, "Found more than one network with this criteria" + raise vimconn.vimconnConflictException("Found more than one network with this criteria") net = net_list[0] subnets=[] for subnet_id in net.get("subnets", () ): try: subnet = self.neutron.show_subnet(subnet_id) except Exception as e: - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - print "osconnector.get_tenant_network(): Error getting subnet %s %s" % (net_id, error_text) - subnet = {"id": subnet_id, "fault": error_text} + self.logger.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id, str(e))) + subnet = {"id": subnet_id, "fault": str(e)} subnets.append(subnet) net["subnets"] = subnets - return 1, net + return net - - def delete_tenant_network(self, net_id): - '''Deletes a tenant network from VIM''' - '''Returns the network identifier''' - if self.debug: - print "osconnector: Deleting a new tenant network from VIM tenant: " + str(self.tenant_name) + ", id: " + net_id + def delete_network(self, net_id): + '''Deletes a tenant network from VIM. Returns the old network identifier''' + self.logger.debug("Deleting network '%s' from VIM", net_id) try: self._reload_connection() #delete VM ports attached to this networks before the network @@ -380,55 +306,79 @@ class vimconnector(vimconn.vimconnector): try: self.neutron.delete_port(p["id"]) except Exception as e: - print "Error deleting port: " + type(e).__name__ + ": "+ str(e) + self.logger.error("Error deleting port %s: %s", p["id"], str(e)) self.neutron.delete_network(net_id) - return 1, net_id - except neClient.exceptions.ConnectionFailed as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except neExceptions.NetworkNotFoundClient as e: - error_value=-vimconn.HTTP_Not_Found - error_text= type(e).__name__ + ": "+ str(e.args[0]) - except (ksExceptions.ClientException, neExceptions.NeutronException) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "delete_tenant_network " + error_text - return error_value, error_text + return net_id + except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException, + neClient.exceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e: + self._format_exception(e) - def get_tenant_flavor(self, flavor_id): - '''Obtain flavor details from the VIM - Returns the flavor dict details - ''' - print "VIMConnector: Getting flavor from VIM" + def refresh_nets_status(self, net_list): + '''Get the status of the networks + Params: the list of network identifiers + Returns a dictionary with: + 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, ...) + # OTHER (Vim reported other status not understood) + # ERROR (VIM indicates an ERROR status) + # ACTIVE, INACTIVE, DOWN (admin down), + # BUILD (on building process) + # + 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) + + ''' + net_dict={} + for net_id in net_list: + net = {} + try: + net_vim = self.get_network(net_id) + if net_vim['status'] in netStatus2manoFormat: + net["status"] = netStatus2manoFormat[ net_vim['status'] ] + else: + net["status"] = "OTHER" + net["error_msg"] = "VIM status reported " + net_vim['status'] + + if net['status'] == "ACIVE" and not net_vim['admin_state_up']: + net['status'] = 'DOWN' + net['vim_info'] = yaml.safe_dump(net_vim) + if net_vim.get('fault'): #TODO + net['error_msg'] = str(net_vim['fault']) + except vimconn.vimconnNotFoundException as e: + self.logger.error("Exception getting net status: %s", str(e)) + net['status'] = "DELETED" + net['error_msg'] = str(e) + except vimconn.vimconnException as e: + self.logger.error("Exception getting net status: %s", str(e)) + net['status'] = "VIM_ERROR" + net['error_msg'] = str(e) + net_dict[net_id] = net + return net_dict + + def get_flavor(self, flavor_id): + '''Obtain flavor details from the VIM. Returns the flavor dict details''' + self.logger.debug("Getting flavor '%s'", flavor_id) try: self._reload_connection() flavor = self.nova.flavors.find(id=flavor_id) #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema) - return 1, {"flavor": flavor.to_dict()} - except nvExceptions.NotFound as e: - error_value=-vimconn.HTTP_Not_Found - error_text= "flavor instance %s not found" % flavor_id - except (ksExceptions.ClientException, nvExceptions.ClientException) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "get_tenant_flavor " + error_text - return error_value, error_text + return flavor.to_dict() + except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException) as e: + self._format_exception(e) - def new_tenant_flavor(self, flavor_dict, change_name_if_used=True): + 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 + if change_name_if_used is True, it will change name in case of conflict, because it is not supported name repetition Returns the flavor identifier ''' + self.logger.debug("Adding flavor '%s'", str(flavor_data)) retry=0 + max_retries=3 name_suffix = 0 - name=flavor_dict['name'] - while retry<2: + name=flavor_data['name'] + while retry0,id if ok; or <0,error_text if error + def delete_flavor(self,flavor_id): + '''Deletes a tenant flavor from openstack VIM. Returns the old flavor_id ''' - retry=0 - while retry<2: - retry+=1 - try: - self._reload_connection() - self.nova.flavors.delete(flavor_id) - return 1, flavor_id - except nvExceptions.NotFound as e: - error_value = -vimconn.HTTP_Not_Found - error_text = "flavor '%s' not found" % flavor_id - break - #except nvExceptions.BadRequest as e: - except (ksExceptions.ClientException, nvExceptions.ClientException) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - break - if self.debug: - print "delete_tenant_flavor " + error_text - #if reaching here is because an exception - return error_value, error_text + try: + self._reload_connection() + self.nova.flavors.delete(flavor_id) + return flavor_id + #except nvExceptions.BadRequest as e: + except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException) as e: + self._format_exception(e) - def new_tenant_image(self,image_dict): + def new_image(self,image_dict): ''' - Adds a tenant image to VIM - if change_name_if_used is True, it will change name in case of conflict - Returns: - >1, image-id if the image is created - <0, message if there is an error + Adds a tenant image to VIM. imge_dict is a dictionary with: + name: name + disk_format: qcow2, vhd, vmdk, raw (by default), ... + location: path or URI + public: "yes" or "no" + metadata: metadata of the image + Returns the image_id ''' - retry=0 #using version 1 of glance client glancev1 = gl1Client.Client('1',self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds - while retry<2: + retry=0 + max_retries=3 + while retry0,id if ok; or <0,error_text if error + def delete_image(self, image_id): + '''Deletes a tenant image from openstack VIM. Returns the old id ''' - retry=0 - while retry<2: - retry+=1 - try: - self._reload_connection() - self.nova.images.delete(image_id) - return 1, image_id - except nvExceptions.NotFound as e: - error_value = -vimconn.HTTP_Not_Found - error_text = "flavor '%s' not found" % image_id - break - #except nvExceptions.BadRequest as e: - except (ksExceptions.ClientException, nvExceptions.ClientException) as e: #TODO remove - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - break - if self.debug: - print "delete_tenant_image " + error_text - #if reaching here is because an exception - return error_value, error_text + try: + self._reload_connection() + self.nova.images.delete(image_id) + return image_id + except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError) as e: #TODO remove + self._format_exception(e) + + def get_image_id_from_path(self, path): + '''Get the image id from image path in the VIM database. Returns the image_id + ''' + try: + self._reload_connection() + images = self.nova.images.list() + for image in images: + if image.metadata.get("location")==path: + return image.id + raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path)) + except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError) as e: + self._format_exception(e) - def new_tenant_vminstance(self,name,description,start,image_id,flavor_id,net_list): + def new_vminstance(self,name,description,start,image_id,flavor_id,net_list): '''Adds a VM instance to VIM Params: start: indicates if VM must start or boot in pause mode. Ignored @@ -645,17 +559,14 @@ class vimconnector(vimconn.vimconnector): type: 'virtual', 'PF', 'VF', 'VFnotShared' vim_id: filled/added by this function #TODO ip, security groups - Returns >=0, the instance identifier - <0, error_text + Returns the instance identifier ''' - if self.debug: - print "osconnector: Creating VM into VIM" - print " image %s flavor %s nics=%s" %(image_id, flavor_id,net_list) + self.logger.debug("Creating VM image '%s' flavor '%s' nics='%s'",image_id, flavor_id,str(net_list)) try: metadata=[] net_list_vim=[] self._reload_connection() - metadata_vpci={} + metadata_vpci={} #For a specific neutron plugin for net in net_list: if not net.get("net_id"): #skip non connected iface continue @@ -664,7 +575,7 @@ class vimconnector(vimconn.vimconnector): if "vpci" in net: metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]] elif net["type"]=="PF": - print "new_tenant_vminstance: Warning, can not connect a passthrough interface " + self.logger.warn("new_vminstance: Warning, can not connect a passthrough interface ") #TODO insert this when openstack consider passthrough ports as openstack neutron ports else: #VF if "vpci" in net: @@ -692,8 +603,8 @@ class vimconnector(vimconn.vimconnector): if metadata_vpci: metadata = {"pci_assignement": json.dumps(metadata_vpci)} - print "name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s"\ - % (name, image_id, flavor_id, str(net_list_vim), description, str(metadata)) + self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s", + name, image_id, flavor_id, str(net_list_vim), description, str(metadata)) security_groups = self.config.get('security_groups') if type(security_groups) is str: @@ -705,7 +616,7 @@ class vimconnector(vimconn.vimconnector): ) #, description=description) - print "DONE :-)", server + #print "DONE :-)", server # #TODO server.add_floating_ip("10.95.87.209") # #To look for a free floating_ip @@ -718,44 +629,28 @@ class vimconnector(vimconn.vimconnector): # server.add_floating_ip(free_floating_ip) - return 1, server.id + 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, TypeError) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - except neClient.exceptions.ConnectionFailed as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "new_tenant_vminstance Exception",e, error_text - return error_value, error_text + except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError, + neClient.exceptions.ConnectionFailed) as e: + self._format_exception(e) + except TypeError as e: + raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request) - def get_tenant_vminstance(self,vm_id): + def get_vminstance(self,vm_id): '''Returns the VM instance information from VIM''' - if self.debug: - print "osconnector: Getting VM from VIM" + #self.logger.debug("Getting VM from VIM") try: self._reload_connection() server = self.nova.servers.find(id=vm_id) #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema) - return 1, {"server": server.to_dict()} - 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) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "get_tenant_vminstance " + error_text - return error_value, error_text - - def get_tenant_vminstance_console(self,vm_id, console_type="vnc"): + return server.to_dict() + except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound) as e: + self._format_exception(e) + + def get_vminstance_console(self,vm_id, console_type="vnc"): ''' Get a console for the virtual machine Params: @@ -763,11 +658,13 @@ class vimconnector(vimconn.vimconnector): console_type, can be: "novnc" (by default), "xvpvnc" for VNC types, "rdp-html5" for RDP types, "spice-html5" for SPICE types - Returns <0, text on error, for example not available - 1, URL/text with the console parameters + 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 ''' - if self.debug: - print "osconnector: Getting VM CONSOLE from VIM" + self.logger.debug("Getting VM CONSOLE from VIM") try: self._reload_connection() server = self.nova.servers.find(id=vm_id) @@ -780,7 +677,7 @@ class vimconnector(vimconn.vimconnector): elif console_type == "spice-html5": console_dict = server.get_spice_console(console_type) else: - return -vimconn.HTTP_Bad_Request, "console type '%s' not allowed" % console_type + raise vimconn.vimconnException("console type '{}' not allowed".format(console_type), http_code=vimconn.HTTP_Bad_Request) console_dict1 = console_dict.get("console") if console_dict1: @@ -798,31 +695,16 @@ class vimconnector(vimconn.vimconnector): "suffix": console_url[suffix_index+1:] } protocol_index += 2 - return 1, console_dict - return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM" + return console_dict + raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM") - #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema) - return 1, {"server": server.to_dict()} - 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, nvExceptions.BadRequest) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - #TODO insert exception vimconn.HTTP_Unauthorized - #if reaching here is because an exception - if self.debug: - print "get_tenant_vminstance " + error_text - return error_value, error_text + except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest) as e: + self._format_exception(e) - - def delete_tenant_vminstance(self, vm_id): - '''Removes a VM instance from VIM - Returns >0, the instance identifier - <0, error_text + def delete_vminstance(self, vm_id): + '''Removes a VM instance from VIM. Returns the old identifier ''' - if self.debug: - print "osconnector: Getting VM from VIM" + #print "osconnector: Getting VM from VIM" try: self._reload_connection() #delete VM ports attached to this networks before the virtual machine @@ -831,125 +713,89 @@ class vimconnector(vimconn.vimconnector): try: self.neutron.delete_port(p["id"]) except Exception as e: - print "Error deleting port: " + type(e).__name__ + ": "+ str(e) + self.logger.error("Error deleting port: " + type(e).__name__ + ": "+ str(e)) self.nova.servers.delete(vm_id) - return 1, vm_id - except nvExceptions.NotFound as e: - error_value=-vimconn.HTTP_Not_Found - error_text= (str(e) if len(e.args)==0 else str(e.args[0])) - except (ksExceptions.ClientException, nvExceptions.ClientException) as e: - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) + return vm_id + except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException) as e: + self._format_exception(e) #TODO insert exception vimconn.HTTP_Unauthorized #if reaching here is because an exception - if self.debug: - print "get_tenant_vminstance " + error_text - return error_value, error_text - def refresh_tenant_vms_and_nets(self, vmDict, netDict): - '''Refreshes the status of the dictionaries of VM instances and nets passed as arguments. It modifies the dictionaries - Returns: - - result: 0 if all elements could be refreshed (even if its status didn't change) - n>0, the number of elements that couldn't be refreshed, - <0 if error (foreseen) - - error_msg: text with reference to possible errors + def refresh_vms_status(self, vm_list): + '''Get the status of the virtual machines and their interfaces/ports + Params: the list of VM identifiers + Returns a dictionary with: + 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, ...) + # OTHER (Vim reported other status not understood) + # ERROR (VIM indicates an ERROR status) + # 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 + 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) + mac_address: #Text format XX:XX:XX:XX:XX:XX + vim_net_id: #network id where this interface is connected + vim_interface_id: #interface/port VIM id + ip_address: #null, or text with IPv4, IPv6 address ''' - #vms_refreshed = [] - #nets_refreshed = [] - vms_unrefreshed = [] - nets_unrefreshed = [] - if self.debug: - print "osconnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM" - for vm_id in vmDict: - vmDict[vm_id] = {'error_msg':None, 'vim_info':None} - r,c = self.get_tenant_vminstance(vm_id) - if r<0: - print "osconnector refresh_tenant_vm. Error getting vm_id '%s' status: %s" % (vm_id, c) - if r==-vimconn.HTTP_Not_Found: - vmDict[vm_id]['status'] = "DELETED" - else: - vmDict[vm_id]['status'] = "VIM_ERROR" - vmDict[vm_id]['error_msg'] = c - vms_unrefreshed.append(vm_id) - else: - try: - vmDict[vm_id]['status'] = vmStatus2manoFormat[ c['server']['status'] ] - vmDict[vm_id]['vim_info'] = yaml.safe_dump(c['server']) - vmDict[vm_id]["interfaces"] = [] - if c['server'].get('fault'): - vmDict[vm_id]['error_msg'] = str(c['server']['fault']) - #get interfaces - try: - self._reload_connection() - port_dict=self.neutron.list_ports(device_id=vm_id) - for port in port_dict["ports"]: - interface={} - interface['vim_info'] = yaml.safe_dump(port) - interface["mac_address"] = port.get("mac_address") - interface["vim_net_id"] = port["network_id"] - interface["vim_interface_id"] = port["id"] - ips=[] - #look for floating ip address - floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"]) - if floating_ip_dict.get("floatingips"): - ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") ) - - for subnet in port["fixed_ips"]: - ips.append(subnet["ip_address"]) - interface["ip_address"] = ";".join(ips) - vmDict[vm_id]["interfaces"].append(interface) - except Exception as e: - print type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - - - #error message at server.fault["message"] - except KeyError as e: - print "osconnector refresh_tenant_elements KeyError %s getting vm_id '%s' status %s" % (str(e), vm_id, c['server']['status']) - vmDict[vm_id]['status'] = "VIM_ERROR" - vmDict[vm_id]['error_msg'] = str(e) - vms_unrefreshed.append(vm_id) - - #print "VMs refreshed: %s" % str(vms_refreshed) - for net_id in netDict: - netDict[net_id]={'error_msg':None, 'vim_info':None} - r,c = self.get_tenant_network(net_id) - if r<0: - print "osconnector refresh_tenant_network. Error getting net_id '%s' status: %s" % (net_id, c) - if r==-vimconn.HTTP_Not_Found: - netDict[net_id]['status'] = "DELETED" #TODO check exit status + vm_dict={} + self.logger.debug("refresh_vms status: Getting tenant VM instance information from VIM") + for vm_id in vm_list: + vm={} + try: + vm_vim = self.get_vminstance(vm_id) + if vm_vim['status'] in vmStatus2manoFormat: + vm['status'] = vmStatus2manoFormat[ vm_vim['status'] ] else: - netDict[vm_id]['status'] = "VIM_ERROR" - netDict[vm_id]['error_msg'] = c - nets_unrefreshed.append(net_id) - else: + vm['status'] = "OTHER" + vm['error_msg'] = "VIM status reported " + vm_vim['status'] + vm['vim_info'] = yaml.safe_dump(vm_vim) + vm["interfaces"] = [] + if vm_vim.get('fault'): + vm['error_msg'] = str(vm_vim['fault']) + #get interfaces try: - netDict[net_id]['status'] = netStatus2manoFormat[ c['status'] ] - if c['status'] == "ACIVE" and not c['admin_state_up']: - netDict[net_id]['status'] = 'DOWN' - netDict[net_id]['vim_info'] = yaml.safe_dump(c) - if c.get('fault'): #TODO - netDict[net_id]['error_msg'] = str(c['fault']) - except KeyError as e: - print "osconnector refresh_tenant_elements KeyError %s getting vm_id '%s' status %s" % (str(e), vm_id, c['network']['status']) - netDict[net_id]['status'] = "VIM_ERROR" - netDict[net_id]['error_msg'] = str(e) - nets_unrefreshed.append(net_id) - - #print "Nets refreshed: %s" % str(nets_refreshed) - - error_msg="" - if len(vms_unrefreshed)+len(nets_unrefreshed)>0: - error_msg += "VMs unrefreshed: " + str(vms_unrefreshed) + "; nets unrefreshed: " + str(nets_unrefreshed) - print error_msg + self._reload_connection() + port_dict=self.neutron.list_ports(device_id=vm_id) + for port in port_dict["ports"]: + interface={} + interface['vim_info'] = yaml.safe_dump(port) + interface["mac_address"] = port.get("mac_address") + interface["vim_net_id"] = port["network_id"] + interface["vim_interface_id"] = port["id"] + ips=[] + #look for floating ip address + floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"]) + if floating_ip_dict.get("floatingips"): + ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") ) - #return len(vms_unrefreshed)+len(nets_unrefreshed), error_msg, vms_refreshed, nets_refreshed - return len(vms_unrefreshed)+len(nets_unrefreshed), error_msg + for subnet in port["fixed_ips"]: + ips.append(subnet["ip_address"]) + interface["ip_address"] = ";".join(ips) + vm["interfaces"].append(interface) + except Exception as e: + self.logger.error("Error getting vm interface information " + type(e).__name__ + ": "+ str(e)) + except vimconn.vimconnNotFoundException as e: + self.logger.error("Exception getting vm status: %s", str(e)) + vm['status'] = "DELETED" + vm['error_msg'] = str(e) + except vimconn.vimconnException as e: + self.logger.error("Exception getting vm status: %s", str(e)) + vm['status'] = "VIM_ERROR" + vm['error_msg'] = str(e) + vm_dict[vm_id] = vm + return vm_dict - def action_tenant_vminstance(self, vm_id, action_dict): + def action_vminstance(self, vm_id, action_dict): '''Send and action over a VM instance from VIM - Returns the status''' - if self.debug: - print "osconnector: Action over VM instance from VIM " + vm_id + Returns the vm_id if the action was successfully sent to the VIM''' + self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict)) try: self._reload_connection() server = self.nova.servers.find(id=vm_id) @@ -996,8 +842,8 @@ class vimconnector(vimconn.vimconnector): elif console_type == "spice-html5": console_dict = server.get_spice_console(console_type) else: - return -vimconn.HTTP_Bad_Request, "console type '%s' not allowed" % console_type - + raise vimconn.vimconnException("console type '{}' not allowed".format(console_type), + http_code=vimconn.HTTP_Bad_Request) try: console_url = console_dict["console"]["url"] #parse console_url @@ -1005,32 +851,80 @@ class vimconnector(vimconn.vimconnector): suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2 if protocol_index < 0 or port_index<0 or suffix_index<0: - print "action_tenant_vminstance, console: response", str(console_dict) - return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM" + raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict)) console_dict2={"protocol": console_url[0:protocol_index], "server": console_url[protocol_index+2 : port_index], "port": int(console_url[port_index+1 : suffix_index]), "suffix": console_url[suffix_index+1:] } - protocol_index += 2 - return 1, console_dict2 - except: - print "action_tenant_vminstance, console: response", str(console_dict) - return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM" + return console_dict2 + except Exception as e: + raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict)) - return 1, vm_id - except nvExceptions.NotFound as e: - error_value=-vimconn.HTTP_Not_Found - error_text= (str(e) if len(e.args)==0 else str(e.args[0])) - except (ksExceptions.ClientException, nvExceptions.ClientException) as e: + return vm_id + except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound) as e: + self._format_exception(e) + #TODO insert exception vimconn.HTTP_Unauthorized + +#NOT USED FUNCTIONS + + def new_external_port(self, port_data): + #TODO openstack if needed + '''Adds a external port to VIM''' + '''Returns the port identifier''' + return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented" + + def connect_port_network(self, port_id, network_id, admin=False): + #TODO openstack if needed + '''Connects a external port to a network''' + '''Returns status code of the VIM response''' + return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented" + + def new_user(self, user_name, user_passwd, tenant_id=None): + '''Adds a new user to openstack VIM''' + '''Returns the user identifier''' + self.logger.debug("osconnector: Adding a new user to VIM") + try: + self._reload_connection() + user=self.keystone.users.create(user_name, user_passwd, tenant_id=tenant_id) + #self.keystone.tenants.add_user(self.k_creds["username"], #role) + return user.id + except ksExceptions.ConnectionError as e: + error_value=-vimconn.HTTP_Bad_Request + error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) + except ksExceptions.ClientException as e: #TODO remove error_value=-vimconn.HTTP_Bad_Request error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) #TODO insert exception vimconn.HTTP_Unauthorized #if reaching here is because an exception if self.debug: - print "action_tenant_vminstance " + error_text + self.logger.debug("new_user " + error_text) return error_value, error_text - + + def delete_user(self, user_id): + '''Delete a user from openstack VIM''' + '''Returns the user identifier''' + if self.debug: + print "osconnector: Deleting a user from VIM" + try: + self._reload_connection() + self.keystone.users.delete(user_id) + return 1, user_id + except ksExceptions.ConnectionError as e: + error_value=-vimconn.HTTP_Bad_Request + error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) + except ksExceptions.NotFound as e: + error_value=-vimconn.HTTP_Not_Found + error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) + except ksExceptions.ClientException as e: #TODO remove + error_value=-vimconn.HTTP_Bad_Request + error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) + #TODO insert exception vimconn.HTTP_Unauthorized + #if reaching here is because an exception + if self.debug: + print "delete_tenant " + error_text + return error_value, error_text + def get_hosts_info(self): '''Get the information of deployed hosts Returns the hosts content''' @@ -1084,26 +978,4 @@ class vimconnector(vimconn.vimconnector): print "get_hosts " + error_text return error_value, error_text - def get_image_id_from_path(self, path): - '''Get the image id from image path in the VIM database''' - '''Returns: - 0,"Image not found" if there are no images with that path - 1,image-id if there is one image with that path - <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.) - ''' - try: - self._reload_connection() - images = self.nova.images.list() - for image in images: - if image.metadata.get("location")==path: - return 1, image.id - return 0, "image with location '%s' not found" % path - except (ksExceptions.ClientException, nvExceptions.ClientException) as e: #TODO remove - error_value=-vimconn.HTTP_Bad_Request - error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) - if self.debug: - print "get_image_id_from_path " + error_text - #if reaching here is because an exception - return error_value, error_text - diff --git a/vimconn_openvim.py b/vimconn_openvim.py index e253b4e4..a77c5f63 100644 --- a/vimconn_openvim.py +++ b/vimconn_openvim.py @@ -23,8 +23,6 @@ ''' vimconnector implements all the methods to interact with openvim using the openvim API. - -For interacting with Openstack refer to osconnector. ''' __author__="Alfonso Tierno, Gerardo Garcia" __date__ ="$26-aug-2014 11:09:29$" @@ -33,6 +31,7 @@ import vimconn import requests import json import yaml +import logging from openmano_schemas import id_schema, name_schema, nameshort_schema, description_schema, \ vlan1000_schema, integer0_schema from jsonschema import validate as js_v, exceptions as js_e @@ -249,6 +248,23 @@ new_flavor_response_schema = { "additionalProperties": False } +get_image_response_schema = { + "title":"openvim images response information schema", + "$schema": "http://json-schema.org/draft-04/schema#", + "type":"object", + "properties":{ + "image":{ + "type":"object", + "properties":{ + "id": id_schema, + "name": name_schema, + }, + "required": ["id", "name"], + } + }, + "required": ["flavor"], + "additionalProperties": False +} new_image_response_schema = { "title":"image response information schema", "$schema": "http://json-schema.org/draft-04/schema#", @@ -306,10 +322,11 @@ get_processor_rankings_response_schema = { } class vimconnector(vimconn.vimconnector): - def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,debug=True,config={}): - vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, debug, config) + def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,log_level="DEBUG",config={}): + vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config) self.tenant = None self.headers_req = {'content-type': 'application/json'} + self.logger = logging.getLogger('mano.vim.openvim') if tenant_id: self.tenant = tenant_id @@ -329,18 +346,23 @@ class vimconnector(vimconn.vimconnector): if self.tenant: return self.tenant - vim_response = requests.get(self.url+'/tenants?name='+ self.tenant_name, headers = self.headers_req) - if vim_response.status_code != 200: - raise vimconn.vimconnectorException ("_get_my_tenant response " + str(vim_response.status_code)) - tenant_list = vim_response.json()["tenants"] - if len(tenant_list) == 0: - raise vimconn.vimconnectorException ("No tenant found for name '%s'" % str(self.tenant_name)) - elif len(tenant_list) > 1: - raise vimconn.vimconnectorException ("More that one tenant found for name '%s'" % str(self.tenant_name)) - self.tenant = tenant_list[0]["id"] - return self.tenant + url = self.url+'/tenants?name='+ self.tenant_name + self.logger.info("Getting VIM tenant_id GET %s", url) + vim_response = requests.get(url, headers = self.headers_req) + self._check_http_request_response(vim_response) + try: + tenant_list = vim_response.json()["tenants"] + if len(tenant_list) == 0: + raise vimconn.vimconnNotFoundException("No tenant found for name '%s'" % str(self.tenant_name)) + elif len(tenant_list) > 1: + raise vimconn.vimconnConflictException ("More that one tenant found for name '%s'" % str(self.tenant_name)) + self.tenant = tenant_list[0]["id"] + return self.tenant + except Exception as e: + raise vimconn.vimconnUnexpectedResponse("Get VIM tenant {} '{}'".format(type(e).__name__, str(e))) def _format_jsonerror(self,http_response): + #DEPRECATED, to delete in the future try: data = http_response.json() return data["error"]["description"] @@ -348,6 +370,7 @@ class vimconnector(vimconn.vimconnector): return http_response.text def _format_in(self, http_response, schema): + #DEPRECATED, to delete in the future try: client_data = http_response.json() js_v(client_data, schema) @@ -374,245 +397,72 @@ class vimconnector(vimconn.vimconnector): if len(deleted) == 0: return None elif len(deleted) == 1: return deleted[0] else: return deleted - - def new_host(self, host_data): - '''Adds a new host to VIM''' - '''Returns status code of the VIM response''' - print "VIMConnector: Adding a new host" - payload_req = host_data - try: - vim_response = requests.post(self.url_admin+'/hosts', headers = self.headers_req, data=payload_req) - except requests.exceptions.RequestException, e: - print "new_host Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() - #print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_host_response_schema) - #print http_content - if res : - r = self._remove_extra_items(http_content, new_host_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - host_id = http_content['host']['id'] - #print "Host id: ",host_id - return vim_response.status_code,host_id - else: return -vimconn.HTTP_Bad_Request,http_content - else: - #print vim_response.text - jsonerror = self.__format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to add new host. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - - def new_external_port(self, port_data): - '''Adds a external port to VIM''' - '''Returns the port identifier''' - print "VIMConnector: Adding a new external port" - payload_req = port_data - try: - vim_response = requests.post(self.url_admin+'/ports', headers = self.headers_req, data=payload_req) - except requests.exceptions.RequestException, e: - print "new_external_port Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() - #print json.dumps(vim_response.json(), indent=4) - res, http_content = self.__format_in(vim_response, new_port_response_schema) - #print http_content - if res: - r = self._remove_extra_items(http_content, new_port_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - port_id = http_content['port']['id'] - print "Port id: ",port_id - return vim_response.status_code,port_id - else: return -vimconn.HTTP_Bad_Request,http_content - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to add new external port. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - - def new_external_network(self,net_name,net_type): - '''Adds a external network to VIM (shared)''' - '''Returns the network identifier''' - print "VIMConnector: Adding external shared network to VIM (type " + net_type + "): "+ net_name - - payload_req = '{"network":{"name": "' + net_name + '","shared":true,"type": "' + net_type + '"}}' - try: - vim_response = requests.post(self.url+'/networks', headers = self.headers_req, data=payload_req) - except requests.exceptions.RequestException, e: - print "new_external_network Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() - #print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_network_response_schema) - #print http_content - if res: - r = self._remove_extra_items(http_content, new_network_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - network_id = http_content['network']['id'] - print "Network id: ",network_id - return vim_response.status_code,network_id - else: return -vimconn.HTTP_Bad_Request,http_content - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to add new external network. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - def connect_port_network(self, port_id, network_id, admin=False): - '''Connects a external port to a network''' - '''Returns status code of the VIM response''' - print "VIMConnector: Connecting external port to network" - - payload_req = '{"port":{"network_id":"' + network_id + '"}}' - if admin: - if self.url_admin==None: - return -vimconn.HTTP_Unauthorized, "datacenter cannot contain admin URL" - url= self.url_admin - else: - url= self.url - try: - vim_response = requests.put(url +'/ports/'+port_id, headers = self.headers_req, data=payload_req) - except requests.exceptions.RequestException, e: - print "connect_port_network Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() - #print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_port_response_schema) - #print http_content - if res: - r = self._remove_extra_items(http_content, new_port_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - port_id = http_content['port']['id'] - print "Port id: ",port_id - return vim_response.status_code,port_id - else: return -vimconn.HTTP_Bad_Request,http_content + def _format_request_exception(self, request_exception): + '''Transform a request exception into a vimconn exception''' + if isinstance(request_exception, js_e.ValidationError): + raise vimconn.vimconnUnexpectedResponse("jsonschema exception '{}' at '{}'".format(request_exception.message, request_exception.path)) + elif isinstance(request_exception, requests.exceptions.HTTPError): + raise vimconn.vimconnUnexpectedResponse(type(request_exception).__name__ + ": " + str(request_exception)) else: - print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to connect external port to network. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror) - print text - return -vim_response.status_code,text - + raise vimconn.vimconnConnectionException(type(request_exception).__name__ + ": " + str(request_exception)) + + def _check_http_request_response(self, request_response): + '''Raise a vimconn exception if the response is not Ok''' + if request_response.status_code >= 200 and request_response.status_code < 300: + return + if request_response.status_code == vimconn.HTTP_Unauthorized: + raise vimconn.vimconnAuthException(request_response.text) + elif request_response.status_code == vimconn.HTTP_Not_Found: + raise vimconn.vimconnNotFoundException(request_response.text) + elif request_response.status_code == vimconn.HTTP_Conflict: + raise vimconn.vimconnConflictException(request_response.text) + else: + raise vimconn.vimconnUnexpectedResponse("VIM HTTP_response {}, {}".format(request_response.status_code, str(request_response.text))) + def new_tenant(self,tenant_name,tenant_description): - '''Adds a new tenant to VIM''' - '''Returns the tenant identifier''' - print "VIMConnector: Adding a new tenant to VIM" + '''Adds a new tenant to VIM with this name and description, returns the tenant identifier''' + #print "VIMConnector: Adding a new tenant to VIM" payload_dict = {"tenant": {"name":tenant_name,"description": tenant_description, "enabled": True}} payload_req = json.dumps(payload_dict) - #print payload_req - try: - vim_response = requests.post(self.url+'/tenants', headers = self.headers_req, data=payload_req) - except requests.exceptions.RequestException, e: - print "new_tenant Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - #print vim_response - if vim_response.status_code == 200: - #print vim_response.json() + url = self.url_admin+'/tenants' + self.logger.info("Adding a new tenant %s", url) + vim_response = requests.post(url, headers = self.headers_req, data=payload_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) #print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_tenant_response_schema) - #print http_content - if res: - r = self._remove_extra_items(http_content, new_tenant_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - tenant_id = http_content['tenant']['id'] - #print "Tenant id: ",tenant_id - return vim_response.status_code,tenant_id - else: return -vimconn.HTTP_Bad_Request,http_content - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to add new tenant. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - - def delete_tenant(self,tenant_id,): - '''Delete a tenant from VIM''' - '''Returns the tenant identifier''' - print "VIMConnector: Deleting a tenant from VIM" - try: - vim_response = requests.delete(self.url+'/tenants/'+tenant_id, headers = self.headers_req) - except requests.exceptions.RequestException, e: - print "delete_tenant Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - #print vim_response - if vim_response.status_code == 200: - return vim_response.status_code,tenant_id - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to delete tenant. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - - def new_tenant_network(self,net_name,net_type,public=False, **vim_specific): - '''Adds a tenant network to VIM''' - '''Returns the network identifier''' - try: - self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "vim_specific", vim_specific - if net_type=="bridge": - net_type="bridge_data" - print "VIMConnector: Adding a new tenant network to VIM (tenant: " + str(self.tenant) + ", type: " + net_type + "): "+ net_name + response = vim_response.json() + js_v(response, new_tenant_response_schema) + #r = self._remove_extra_items(response, new_tenant_response_schema) + #if r is not None: + # self.logger.warn("Warning: remove extra items %s", str(r)) + tenant_id = response['tenant']['id'] + return tenant_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - payload_req = {"name": net_name, "type": net_type, "tenant_id": self.tenant, "shared": public} - payload_req.update(vim_specific) + def delete_tenant(self,tenant_id): + '''Delete a tenant from VIM. Returns the old tenant identifier''' try: - vim_response = requests.post(self.url+'/networks', headers = self.headers_req, data=json.dumps({"network": payload_req}) ) - except requests.exceptions.RequestException, e: - print "new_tenant_network Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() + url = self.url_admin+'/tenants/'+tenant_id + self.logger.info("Delete a tenant DELETE %s", url) + vim_response = requests.delete(url, headers = self.headers_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) #print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_network_response_schema) - #print http_content - if res: - r = self._remove_extra_items(http_content, new_network_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - network_id = http_content['network']['id'] - print "Tenant Network id: ",network_id - return vim_response.status_code,network_id - else: return -vimconn.HTTP_Bad_Request,http_content - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to add new tenant network. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text + return tenant_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) def get_tenant_list(self, filter_dict={}): '''Obtain tenants of VIM - Filter_dict can be: - name: network name - id: network uuid - Returns the network list of dictionaries + filter_dict can contain the following keys: + name: filter by tenant name + id: filter by tenant uuid/id + + Returns the tenant list of dictionaries: [{'name':', 'id':', ...}, ...] ''' - print "VIMConnector.get_tenant_list: Getting tenants from VIM (filter: " + str(filter_dict) + "): " filterquery=[] filterquery_text='' for k,v in filter_dict.iteritems(): @@ -620,301 +470,246 @@ class vimconnector(vimconn.vimconnector): if len(filterquery)>0: filterquery_text='?'+ '&'.join(filterquery) try: - print self.url+'/tenants'+filterquery_text - vim_response = requests.get(self.url+'/tenants'+filterquery_text, headers = self.headers_req) - except requests.exceptions.RequestException, e: - print "get_tenant_list Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() + url = self.url+'/tenants'+filterquery_text + self.logger.info("get_tenant_list GET %s", url) + vim_response = requests.get(url, headers = self.headers_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) #print json.dumps(vim_response.json(), indent=4) - #TODO: parse input datares,http_content = self._format_in(vim_response, new_network_response_schema) - #print http_content - return vim_response.status_code, vim_response.json()["tenants"] - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to get tenant list. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text + return vim_response.json()["tenants"] + except requests.exceptions.RequestException as e: + self._format_request_exception(e) + + def new_network(self,net_name,net_type, shared=False, **vim_specific): + '''Adds a tenant network to VIM''' + '''Returns the network identifier''' + try: + self._get_my_tenant() + if net_type=="bridge": + net_type="bridge_data" + payload_req = {"name": net_name, "type": net_type, "tenant_id": self.tenant, "shared": shared} + payload_req.update(vim_specific) + url = self.url+'/networks' + self.logger.info("Adding a new network POST: %s DATA: %s", url, str(payload_req)) + vim_response = requests.post(url, headers = self.headers_req, data=json.dumps({"network": payload_req}) ) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + response = vim_response.json() + js_v(response, new_network_response_schema) + #r = self._remove_extra_items(response, new_network_response_schema) + #if r is not None: + # self.logger.warn("Warning: remove extra items %s", str(r)) + network_id = response['network']['id'] + return network_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) def get_network_list(self, filter_dict={}): '''Obtain tenant networks of VIM Filter_dict can be: name: network name id: network uuid - shared: boolean + public: boolean tenant_id: tenant admin_state_up: boolean status: 'ACTIVE' Returns the network list of dictionaries ''' - - filter_dict["tenant_id"] = self._get_my_tenant() - print "VIMConnector.get_network_list: Getting tenant network from VIM (filter: " + str(filter_dict) + "): " - filterquery=[] - filterquery_text='' - for k,v in filter_dict.iteritems(): - filterquery.append(str(k)+'='+str(v)) - if len(filterquery)>0: - filterquery_text='?'+ '&'.join(filterquery) try: - print self.url+'/networks'+filterquery_text - vim_response = requests.get(self.url+'/networks'+filterquery_text, headers = self.headers_req) - except requests.exceptions.RequestException, e: - print "get_network_list Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() + if 'tenant_id' not in filter_dict: + filter_dict["tenant_id"] = self._get_my_tenant() + elif not filter_dict["tenant_id"]: + del filter_dict["tenant_id"] + filterquery=[] + filterquery_text='' + for k,v in filter_dict.iteritems(): + filterquery.append(str(k)+'='+str(v)) + if len(filterquery)>0: + filterquery_text='?'+ '&'.join(filterquery) + url = self.url+'/networks'+filterquery_text + self.logger.info("Getting network list GET %s", url) + vim_response = requests.get(url, headers = self.headers_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) #print json.dumps(vim_response.json(), indent=4) - #TODO: parse input datares,http_content = self._format_in(vim_response, new_network_response_schema) - #print http_content - return vim_response.status_code, vim_response.json()["networks"] - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to get network list. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - - def get_tenant_network(self, net_id, tenant_id=None): - '''Obtain tenant networks of VIM''' - '''Returns the network information from a network id''' - if self.debug: - print "VIMconnector.get_tenant_network(): Getting tenant network %s from VIM" % net_id - filter_dict={"id": net_id} - if tenant_id: - filter_dict["tenant_id"] = tenant_id - r, net_list = self.get_network_list(filter_dict) - if r<0: - return r, net_list - if len(net_list)==0: - return -vimconn.HTTP_Not_Found, "Network '%s' not found" % net_id - elif len(net_list)>1: - return -vimconn.HTTP_Conflict, "Found more than one network with this criteria" - return 1, net_list[0] + response = vim_response.json() + return response['networks'] + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - def delete_tenant_network(self, net_id): + def get_network(self, net_id): + '''Obtain network details of network id''' + try: + url = self.url+'/networks/'+net_id + self.logger.info("Getting network GET %s", url) + vim_response = requests.get(url, headers = self.headers_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + response = vim_response.json() + return response['network'] + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) + + def delete_network(self, net_id): '''Deletes a tenant network from VIM''' '''Returns the network identifier''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Deleting a new tenant network from VIM tenant: " + self.tenant + ", id: " + net_id + url = self.url+'/networks/'+net_id + self.logger.info("Deleting VIM network DELETE %s", url) + vim_response = requests.delete(url, headers=self.headers_req) + self._check_http_request_response(vim_response) + #self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + return net_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - try: - vim_response = requests.delete(self.url+'/networks/'+net_id, headers=self.headers_req) - except requests.exceptions.RequestException, e: - print "delete_tenant_network Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - return vim_response.status_code,net_id - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to delete tenant network. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - - def refresh_tenant_network(self, net_id): - '''Refreshes the status of the tenant network''' - '''Returns: 0 if no error, - <0 if error''' - return 0 - - def get_tenant_flavor(self, flavor_id): - '''Obtain flavor details from the VIM - Returns the flavor dict details - ''' + def get_flavor(self, flavor_id): + '''Obtain flavor details from the VIM''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Getting flavor from VIM" - #print "VIM URL:",self.url - #print "Tenant id:",self.tenant - #print "Flavor:",flavor_data - try: - vim_response = requests.get(self.url+'/'+self.tenant+'/flavors/'+flavor_id, headers = self.headers_req) - except requests.exceptions.RequestException, e: - print "get_tenant_flavor Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() + url = self.url+'/'+self.tenant+'/flavors/'+flavor_id + self.logger.info("Getting flavor GET %s", url) + vim_response = requests.get(url, headers = self.headers_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) #print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, get_flavor_response_schema) - #print http_content - if res: - r = self._remove_extra_items(http_content, get_flavor_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - flavor_id = http_content['flavor']['id'] - print "Flavor id: ",flavor_id - return vim_response.status_code,flavor_id - else: return -vimconn.HTTP_Bad_Request,http_content - - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to get flavor. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text + response = vim_response.json() + js_v(response, get_flavor_response_schema) + r = self._remove_extra_items(response, get_flavor_response_schema) + if r is not None: + self.logger.warn("Warning: remove extra items %s", str(r)) + return response['flavor'] + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - def new_tenant_flavor(self, flavor_data): + def new_flavor(self, flavor_data): '''Adds a tenant flavor to VIM''' '''Returns the flavor identifier''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Adding a new flavor to VIM" - #print "VIM URL:",self.url - #print "Tenant id:",self.tenant - #print "Flavor:",flavor_data - payload_req = json.dumps({'flavor': flavor_data}) - try: - vim_response = requests.post(self.url+'/'+self.tenant+'/flavors', headers = self.headers_req, data=payload_req) - except requests.exceptions.RequestException, e: - print "new_tenant_flavor Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() + payload_req = json.dumps({'flavor': flavor_data}) + url = self.url+'/'+self.tenant+'/flavors' + self.logger.info("Adding a new VIM flavor POST %s", url) + vim_response = requests.post(url, headers = self.headers_req, data=payload_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) #print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_flavor_response_schema) - #print http_content - if res: - r = self._remove_extra_items(http_content, new_flavor_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - flavor_id = http_content['flavor']['id'] - print "Flavor id: ",flavor_id - return vim_response.status_code,flavor_id - else: return -vimconn.HTTP_Bad_Request,http_content - - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to add new flavor. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text + response = vim_response.json() + js_v(response, new_flavor_response_schema) + r = self._remove_extra_items(response, new_flavor_response_schema) + if r is not None: + self.logger.warn("Warning: remove extra items %s", str(r)) + flavor_id = response['flavor']['id'] + return flavor_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - def delete_tenant_flavor(self,flavor_id): + def delete_flavor(self,flavor_id): '''Deletes a tenant flavor from VIM''' - '''Returns the HTTP response code and a message indicating details of the success or fail''' + '''Returns the old flavor_id''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Deleting a flavor from VIM" - print "VIM URL:",self.url - print "Tenant id:",self.tenant - print "Flavor id:",flavor_id - #payload_req = flavor_data - try: - vim_response = requests.delete(self.url+'/'+self.tenant+'/flavors/'+flavor_id) - except requests.exceptions.RequestException, e: - print "delete_tenant_flavor Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - print vim_response.status_code - if vim_response.status_code == 200: - result = vim_response.json()["result"] - return 200,result - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to delete flavor. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text + url = self.url+'/'+self.tenant+'/flavors/'+flavor_id + self.logger.info("Deleting VIM flavor DELETE %s", url) + vim_response = requests.delete(url, headers=self.headers_req) + self._check_http_request_response(vim_response) + #self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + return flavor_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - def new_tenant_image(self,image_dict): - ''' - Adds a tenant image to VIM - Returns: - 200, image-id if the image is created - <0, message if there is an error - ''' + def get_image(self, image_id): + '''Obtain image details from the VIM''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Adding a new image to VIM", image_dict['location'] - new_image_dict={'name': image_dict['name']} - if 'description' in image_dict and image_dict['description'] != None: - new_image_dict['description'] = image_dict['description'] - if 'metadata' in image_dict and image_dict['metadata'] != None: - new_image_dict['metadata'] = yaml.load(image_dict['metadata']) - if 'location' in image_dict and image_dict['location'] != None: - new_image_dict['path'] = image_dict['location'] - payload_req = json.dumps({"image":new_image_dict}) - url=self.url + '/' + self.tenant + '/images' + url = self.url+'/'+self.tenant+'/images/'+image_id + self.logger.info("Getting image GET %s", url) + vim_response = requests.get(url, headers = self.headers_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + response = vim_response.json() + js_v(response, get_image_response_schema) + r = self._remove_extra_items(response, get_image_response_schema) + if r is not None: + self.logger.warn("Warning: remove extra items %s", str(r)) + return response['image'] + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) + + def new_image(self,image_dict): + ''' Adds a tenant image to VIM, returns image_id''' try: + self._get_my_tenant() + new_image_dict={'name': image_dict['name']} + if image_dict.get('description'): + new_image_dict['description'] = image_dict['description'] + if image_dict.get('metadata'): + new_image_dict['metadata'] = yaml.load(image_dict['metadata']) + if image_dict.get('location'): + new_image_dict['path'] = image_dict['location'] + payload_req = json.dumps({"image":new_image_dict}) + url=self.url + '/' + self.tenant + '/images' + self.logger.info("Adding a new VIM image POST %s", url) vim_response = requests.post(url, headers = self.headers_req, data=payload_req) - except requests.exceptions.RequestException, e: - print "new_tenant_image Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) #print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_image_response_schema) - #print http_content - if res: - r = self._remove_extra_items(http_content, new_image_response_schema) - if r is not None: print "Warning: remove extra items ", r - #print http_content - image_id = http_content['image']['id'] - print "Image id: ",image_id - return vim_response.status_code,image_id - else: return -vimconn.HTTP_Bad_Request,http_content - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to add new image. HTTP Response: %d. Error: %s' % (url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - - def delete_tenant_image(self, image_id): + response = vim_response.json() + js_v(response, new_image_response_schema) + r = self._remove_extra_items(response, new_image_response_schema) + if r is not None: + self.logger.warn("Warning: remove extra items %s", str(r)) + image_id = response['image']['id'] + return image_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) + + def delete_image(self, image_id): '''Deletes a tenant image from VIM''' - '''Returns the HTTP response code and a message indicating details of the success or fail''' + '''Returns the deleted image_id''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Deleting an image from VIM" - #payload_req = flavor_data - url=self.url + '/'+ self.tenant +'/images/'+image_id + url = self.url + '/'+ self.tenant +'/images/'+image_id + self.logger.info("Deleting VIM image DELETE %s", url) + vim_response = requests.delete(url, headers=self.headers_req) + self._check_http_request_response(vim_response) + #self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + return image_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) + + + def get_image_id_from_path(self, path): + '''Get the image id from image path in the VIM database''' try: - vim_response = requests.delete(url) - except requests.exceptions.RequestException, e: - print "delete_tenant_image Exception url '%s': " % url, e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - print vim_response.status_code - if vim_response.status_code == 200: - result = vim_response.json()["result"] - return 200,result - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to delete image. HTTP Response: %d. Error: %s' % (url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - - def new_tenant_vminstancefromJSON(self, vm_data): + self._get_my_tenant() + url=self.url + '/' + self.tenant + '/images?path='+path + self.logger.info("Getting images GET %s", url) + vim_response = requests.get(url) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + response = vim_response.json() + js_v(response, get_images_response_schema) + #r = self._remove_extra_items(response, get_images_response_schema) + #if r is not None: + # self.logger.warn("Warning: remove extra items %s", str(r)) + if len(response['images'])==0: + raise vimconn.vimconnNotFoundException("Image not found at VIM with path '%s'", path) + elif len(response['images'])>1: + raise vimconn.vimconnConflictException("More than one image found at VIM with path '%s'", path) + return response['images'][0]['id'] + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) + + def new_vminstancefromJSON(self, vm_data): '''Adds a VM instance to VIM''' '''Returns the instance identifier''' try: @@ -926,7 +721,7 @@ class vimconnector(vimconn.vimconnector): try: vim_response = requests.post(self.url+'/'+self.tenant+'/servers', headers = self.headers_req, data=payload_req) except requests.exceptions.RequestException, e: - print "new_tenant_vminstancefromJSON Exception: ", e.args + print "new_vminstancefromJSON Exception: ", e.args return -vimconn.HTTP_Not_Found, str(e.args[0]) print vim_response #print vim_response.status_code @@ -950,7 +745,7 @@ class vimconnector(vimconn.vimconnector): #print text return -vim_response.status_code,text - def new_tenant_vminstance(self,name,description,start,image_id,flavor_id,net_list): + def new_vminstance(self,name,description,start,image_id,flavor_id,net_list): '''Adds a VM instance to VIM Params: start: indicates if VM must start or boot in pause mode. Ignored @@ -965,312 +760,248 @@ class vimconnector(vimconn.vimconnector): type: 'virtual', 'PF', 'VF', 'VFnotShared' vim_id: filled/added by this function #TODO ip, security groups - Returns >=0, the instance identifier - <0, error_text + Returns the instance identifier ''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Adding a new VM instance to VIM" - -# net_list = [] -# for k,v in net_dict.items(): -# print k,v -# net_list.append('{"name":"' + k + '", "uuid":"' + v + '"}') -# net_list_string = ', '.join(net_list) - virtio_net_list=[] - for net in net_list: - if not net.get("net_id"): - continue - net_dict={'uuid': net["net_id"]} - if net.get("type"): net_dict["type"] = net["type"] - if net.get("name"): net_dict["name"] = net["name"] - if net.get("vpci"): net_dict["vpci"] = net["vpci"] - if net.get("model"): net_dict["model"] = net["model"] - if net.get("mac_address"): net_dict["mac_address"] = net["mac_address"] - virtio_net_list.append(net_dict) - payload_dict={ "name": name, - "description": description, - "imageRef": image_id, - "flavorRef": flavor_id, - "networks": virtio_net_list - } - if start != None: - payload_dict["start"] = start - payload_req = json.dumps({"server": payload_dict}) - print self.url+'/'+self.tenant+'/servers'+payload_req - try: - vim_response = requests.post(self.url+'/'+self.tenant+'/servers', headers = self.headers_req, data=payload_req) - except requests.exceptions.RequestException, e: - print "new_tenant_vminstance Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code != 200: - print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to add new vm instance. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text - #ok - print vim_response.json() - print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_vminstance_response_schema) - #print http_content - if not res: - return -vimconn.HTTP_Bad_Request,http_content - #r = self._remove_extra_items(http_content, new_vminstance_response_schema) - #if r is not None: print "Warning: remove extra items ", r - vminstance_id = http_content['server']['id'] - print json.dumps(http_content, indent=4) - #connect data plane interfaces to network - for net in net_list: - if net["type"]=="virtual": +# net_list = [] +# for k,v in net_dict.items(): +# print k,v +# net_list.append('{"name":"' + k + '", "uuid":"' + v + '"}') +# net_list_string = ', '.join(net_list) + virtio_net_list=[] + for net in net_list: if not net.get("net_id"): continue - for iface in http_content['server']['networks']: - if "name" in net: - if net["name"]==iface["name"]: - net["vim_id"] = iface['iface_id'] - break - elif "net_id" in net: - if net["net_id"]==iface["net_id"]: - net["vim_id"] = iface['iface_id'] - break - else: #dataplane - for numa in http_content['server'].get('extended',{}).get('numas',() ): - for iface in numa.get('interfaces',() ): - if net['name'] == iface['name']: - net['vim_id'] = iface['iface_id'] - #Code bellow is not needed, current openvim connect dataplane interfaces - #if net.get("net_id"): - ##connect dataplane interface - # result, port_id = self.connect_port_network(iface['iface_id'], net["net_id"]) - # if result < 0: - # error_text = "Error attaching port %s to network %s: %s." % (iface['iface_id'], net["net_id"], port_id) - # print "new_tenant_vminstance: " + error_text - # self.delete_tenant_vminstance(vminstance_id) - # return result, error_text - break + net_dict={'uuid': net["net_id"]} + if net.get("type"): net_dict["type"] = net["type"] + if net.get("name"): net_dict["name"] = net["name"] + if net.get("vpci"): net_dict["vpci"] = net["vpci"] + if net.get("model"): net_dict["model"] = net["model"] + if net.get("mac_address"): net_dict["mac_address"] = net["mac_address"] + virtio_net_list.append(net_dict) + payload_dict={ "name": name, + "description": description, + "imageRef": image_id, + "flavorRef": flavor_id, + "networks": virtio_net_list + } + if start != None: + payload_dict["start"] = start + payload_req = json.dumps({"server": payload_dict}) + url = self.url+'/'+self.tenant+'/servers' + self.logger.info("Adding a new vm POST %s DATA %s", url, payload_req) + vim_response = requests.post(url, headers = self.headers_req, data=payload_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + response = vim_response.json() + js_v(response, new_vminstance_response_schema) + #r = self._remove_extra_items(response, new_vminstance_response_schema) + #if r is not None: + # self.logger.warn("Warning: remove extra items %s", str(r)) + vminstance_id = response['server']['id'] + + #connect data plane interfaces to network + for net in net_list: + if net["type"]=="virtual": + if not net.get("net_id"): + continue + for iface in response['server']['networks']: + if "name" in net: + if net["name"]==iface["name"]: + net["vim_id"] = iface['iface_id'] + break + elif "net_id" in net: + if net["net_id"]==iface["net_id"]: + net["vim_id"] = iface['iface_id'] + break + else: #dataplane + for numa in response['server'].get('extended',{}).get('numas',() ): + for iface in numa.get('interfaces',() ): + if net['name'] == iface['name']: + net['vim_id'] = iface['iface_id'] + #Code bellow is not needed, current openvim connect dataplane interfaces + #if net.get("net_id"): + ##connect dataplane interface + # result, port_id = self.connect_port_network(iface['iface_id'], net["net_id"]) + # if result < 0: + # error_text = "Error attaching port %s to network %s: %s." % (iface['iface_id'], net["net_id"], port_id) + # print "new_vminstance: " + error_text + # self.delete_vminstance(vminstance_id) + # return result, error_text + break - print "VM instance id: ",vminstance_id - return vim_response.status_code,vminstance_id + return vminstance_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - def get_tenant_vminstance(self,vm_id): + def get_vminstance(self, vm_id): '''Returns the VM instance information from VIM''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Getting tenant VM instance information from VIM" - - url = self.url+'/'+self.tenant+'/servers/'+vm_id - print url - try: + url = self.url+'/'+self.tenant+'/servers/'+vm_id + self.logger.info("Getting vm GET %s", url) vim_response = requests.get(url, headers = self.headers_req) - except requests.exceptions.RequestException, e: - print "get_tenant_vminstance Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - print vim_response.json() - print json.dumps(vim_response.json(), indent=4) - res,http_content = self._format_in(vim_response, new_vminstance_response_schema) - #print http_content - if res: - print json.dumps(http_content, indent=4) - return vim_response.status_code,http_content - else: return -vimconn.HTTP_Bad_Request,http_content - else: - print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to get vm instance. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code,text + vim_response = requests.get(url, headers = self.headers_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + response = vim_response.json() + js_v(response, new_vminstance_response_schema) + #r = self._remove_extra_items(response, new_vminstance_response_schema) + #if r is not None: + # self.logger.warn("Warning: remove extra items %s", str(r)) + return response['server'] + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - def delete_tenant_vminstance(self, vm_id): - '''Removes a VM instance from VIM''' - '''Returns the instance identifier''' + def delete_vminstance(self, vm_id): + '''Removes a VM instance from VIM, returns the deleted vm_id''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Delete a VM instance from VIM " + vm_id - - try: - vim_response = requests.delete(self.url+'/'+self.tenant+'/servers/'+vm_id, headers = self.headers_req) - except requests.exceptions.RequestException, e: - print "delete_tenant_vminstance Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) - - #print vim_response.status_code - if vim_response.status_code == 200: - print json.dumps(vim_response.json(), indent=4) - return vim_response.status_code, vm_id - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": not possible to delete vm instance. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return -vim_response.status_code, text + url = self.url+'/'+self.tenant+'/servers/'+vm_id + self.logger.info("Deleting VIM vm DELETE %s", url) + vim_response = requests.delete(url, headers=self.headers_req) + self._check_http_request_response(vim_response) + #self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + return vm_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - def refresh_tenant_vms_and_nets(self, vmDict, netDict): - '''Refreshes the status of the dictionaries of VM instances and nets passed as arguments. It modifies the dictionaries''' - '''Returns: - - result: 0 if all elements could be refreshed (even if its status didn't change) - n>0, the number of elements that couldn't be refreshed, - <0 if error (foreseen) - - error_msg: text with reference to possible errors - ''' + def refresh_vms_status(self, vm_list): + '''Refreshes the status of the virtual machines''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - #vms_refreshed = [] - #nets_refreshed = [] - vms_unrefreshed = [] - nets_unrefreshed = [] - for vm_id in vmDict: - vmDict[vm_id]={'error_msg':None, 'vim_info':None} - print "VIMConnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM" - - url = self.url+'/'+self.tenant+'/servers/'+ vm_id - print url + except requests.exceptions.RequestException as e: + self._format_request_exception(e) + vm_dict={} + for vm_id in vm_list: + vm={} + #print "VIMConnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM" try: + url = self.url+'/'+self.tenant+'/servers/'+ vm_id + self.logger.info("Getting vm GET %s", url) vim_response = requests.get(url, headers = self.headers_req) - except requests.exceptions.RequestException, e: - print "VIMConnector refresh_tenant_elements. Exception: ", e.args - vmDict[vm_id]['status'] = "VIM_ERROR" - vmDict[vm_id]['error_msg'] = str(e) - vms_unrefreshed.append(vm_id) - continue - #print vim_response - #print vim_response.status_code - if vim_response.status_code == 200: - #print vim_response.json() - #print json.dumps(vim_response.json(), indent=4) - management_ip = False - res,http_content = self._format_in(vim_response, new_vminstance_response_schema) - if res: - try: - vmDict[vm_id]['status'] = vmStatus2manoFormat[ http_content['server']['status'] ] - if http_content['server'].get('last_error'): - vmDict[vm_id]['error_msg'] = http_content['server']['last_error'] - vmDict[vm_id]["vim_info"] = yaml.safe_dump(http_content['server']) - vmDict[vm_id]["interfaces"]=[] - #get interfaces info - url2 = self.url+'/ports?device_id='+ vm_id - try: - vim_response2 = requests.get(url2, headers = self.headers_req) - if vim_response.status_code == 200: - client_data = vim_response2.json() - for port in client_data.get("ports"): - print "VACAport", port - interface={} - interface['vim_info'] = yaml.safe_dump(port) - interface["mac_address"] = port.get("mac_address") - interface["vim_net_id"] = port["network_id"] - interface["vim_interface_id"] = port["id"] - interface["ip_address"] = port.get("ip_address") - if interface["ip_address"]: - management_ip = True - if interface["ip_address"] == "0.0.0.0": - interface["ip_address"] = None - vmDict[vm_id]["interfaces"].append(interface) - - except Exception as e: - print "VIMConnector refresh_tenant_elements. Port get %s: %s", (type(e).__name__, (str(e) if len(e.args)==0 else str(e.args[0]))) - - if vmDict[vm_id]['status'] == "ACTIVE" and not management_ip: - vmDict[vm_id]['status'] = "ACTIVE:NoMgmtIP" - - except Exception as e: - vmDict[vm_id]['status'] = "VIM_ERROR" - vmDict[vm_id]['error_msg'] = str(e) - vms_unrefreshed.append(vm_id) - else: - vmDict[vm_id]['status'] = "VIM_ERROR" - vmDict[vm_id]['error_msg'] = str(http_content) - vms_unrefreshed.append(vm_id) - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - print 'VIMConnector refresh_tenant_vms_and_nets. Error in VIM "%s": not possible to get VM instance. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - if vim_response.status_code == 404: # HTTP_Not_Found - vmDict[vm_id]['status'] = "DELETED" + self._check_http_request_response(vim_response) + response = vim_response.json() + js_v(response, new_vminstance_response_schema) + if response['server']['status'] in vmStatus2manoFormat: + vm['status'] = vmStatus2manoFormat[ response['server']['status'] ] else: - vmDict[vm_id]['status'] = "VIM_ERROR" - vmDict[vm_id]['error_msg'] = jsonerror - vms_unrefreshed.append(vm_id) - - #print "VMs refreshed: %s" % str(vms_refreshed) - for net_id in netDict: - netDict[net_id] = {'error_msg':None, 'vim_info':None} - print "VIMConnector refresh_tenant_vms_and_nets: Getting tenant network from VIM (tenant: " + str(self.tenant) + "): " - r,c = self.get_tenant_network(net_id) - if r<0: - print "VIMconnector refresh_tenant_network. Error getting net_id '%s' status: %s" % (net_id, c) - if r==-vimconn.HTTP_Not_Found: - netDict[net_id]['status'] = "DELETED" #TODO check exit status - else: - netDict[net_id]['status'] = "VIM_ERROR" - netDict[net_id]['error_msg'] = c - nets_unrefreshed.append(net_id) - else: - try: - net_status = netStatus2manoFormat[ c['status'] ] - if net_status == "ACTIVE" and not c['admin_state_up']: - net_status = "DOWN" - netDict[net_id]['status'] = net_status - if c.get('last_error'): - netDict[net_id]['error_msg'] = c['last_error'] - netDict[net_id]["vim_info"] = yaml.safe_dump(c) + vm['status'] = "OTHER" + vm['error_msg'] = "VIM status reported " + response['server']['status'] + if response['server'].get('last_error'): + vm['error_msg'] = response['server']['last_error'] + vm["vim_info"] = yaml.safe_dump(response['server']) + #get interfaces info + try: + management_ip = False + url2 = self.url+'/ports?device_id='+ vm_id + self.logger.info("Getting PORTS GET %s", url2) + vim_response2 = requests.get(url2, headers = self.headers_req) + self._check_http_request_response(vim_response2) + client_data = vim_response2.json() + if isinstance(client_data.get("ports"), list): + vm["interfaces"]=[] + for port in client_data.get("ports"): + interface={} + interface['vim_info'] = yaml.safe_dump(port) + interface["mac_address"] = port.get("mac_address") + interface["vim_net_id"] = port["network_id"] + interface["vim_interface_id"] = port["id"] + interface["ip_address"] = port.get("ip_address") + if interface["ip_address"]: + management_ip = True + if interface["ip_address"] == "0.0.0.0": + interface["ip_address"] = None + vm["interfaces"].append(interface) + except Exception as e: - netDict[net_id]['status'] = "VIM_ERROR" - netDict[net_id]['error_msg'] = str(e) - nets_unrefreshed.append(net_id) + self.logger.error("refresh_vms_and_nets. Port get %s: %s", type(e).__name__, str(e)) - #print "Nets refreshed: %s" % str(nets_refreshed) - - error_msg="" - if len(vms_unrefreshed)+len(nets_unrefreshed)>0: - error_msg += "VMs unrefreshed: " + str(vms_unrefreshed) + "; nets unrefreshed: " + str(nets_unrefreshed) - print error_msg + if vm['status'] == "ACTIVE" and not management_ip: + vm['status'] = "ACTIVE:NoMgmtIP" + + except vimconn.vimconnNotFoundException as e: + self.logger.error("Exception getting vm status: %s", str(e)) + vm['status'] = "DELETED" + vm['error_msg'] = str(e) + except (requests.exceptions.RequestException, js_e.ValidationError, vimconn.vimconnException) as e: + self.logger.error("Exception getting vm status: %s", str(e)) + vm['status'] = "VIM_ERROR" + vm['error_msg'] = str(e) + vm_dict[vm_id] = vm + return vm_dict + + def refresh_nets_status(self, net_list): + '''Get the status of the networks + Params: the list of network identifiers + Returns a dictionary with: + 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, ...) + # OTHER (Vim reported other status not understood) + # ERROR (VIM indicates an ERROR status) + # ACTIVE, INACTIVE, DOWN (admin down), + # BUILD (on building process) + # + 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) - #return len(vms_unrefreshed)+len(nets_unrefreshed), error_msg, vms_refreshed, nets_refreshed - return len(vms_unrefreshed)+len(nets_unrefreshed), error_msg + ''' + try: + self._get_my_tenant() + except requests.exceptions.RequestException as e: + self._format_request_exception(e) + + net_dict={} + for net_id in net_list: + net = {} + #print "VIMConnector refresh_tenant_vms_and_nets: Getting tenant network from VIM (tenant: " + str(self.tenant) + "): " + try: + net_vim = self.get_network(net_id) + if net_vim['status'] in netStatus2manoFormat: + net["status"] = netStatus2manoFormat[ net_vim['status'] ] + else: + net["status"] = "OTHER" + net["error_msg"] = "VIM status reported " + net_vim['status'] + + if net["status"] == "ACTIVE" and not net_vim['admin_state_up']: + net["status"] = "DOWN" + if net_vim.get('last_error'): + net['error_msg'] = net_vim['last_error'] + net["vim_info"] = yaml.safe_dump(net_vim) + except vimconn.vimconnNotFoundException as e: + self.logger.error("Exception getting net status: %s", str(e)) + net['status'] = "DELETED" + net['error_msg'] = str(e) + except (requests.exceptions.RequestException, js_e.ValidationError, vimconn.vimconnException) as e: + self.logger.error("Exception getting net status: %s", str(e)) + net['status'] = "VIM_ERROR" + net['error_msg'] = str(e) + net_dict[net_id] = net + return net_dict - def action_tenant_vminstance(self, vm_id, action_dict): + def action_vminstance(self, vm_id, action_dict): '''Send and action over a VM instance from VIM''' '''Returns the status''' try: self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - print "VIMConnector: Action over VM instance from VIM " + vm_id - - try: if "console" in action_dict: - return -vimconn.HTTP_Service_Unavailable, "getting console is not available at openvim" - - vim_response = requests.post(self.url+'/'+self.tenant+'/servers/'+vm_id+"/action", headers = self.headers_req, data=json.dumps(action_dict) ) - except requests.exceptions.RequestException, e: - print "action_tenant_vminstance Exception: ", e.args - return -vimconn.HTTP_Not_Found, str(e.args[0]) + raise vimconn.vimconnException("getting console is not available at openvim", http_code=vimconn.HTTP_Service_Unavailable) + url = self.url+'/'+self.tenant+'/servers/'+vm_id+"/action" + self.logger.info("Action over VM instance POST %s", url) + vim_response = requests.post(url, headers = self.headers_req, data=json.dumps(action_dict) ) + self._check_http_request_response(vim_response) + return vm_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) - #print vim_response.status_code - if vim_response.status_code == 200: - #print "vimconnector.action_tenant_vminstance():", json.dumps(vim_response.json(), indent=4) - return vim_response.status_code, vm_id - else: - #print vim_response.text - jsonerror = self._format_jsonerror(vim_response) - text = 'Error in VIM "%s": action over vm instance. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) - #print text - return vim_response.status_code, text - +#NOT USED METHODS in current version + def host_vim2gui(self, host, server_dict): '''Transform host dictionary from VIM format to GUI format, and append to the server_dict @@ -1414,40 +1145,132 @@ class vimconnector(vimconn.vimconnector): res,rankings = self._format_in(vim_response, get_processor_rankings_response_schema) return res, rankings['rankings'] - def get_image_id_from_path(self, path): - '''Get the image id from image path in the VIM database''' - '''Returns: - 0,"Image not found" if there are no images with that path - 1,image-id if there is one image with that path - <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.) - ''' + def new_host(self, host_data): + '''Adds a new host to VIM''' + '''Returns status code of the VIM response''' + payload_req = host_data try: - self._get_my_tenant() - except Exception as e: - return -vimconn.HTTP_Not_Found, str(e) - url=self.url + '/' + self.tenant + '/images?path='+path + url = self.url_admin+'/hosts' + self.logger.info("Adding a new host POST %s", url) + vim_response = requests.post(url, headers = self.headers_req, data=payload_req) + self._check_http_request_response(vim_response) + self.logger.debug(vim_response.text) + #print json.dumps(vim_response.json(), indent=4) + response = vim_response.json() + js_v(response, new_host_response_schema) + r = self._remove_extra_items(response, new_host_response_schema) + if r is not None: + self.logger.warn("Warning: remove extra items %s", str(r)) + host_id = response['host']['id'] + return host_id + except (requests.exceptions.RequestException, js_e.ValidationError) as e: + self._format_request_exception(e) + + def new_external_port(self, port_data): + '''Adds a external port to VIM''' + '''Returns the port identifier''' + #TODO change to logging exception code policies + print "VIMConnector: Adding a new external port" + payload_req = port_data try: - vim_response = requests.get(url) + vim_response = requests.post(self.url_admin+'/ports', headers = self.headers_req, data=payload_req) except requests.exceptions.RequestException, e: - print "get_image_id_from_path url='%s'Exception: '%s'" % (url, str(e.args)) + self.logger.error("new_external_port Exception: ", str(e)) return -vimconn.HTTP_Not_Found, str(e.args[0]) - print "vim get_image_id_from_path", url, "response:", vim_response.status_code, vim_response.json() + print vim_response #print vim_response.status_code + if vim_response.status_code == 200: + #print vim_response.json() #print json.dumps(vim_response.json(), indent=4) - if vim_response.status_code != 200: - #TODO: get error - print 'vimconnector.get_image_id_from_path error getting image id from path. Error code: %d Description: %s' %(vim_response.status_code, vim_response.json()) - return -vim_response.status_code, "Error getting image id from path" + res, http_content = self._format_in(vim_response, new_port_response_schema) + #print http_content + if res: + r = self._remove_extra_items(http_content, new_port_response_schema) + if r is not None: print "Warning: remove extra items ", r + #print http_content + port_id = http_content['port']['id'] + print "Port id: ",port_id + return vim_response.status_code,port_id + else: return -vimconn.HTTP_Bad_Request,http_content + else: + #print vim_response.text + jsonerror = self._format_jsonerror(vim_response) + text = 'Error in VIM "%s": not possible to add new external port. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror) + #print text + return -vim_response.status_code,text + + def new_external_network(self,net_name,net_type): + '''Adds a external network to VIM (shared)''' + '''Returns the network identifier''' + #TODO change to logging exception code policies + print "VIMConnector: Adding external shared network to VIM (type " + net_type + "): "+ net_name + + payload_req = '{"network":{"name": "' + net_name + '","shared":true,"type": "' + net_type + '"}}' + try: + vim_response = requests.post(self.url+'/networks', headers = self.headers_req, data=payload_req) + except requests.exceptions.RequestException, e: + self.logger.error( "new_external_network Exception: ", e.args) + return -vimconn.HTTP_Not_Found, str(e.args[0]) + print vim_response + #print vim_response.status_code + if vim_response.status_code == 200: + #print vim_response.json() + #print json.dumps(vim_response.json(), indent=4) + res,http_content = self._format_in(vim_response, new_network_response_schema) + #print http_content + if res: + r = self._remove_extra_items(http_content, new_network_response_schema) + if r is not None: print "Warning: remove extra items ", r + #print http_content + network_id = http_content['network']['id'] + print "Network id: ",network_id + return vim_response.status_code,network_id + else: return -vimconn.HTTP_Bad_Request,http_content + else: + #print vim_response.text + jsonerror = self._format_jsonerror(vim_response) + text = 'Error in VIM "%s": not possible to add new external network. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror) + #print text + return -vim_response.status_code,text - res,image = self._format_in(vim_response, get_images_response_schema) - if not res: - print "vimconnector.get_image_id_from_path error" - return -vimconn.HTTP_Bad_Request, image - if len(image['images'])==0: - return 0,"Image not found" - elif len(image['images'])>1: - print "vimconnector.get_image_id_from_path error. More than one images with the path %s." %(path) - return -vimconn.HTTP_Internal_Server_Error,"More than one images with that path" - return 1, image['images'][0]['id'] + def connect_port_network(self, port_id, network_id, admin=False): + '''Connects a external port to a network''' + '''Returns status code of the VIM response''' + #TODO change to logging exception code policies + print "VIMConnector: Connecting external port to network" + + payload_req = '{"port":{"network_id":"' + network_id + '"}}' + if admin: + if self.url_admin==None: + return -vimconn.HTTP_Unauthorized, "datacenter cannot contain admin URL" + url= self.url_admin + else: + url= self.url + try: + vim_response = requests.put(url +'/ports/'+port_id, headers = self.headers_req, data=payload_req) + except requests.exceptions.RequestException, e: + print "connect_port_network Exception: ", e.args + return -vimconn.HTTP_Not_Found, str(e.args[0]) + print vim_response + #print vim_response.status_code + if vim_response.status_code == 200: + #print vim_response.json() + #print json.dumps(vim_response.json(), indent=4) + res,http_content = self._format_in(vim_response, new_port_response_schema) + #print http_content + if res: + r = self._remove_extra_items(http_content, new_port_response_schema) + if r is not None: print "Warning: remove extra items ", r + #print http_content + port_id = http_content['port']['id'] + print "Port id: ",port_id + return vim_response.status_code,port_id + else: return -vimconn.HTTP_Bad_Request,http_content + else: + print vim_response.text + jsonerror = self._format_jsonerror(vim_response) + text = 'Error in VIM "%s": not possible to connect external port to network. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror) + print text + return -vim_response.status_code,text