""" CloudEndpoint is a base class for the clients used to connect to different cloud providers """ from __future__ import print_function from __future__ import division from __future__ import absolute_import __RCSID__ = "45ddde113 (2021-10-01 11:19:55 +0200) Chris Burr " import os import ssl import time from libcloud import security from libcloud.compute.types import Provider from libcloud.compute.providers import get_driver from libcloud.common.exceptions import BaseHTTPError # DIRAC from DIRAC import gLogger, S_OK, S_ERROR from DIRAC.Core.Utilities.File import makeGuid from DIRAC.Resources.Cloud.Endpoint import Endpoint from DIRAC.Resources.Cloud.Utilities import STATE_MAP class CloudEndpoint(Endpoint): """CloudEndpoint base class""" def __init__(self, parameters=None): super(CloudEndpoint, self).__init__(parameters=parameters) # logger self.log = gLogger.getSubLogger("CloudEndpoint") self.valid = False result = self.initialize() if result["OK"]: self.log.debug("CloudEndpoint created and validated") self.valid = True def initialize(self): # Relax security security.SSL_VERSION = ssl.PROTOCOL_SSLv23 security.VERIFY_SSL_CERT = False # Variables needed to contact the service connDict = {} for var in [ "ex_domain_name", "ex_force_auth_url", "ex_force_service_region", "ex_force_auth_version", "ex_tenant_name", "ex_keyname", "ex_voms_proxy", ]: if var in self.parameters: connDict[var] = self.parameters[var] username = self.parameters.get("User") password = self.parameters.get("Password") for key in connDict: self.log.info("%s: %s" % (key, connDict[key])) # get cloud driver providerName = self.parameters.get("Provider", "OPENSTACK").upper() providerCode = getattr(Provider, providerName) self.driverClass = get_driver(providerCode) self.__driver = self.driverClass(username, password, **connDict) return self.__checkConnection() def __checkConnection(self): """ Checks connection status by trying to list the images. :return: S_OK | S_ERROR """ try: _result = self.__driver.list_images() except Exception as errmsg: return S_ERROR(errmsg) return S_OK() def __getImageByName(self, imageName): """ Given the imageName, returns the current image object from the server. :Parameters: **imageName** - `string` imageName as stored on the OpenStack image repository ( glance ) :return: S_OK( image ) | S_ERROR """ try: images = self.__driver.list_images() except Exception as errmsg: return S_ERROR(errmsg) image = None for im in images: if im.name == imageName: image = im break if image is None: return S_ERROR("Image %s not found" % imageName) return S_OK(image) def __getFlavorByName(self, flavorName): """ Given the flavorName, returns the current flavor object from the server. :Parameters: **flavorName** - `string` flavorName as stored on the OpenStack service :return: S_OK( flavor ) | S_ERROR """ try: flavors = self.__driver.list_sizes() except Exception as errmsg: return S_ERROR(errmsg) flavor = None for fl in flavors: if fl.name == flavorName: flavor = fl if flavor is None: return S_ERROR("Flavor %s not found" % flavorName) return S_OK(flavor) def __getSecurityGroups(self, securityGroupNames=None): """ Given the securityGroupName, returns the current security group object from the server. :Parameters: **securityGroupName** - `string` securityGroupName as stored on the OpenStack service :return: S_OK( securityGroup ) | S_ERROR """ if not securityGroupNames: securityGroupNames = [] elif not isinstance(securityGroupNames, list): securityGroupNames = [securityGroupNames] if "default" not in securityGroupNames: securityGroupNames.append("default") try: secGroups = self.__driver.ex_list_security_groups() except Exception as errmsg: return S_ERROR(errmsg) return S_OK([secGroup for secGroup in secGroups if secGroup.name in securityGroupNames]) def createInstances(self, vmsToSubmit): outputDict = {} for nvm in range(vmsToSubmit): instanceID = makeGuid()[:8] createPublicIP = "ipPool" in self.parameters result = self.createInstance(instanceID, createPublicIP) if result["OK"]: node, publicIP = result["Value"] self.log.debug("Created VM instance %s/%s with publicIP %s" % (node.id, instanceID, publicIP)) nodeDict = {} nodeDict["PublicIP"] = publicIP nodeDict["InstanceID"] = instanceID nodeDict["NumberOfProcessors"] = self.flavor.vcpus nodeDict["RAM"] = self.flavor.ram nodeDict["DiskSize"] = self.flavor.disk nodeDict["Price"] = self.flavor.price outputDict[node.id] = nodeDict else: break if not outputDict: # Submission failed return result return S_OK(outputDict) def createInstance(self, instanceID="", createPublicIP=True): """ This creates a VM instance for the given boot image and creates a context script, taken the given parameters. Successful creation returns instance VM Boots a new node on the OpenStack server defined by self.endpointConfig. The 'personality' of the node is done by self.imageConfig. Both variables are defined on initialization phase. The node name has the following format: