diff --git a/IM/AppDB.py b/IM/AppDB.py index 08a17b0e5..5e9be270f 100644 --- a/IM/AppDB.py +++ b/IM/AppDB.py @@ -105,6 +105,7 @@ def get_image_id(site_id, image_name, vo_name): def get_image_data(str_url, stype="occi"): """ The url has this format: appdb://UPV-GRyCAP/egi.docker.ubuntu.16.04?fedcloud.egi.eu + or this one appdb://UPV-GRyCAP/83d5e854-a128-5b1f-9457-d32e10a720a6:8135 Get the Site url from the AppDB """ url = urlparse(str_url) @@ -114,17 +115,49 @@ def get_image_data(str_url, stype="occi"): site_name = url[1] image_name = url[2][1:] vo_name = url[4] + site_id = AppDB.get_site_id(site_name, stype) if not site_id: return None, None, "No site ID returned from EGI AppDB for site: %s." % site_name + site_url = AppDB.get_site_url(site_id, stype) if not site_url: return None, None, "No site URL returned from EGI AppDB for site id: %s." % site_id - image_id = AppDB.get_image_id(site_id, image_name, vo_name) - if not image_id: - return None, None, "No image ID returned from EGI AppDB for image: %s/%s/%s." % (site_id, - image_name, - vo_name) + + if not vo_name and len(image_name) >= 37 and ":" in image_name: + image_id = AppDB.get_image_id_from_uri(site_id, image_name) + if not image_id: + return None, None, "No image ID returned from EGI AppDB for image: %s/%s." % (site_id, + image_name) + else: + image_id = AppDB.get_image_id(site_id, image_name, vo_name) + if not image_id: + return None, None, "No image ID returned from EGI AppDB for image: %s/%s/%s." % (site_id, + image_name, + vo_name) + return site_url, image_id, "" return None, None, "Incorrect Protocol" + + @staticmethod + def get_image_id_from_uri(site_id, image_mp_uri): + """ + Get the image ID from the site id and image mp_uri + """ + if not image_mp_uri.startswith("http"): + image_mp_uri = "https://appdb.egi.eu/store/vo/image/%s/" % image_mp_uri + + data = AppDB.appdb_call('/rest/1.0/va_providers/%s' % site_id) + if data: + if 'provider:image' in data['appdb:appdb']['virtualization:provider']: + for image in data['appdb:appdb']['virtualization:provider']['provider:image']: + if image['@mp_uri'] == image_mp_uri: + image_basename = os.path.basename(image['@va_provider_image_id']) + parts = image_basename.split("#") + if len(parts) > 1: + return parts[1] + else: + return image_basename + + return None diff --git a/IM/REST.py b/IM/REST.py index ce8f131e2..9bbaeeb42 100644 --- a/IM/REST.py +++ b/IM/REST.py @@ -26,6 +26,7 @@ InvaliddUserException, DisabledFunctionException) from IM.auth import Authentication from IM.config import Config +from IM import get_ex_error from radl.radl_json import parse_radl as parse_radl_json, dump_radl as dump_radl_json, featuresToSimple, radlToSimple from radl.radl import RADL, Features, Feature from IM.tosca.Tosca import Tosca @@ -304,13 +305,6 @@ def format_output(res, default_type="text/plain", field_name=None, list_field_na return info -def get_ex_error(ex): - """ - Return a secure string with the error of the exception in Py2 and Py3 - """ - return getattr(ex, 'message', ex.args[0] if len(ex.args) else repr(ex)) - - @app.route('/infrastructures/:infid', method='DELETE') def RESTDestroyInfrastructure(infid=None): try: diff --git a/IM/ServiceRequests.py b/IM/ServiceRequests.py index d85f3f9e3..9dfa1a5e4 100644 --- a/IM/ServiceRequests.py +++ b/IM/ServiceRequests.py @@ -23,6 +23,7 @@ from IM.config import Config from IM.auth import Authentication from IM import __version__ as version +from IM import get_ex_error logger = logging.getLogger('InfrastructureManager') @@ -123,7 +124,7 @@ def _execute(self): return True except Exception as ex: logger.exception(self._error_mesage) - self.set("%s" % getattr(ex, 'message', ex.args[0] if len(ex.args) else repr(ex))) + self.set(get_ex_error(ex)) return False diff --git a/IM/__init__.py b/IM/__init__.py index 815cef794..3e0e0d0fc 100644 --- a/IM/__init__.py +++ b/IM/__init__.py @@ -21,3 +21,16 @@ 'VirtualMachine', 'VMRC', 'xmlobject'] __version__ = '1.9.0' __author__ = 'Miguel Caballer' + + +def get_ex_error(ex): + """ + Return a secure string with the error of the exception in Py2 and Py3 + """ + try: + return "%s" % ex + except Exception: + error = getattr(ex, 'message', None) + if not error: + error = ex.args[0] if len(ex.args) else repr(ex) + return error diff --git a/IM/connectors/OpenStack.py b/IM/connectors/OpenStack.py index 89855dab6..ec1e1676d 100644 --- a/IM/connectors/OpenStack.py +++ b/IM/connectors/OpenStack.py @@ -39,6 +39,7 @@ from IM.VirtualMachine import VirtualMachine from radl.radl import Feature from IM.AppDB import AppDB +from IM import get_ex_error class OpenStackCloudConnector(LibCloudCloudConnector): @@ -348,7 +349,7 @@ def setVolumesInfo(self, vm, node): os.path.basename(volume.extra['attachments'][0]['device'])) cont += 1 except Exception as ex: - self.log_warn("Error getting volume info: %s" % ex.args[0]) + self.log_warn("Error getting volume info: %s" % get_ex_error(ex)) def updateVMInfo(self, vm, auth_data): node = self.get_node_with_id(vm.id, auth_data) @@ -656,7 +657,7 @@ def get_router_public(self, driver, radl): if router.extra['external_gateway_info']['network_id'] in pub_nets: routers[pub_nets[router.extra['external_gateway_info']['network_id']]] = router except Exception as ex: - self.log_warn("Error listing routers: %s." % ex.args[0]) + self.log_warn("Error listing routers: %s." % get_ex_error(ex)) # try to select first the router of the net provider id if pub_net_provider_id in routers: @@ -708,7 +709,7 @@ def delete_networks(self, driver, inf): res = False msg = "Error deleting subnet %s from the router %s: %s" % (subnet_id, router.name, - ex.args[0]) + get_ex_error(ex)) self.log_info("Deleting net %s." % ost_net.name) driver.ex_delete_network(ost_net) @@ -775,7 +776,7 @@ def create_networks(self, driver, radl, inf): except Exception as ex: self.log_exception("Error creating ost network for net %s." % net_name) raise Exception("Error creating ost network for net %s: %s" % (net_name, - ex.args[0])) + get_ex_error(ex))) # now create the subnet ost_subnet_name = "im-%s-sub%s" % (inf.id, net_name) @@ -789,7 +790,7 @@ def create_networks(self, driver, radl, inf): self.log_debug("Deleting net: %s" % ost_net_name) driver.ex_delete_network(ost_net) raise Exception("Error creating ost subnet for net %s: %s" % (net_name, - ex.args[0])) + get_ex_error(ex))) if router is None: self.log_warn("No public router found.") @@ -805,7 +806,7 @@ def create_networks(self, driver, radl, inf): self.log_error("Error adding subnet to the router. Deleting net and subnet.") driver.ex_delete_subnet(ost_subnet) driver.ex_delete_network(ost_net) - raise Exception("Error adding subnet to the router: %s" % ex.args[0]) + raise Exception("Error adding subnet to the router: %s" % get_ex_error(ex)) network.setValue('provider_id', ost_net_name) except Exception as ext: @@ -1041,8 +1042,8 @@ def launch(self, inf, radl, requested_radl, num_vm, auth_data): vm.destroy = False res.append((True, vm)) except Exception as ex: - self.log_exception("Error creating node: %s." % ex.args[0]) - res.append((False, "%s" % ex.args[0])) + self.log_exception("Error creating node: %s." % get_ex_error(ex)) + res.append((False, "%s" % get_ex_error(ex))) i += 1 @@ -1189,7 +1190,7 @@ def add_elastic_ip_from_pool(self, vm, node, fixed_ip=None, pool_name=None): node.driver.ex_attach_floating_ip_to_node(node, floating_ip) attached = True except Exception as atex: - self.log_warn("Error attaching a Floating IP to the node: %s" % atex.args[0]) + self.log_warn("Error attaching a Floating IP to the node: %s" % get_ex_error(atex)) cont += 1 if cont < retries: time.sleep(delay) @@ -1209,7 +1210,7 @@ def add_elastic_ip_from_pool(self, vm, node, fixed_ip=None, pool_name=None): except Exception as ex: self.log_exception("Error adding an Elastic/Floating IP to VM ID: %s" % vm.id) - return False, "%s" % ex.args[0] + return False, "%s" % get_ex_error(ex) def _get_security_group(self, driver, sg_name): try: @@ -1275,7 +1276,7 @@ def create_security_groups(self, driver, inf, radl): driver.ex_create_security_group_rule(sg, 'tcp', 1, 65535, source_security_group=sg) driver.ex_create_security_group_rule(sg, 'udp', 1, 65535, source_security_group=sg) except Exception as addex: - self.log_warn("Exception adding SG rules. Probably the rules exists: %s" % addex.args[0]) + self.log_warn("Exception adding SG rules. Probably the rules exists: %s" % get_ex_error(addex)) outports = network.getOutPorts() if outports: @@ -1286,7 +1287,7 @@ def create_security_groups(self, driver, inf, radl): outport.get_port_init(), outport.get_port_end(), '0.0.0.0/0') except Exception as ex: - self.log_warn("Exception adding SG rules: %s" % ex.args[0]) + self.log_warn("Exception adding SG rules: %s" % get_ex_error(ex)) else: if outport.get_remote_port() != 22 or not network.isPublic(): try: @@ -1294,7 +1295,7 @@ def create_security_groups(self, driver, inf, radl): outport.get_remote_port(), outport.get_remote_port(), '0.0.0.0/0') except Exception as ex: - self.log_warn("Exception adding SG rules: %s" % ex.args[0]) + self.log_warn("Exception adding SG rules: %s" % get_ex_error(ex)) return res @@ -1315,7 +1316,7 @@ def finalize(self, vm, last, auth_data): res, msg = self.delete_elastic_ips(node, vm) except Exception as ex: res = False - msg = "%s" % ex.args[0] + msg = get_ex_error(ex) success.append(res) msgs.append(msg) @@ -1331,7 +1332,7 @@ def finalize(self, vm, last, auth_data): volume.destroy() except Exception as ex: res = False - msg = "%s" % ex.args[0] + msg = get_ex_error(ex) success.append(res) msgs.append(msg) @@ -1347,7 +1348,7 @@ def finalize(self, vm, last, auth_data): res, msg = self.delete_security_groups(driver, vm.inf) except Exception as ex: res = False - msg = "%s" % ex.args[0] + msg = get_ex_error(ex) success.append(res) msgs.append(msg) @@ -1356,7 +1357,7 @@ def finalize(self, vm, last, auth_data): res, msg = self.delete_networks(driver, vm.inf) except Exception as ex: res = False - msg = "%s" % ex.args[0] + msg = get_ex_error(ex) success.append(res) msgs.append(msg) else: @@ -1398,8 +1399,8 @@ def delete_security_groups(self, driver, inf, timeout=180, delay=10): driver.ex_delete_security_group(sg) deleted = True except Exception as ex: - self.log_warn("Error deleting the SG: %s" % ex.args[0]) - msg = "Error deleting the SG: %s" % ex.args[0] + self.log_warn("Error deleting the SG: %s" % get_ex_error(ex)) + msg = "Error deleting the SG: %s" % get_ex_error(ex) if not deleted: time.sleep(delay) @@ -1437,7 +1438,7 @@ def create_snapshot(self, vm, disk_num, image_name, auto_delete, auth_data): image = node.driver.create_image(node, image_name) except Exception as ex: self.log_exception("Error creating image.") - return False, "Error creating image: %s." % ex.args[0] + return False, "Error creating image: %s." % get_ex_error(ex) new_url = "ost://%s/%s" % (self.cloud.server, image.id) if auto_delete: vm.inf.snapshots.append(new_url) @@ -1452,13 +1453,13 @@ def delete_image(self, image_url, auth_data): image = driver.get_image(image_id) except Exception as ex: self.log_exception("Error getting image.") - return (False, "Error getting image %s: %s" % (image_id, ex.args[0])) + return (False, "Error getting image %s: %s" % (image_id, get_ex_error(ex))) try: driver.delete_image(image) return True, "" except Exception as ex: self.log_exception("Error deleting image.") - return (False, "Error deleting image.: %s" % ex.args[0]) + return (False, "Error deleting image.: %s" % get_ex_error(ex)) def reboot(self, vm, auth_data): node = self.get_node_with_id(vm.id, auth_data) @@ -1532,7 +1533,7 @@ def create_attach_volume(self, node, disk_size, disk_device, volume_name, locati volume = node.driver.create_volume(int(disk_size), volume_name, location=location) except Exception as ex: self.log_exception("Error creating volume.") - return False, None, ex.args[0] + return False, None, get_ex_error(ex) success = self.wait_volume(volume) if not success: self.log_error("Error waiting the volume ID %s." % volume.id) @@ -1543,7 +1544,7 @@ def create_attach_volume(self, node, disk_size, disk_device, volume_name, locati volume.attach(node, disk_device) except Exception as ex: self.log_exception("Error attaching volume ID %s" % volume.id) - return False, volume, ex.args[0] + return False, volume, get_ex_error(ex) # wait the volume to be attached success = self.wait_volume(volume, state='in-use') # update the volume data @@ -1656,7 +1657,8 @@ def delete_elastic_ips(self, node, vm): try: node.driver.ex_detach_floating_ip_from_node(node, floating_ip) except Exception as ex: - self.log_warn("Error detaching Floating IP: %s. %s" % (floating_ip.ip_address, ex.args[0])) + self.log_warn("Error detaching Floating IP: %s. %s" % (floating_ip.ip_address, + get_ex_error(ex))) # if it is in the list do not release it if floating_ip.ip_address in no_delete_ips: self.log_debug("Do not remove Floating IP: %s" % floating_ip.ip_address) @@ -1667,4 +1669,4 @@ def delete_elastic_ips(self, node, vm): return True, "" except Exception as ex: self.log_exception("Error removing Floating IPs to VM ID: " + str(vm.id)) - return False, "Error removing Floating IPs: %s" % ex.args[0] + return False, "Error removing Floating IPs: %s" % get_ex_error(ex) diff --git a/test/unit/AppDB.py b/test/unit/AppDB.py index 41ec9e723..efb25e6ed 100644 --- a/test/unit/AppDB.py +++ b/test/unit/AppDB.py @@ -51,23 +51,26 @@ def get_response(self, method, url, verify, cert=None, headers=None, data=None): http://cloud.recas.ba.infn.it:8774/v2.1/f41187320a504846b132582e172fa268 + mp_uri="https://appdb.egi.eu/store/vo/image/6c43f8b5-2e26-5e42-82d5-79bb738fa8e2:8187/" + archived="false" + vmiversion="2019.01.21" + va_provider_image_id="http://url/os_tpl#image_id" + appcname="egi.docker.ubuntu.16.04" + voname="fedcloud.egi.eu"/> + mp_uri="https://appdb.egi.eu/store/vo/image/60c0ed25-fea5-5e63-b443-034df484b502:7661/" + archived="false" + vmiversion="2019.01.21" + va_provider_image_id="http://url/os_tpl#image_id2" + appcname="egi.ubuntu.16.04" + voname="fedcloud.egi.eu"/> + mp_uri="https://appdb.egi.eu/store/vo/image/83d5e854-a128-5b1f-9457-d32e10a720a6:8135/" + archived="true" + vmiversion="2018.01.21" + va_provider_image_id="http://url/os_tpl#image_id3" + appcname="egi.ubuntu.16.04" + voname="fedcloud.egi.eu"/> """ @@ -95,10 +98,21 @@ def test_get_image_id(self, requests): res = AppDB.get_image_id("8016G0", "egi.ubuntu.16.04", "fedcloud.egi.eu") self.assertEqual(res, "image_id2") + @patch('requests.request') + def test_get_image_id_from_uri(self, requests): + requests.side_effect = self.get_response + res = AppDB.get_image_id_from_uri("8016G0", "83d5e854-a128-5b1f-9457-d32e10a720a6:8135") + self.assertEqual(res, "image_id3") + @patch('requests.request') def test_get_image_data(self, requests): requests.side_effect = self.get_response str_url = "appdb://RECAS-BARI/egi.ubuntu.16.04?fedcloud.egi.eu" - site_url, image_id, msg = AppDB.get_image_data(str_url, "openstack") + site_url, image_id, _ = AppDB.get_image_data(str_url, "openstack") + self.assertEqual(site_url, "https://cloud.recas.ba.infn.it:5000") + self.assertEqual(image_id, "image_id2") + + str_url = "appdb://RECAS-BARI/60c0ed25-fea5-5e63-b443-034df484b502:7661" + site_url, image_id, _ = AppDB.get_image_data(str_url, "openstack") self.assertEqual(site_url, "https://cloud.recas.ba.infn.it:5000") self.assertEqual(image_id, "image_id2")