Пример #1
0
class AmazonImage:
  """
  AmazonImage class.
  """

  def __init__( self, imageName, endPoint ):
    """
    Constructor: uses AmazonConfiguration to parse the endPoint CS configuration
    and ImageConfiguration to parse the imageName CS configuration. 
    
    :Parameters:
      **imageName** - `string`
        imageName as defined on CS:/Resources/VirtualMachines/Images
        
      **endPoint** - `string`
        endPoint as defined on CS:/Resources/VirtualMachines/CloudEndpoint 
    
    """
    # logger
    self.log       = gLogger.getSubLogger( 'AmazonImage %s: ' % imageName )
    
    self.imageName = imageName
    self.endPoint  = endPoint 
    
    # their config() method returns a dictionary with the parsed configuration
    # they also provide a validate() method to make sure it is correct 
    self.__imageConfig = ImageConfiguration( imageName, endPoint )    
    self.__amazonConfig  = AmazonConfiguration( endPoint )

    self.__cliamazon  = None

  def connectAmazon( self ):
    """
    Method that issues the connection with Amazon. 
    In order to do it, validates the CS configurations.
    auth secretaccesskey

    :return: S_OK | S_ERROR
    """

    # Before doing anything, make sure the configurations make sense
    # ImageConfiguration
    validImage = self.__imageConfig.validate()
    if not validImage[ 'OK' ]:
      return validImage
    # EndpointConfiguration
    validAmazon = self.__amazonConfig.validate()
    if not validAmazon[ 'OK' ]:
      return validAmazon

    # Get authentication configuration
    # not needed jet, only secretaccesskey method
    #if not auth == 'secretaccesskey':
    #  self.__errorStatus = "auth not supported (currently secretaccesskey)"
    #  self.log.error( self.__errorStatus )
    #  return
   
    # this object will connect to the Amazon server. Better keep it private.   
    self.__cliamazon = AmazonClient( endpointConfig=self.__amazonConfig.config(), imageConfig=self.__imageConfig.config() )

    # Check connection to the server
    result = self.__cliamazon.check_connection()
    if not result[ 'OK' ]:
      self.log.error( "connectAmazon" )
      self.log.error( result[ 'Message' ] )
    else:
      self.log.info( "Successful connection check" )
      
    return result
   
  def startNewInstance( self, runningPodRequirements ):
    """
    Once the connection is stablished using the `connectAmazon` method, we can boot
    nodes. To do so, the config in __imageConfig and __amazonConfig applied to
    AmazonClient initialization is applied.
    
    :return: S_OK | S_ERROR
    """
    
    self.log.info( "Booting %s / %s" % ( self.__imageConfig.config()[ 'bootImageName' ],
                                         self.__amazonConfig.config()[ 'endpointURL' ] ) )

    result = self.__cliamazon.create_VMInstance( runningPodRequirements )

    if not result[ 'OK' ]:
      self.log.error( "startNewInstance" )
      self.log.error( result[ 'Message' ] )
    return result

  def getInstanceStatus( self, uniqueId ):
    """
    Given the node ID and the current instanceName (AMI in Amazon terms), 
    returns the status. 
    
    :Parameters:
      **uniqueId** - `string`
        node ID, given by the OpenStack service       
    
    :return: S_OK | S_ERROR
    """
    
    result = self.__cliamazon.getStatus_VMInstance( uniqueId, self.imageName )
    
    if not result[ 'OK' ]:
      self.log.error( "getInstanceStatus: %s" % uniqueId )
      self.log.error( result[ 'Message' ] )
    
    return result  
    
  def stopInstance( self, uniqueId ):
    """
    Method that destroys the instance 
    
    :Parameters:
      **uniqueId** - `string`
    
    :return: S_OK | S_ERROR
    """

    result = self.__cliamazon.terminate_VMinstance( uniqueId )
    
    if not result[ 'OK' ]:
      self.log.error( "stopInstance: %s " % ( uniqueId ) )
      self.log.error( result[ 'Message' ] )
    
    return result
Пример #2
0
class NovaImage:
  """
  NovaImage class.
  """

  def __init__( self, imageName, endPoint ):
    """
    Constructor: uses NovaConfiguration to parse the endPoint CS configuration
    and ImageConfiguration to parse the imageName CS configuration. 
    
    :Parameters:
      **imageName** - `string`
        imageName as defined on CS:/Resources/VirtualMachines/Images
        
      **endPoint** - `string`
        endPoint as defined on CS:/Resources/VirtualMachines/CloudEndpoint 
    
    """
    # logger
    self.log       = gLogger.getSubLogger( 'NovaImage %s: ' % imageName )
    
    self.imageName = imageName
    self.endPoint  = endPoint 
    
    # their config() method returns a dictionary with the parsed configuration
    # they also provide a validate() method to make sure it is correct 
    self.__imageConfig = ImageConfiguration( imageName )    
    self.__novaConfig  = NovaConfiguration( endPoint )
    
    # this object will connect to the server. Better keep it private.                 
    self.__clinova   = None


  def connectNova( self ):
    """
    Method that issues the connection with the OpenStack server. In order to do
    it, validates the CS configurations.
    auth user/password or x509 proxy
     
    :return: S_OK | S_ERROR
    """
    
    # Before doing anything, make sure the configurations make sense
    # ImageConfiguration
    validImage = self.__imageConfig.validate()
    if not validImage[ 'OK' ]:
      return validImage
    # EndpointConfiguration
    validNova = self.__novaConfig.validate()
    if not validNova[ 'OK' ]:
      return validNova

    # Get authentication configuration
    auth, u, p = self.__novaConfig.authConfig()
    if auth == 'userpasswd':
      user = u
      secret = p
    elif auth == 'proxy':
      proxyPath = u
    else:
      self.__errorStatus = "auth not supported (userpasswd/proxy)"
      self.log.error( self.__errorStatus )
      return
   
    # Create the libcloud and novaclient objects in NovaClient.Nova11
    if auth == 'userpasswd':
      self.__clinova = NovaClient( user=user, secret=secret, endpointConfig=self.__novaConfig.config(), imageConfig=self.__imageConfig.config() )
    else:
      self.__clinova = NovaClient( user=proxyPath, secret=None, endpointConfig=self.__novaConfig.config(), imageConfig=self.__imageConfig.config() )

    # Check connection to the server
    result = self.__clinova.check_connection()
    if not result[ 'OK' ]:
      self.log.error( "connectNova" )
      self.log.error( result[ 'Message' ] )
    else:
      self.log.info( "Successful connection check" )
      
    return result
   
  def startNewInstance( self, vmdiracInstanceID, runningPodRequirements ):
    """
    Once the connection is stablished using the `connectNova` method, we can boot
    nodes. To do so, the config in __imageConfig and __novaConfig applied to
    NovaClient initialization is applied.
    
    :return: S_OK | S_ERROR
    """
    
    self.log.info( "Booting %s / %s" % ( self.__imageConfig.config()[ 'bootImageName' ],
                                         self.__novaConfig.config()[ 'ex_force_auth_url' ] ) )

    result = self.__clinova.create_VMInstance( vmdiracInstanceID, runningPodRequirements )

    if not result[ 'OK' ]:
      self.log.error( "startNewInstance" )
      self.log.error( result[ 'Message' ] )
    return result

  def getInstanceStatus( self, uniqueId ):
    """
    Given the node ID, returns the status. Bear in mind, is the translation of
    the status done by libcloud and then reversed to a string. Its possible values
    are: RUNNING, REBOOTING, TERMINATED, PENDING, UNKNOWN.
    
    :Parameters:
      **uniqueId** - `string`
        node ID, given by the OpenStack service       
    
    :return: S_OK | S_ERROR
    """
    
    result = self.__clinova.getStatus_VMInstance( uniqueId )
    
    if not result[ 'OK' ]:
      self.log.error( "getInstanceStatus: %s" % uniqueId )
      self.log.error( result[ 'Message' ] )
    
    return result  
    
  def stopInstance( self, uniqueId, public_ip ):
    """
    Method that destroys the node and if using floating IPs and frees the floating
    IPs if any.
    
    :Parameters:
      **uniqueId** - `string`
        node ID, given by the OpenStack service   
      **public_ip** - `string`
        public IP of the VM, needed for some setups ( floating IP ).   
    
    :return: S_OK | S_ERROR
    """

    # FIXME: maybe it makes sense to get the public_ip in Nova11 and encapsulate it.
    # FIXME: after all, is an implementation detail.

    result = self.__clinova.terminate_VMinstance( uniqueId, public_ip )
    
    if not result[ 'OK' ]:
      self.log.error( "stopInstance: %s, %s" % ( uniqueId, public_ip ) )
      self.log.error( result[ 'Message' ] )
    
    return result

  def contextualizeInstance( self, uniqueId, public_ip, cpuTime, submitPool ):
    """
    This method is not a regular method in the sense that is not generic at all.
    It will be called only of those VMs which need after-booting contextualisation,
    for the time being, just ssh contextualisation.
    On the other hand, one can say this is the most generic method because you don't
    need any particular "golden" image, like HEPiX, just whatever linux image with 
    available ssh connectivity
        
    :Parameters:
      **uniqueId** - `string`
        node ID, given by the OpenStack service   
      **public_ip** - `string`
        public IP of the VM, needed for asynchronous contextualisation
        
    
    :return: S_OK | S_ERROR
    """

    # FIXME: maybe is worth hiding the public_ip attribute and getting it on
    # FIXME: the contextualize step. 

    result = self.__clinova.contextualize_VMInstance( uniqueId, public_ip, cpuTime, submitPool )
    
    if not result[ 'OK' ]:
      self.log.error( "contextualizeInstance: %s, %s" % ( uniqueId, public_ip ) )
      self.log.error( result[ 'Message' ] )
      return result

    return S_OK( uniqueId )
Пример #3
0
class OcciImage:

  def __init__( self, imageName, endPoint):
    """
    The OCCI Image Interface provides the functionality required to use a standard occi cloud infrastructure.
    Constructor: uses OcciConfiguration to parse the endPoint CS configuration
    and ImageConfiguration to parse the imageName CS configuration.

    :Parameters:
      **imageName** - `string`
        imageName as defined on CS:/Resources/VirtualMachines/Images

      **endPoint** - `string`
        endPoint as defined on CS:/Resources/VirtualMachines/CloudEndpoint

    """
    # logger
    self.log       = gLogger.getSubLogger( 'OcciImage %s: ' % imageName )

    self.imageName = imageName
    self.endPoint  = endPoint

    # their config() method returns a dictionary with the parsed configuration
    # they also provide a validate() method to make sure it is correct
    self.__imageConfig = ImageConfiguration( imageName )
    self.__occiConfig  = OcciConfiguration( endPoint )

    # this object will connect to the server. Better keep it private.
    self.__cliocci   = None
    # error status of the image with the asociated context (endpoing + image), basically for check_connction propagation
    self.__errorStatus   = None

  def connectOcci( self ):
    """
    Method that issues the connection with the OpenNebula server. In order to do
    it, validates the CS configurations. 
    OcciClient which will check the connection.

    :return: S_OK | S_ERROR
    """

    # Before doing anything, make sure the configurations make sense
    # ImageConfiguration
    validImage = self.__imageConfig.validate()
    if not validImage[ 'OK' ]:
      return validImage
    # EndpointConfiguration
    validOcci = self.__occiConfig.validate()
    if not validOcci[ 'OK' ]:
      return validOcci

    # Get authentication configuration
    auth, u, p = self.__occiConfig.authConfig()
    if auth == 'userpasswd':
      user = u
      secret = p
    elif auth == 'proxy':
      userCredPath = u
    else:
      self.__errorStatus = "auth not supported (userpasswd/proxy)" 
      self.log.error( self.__errorStatus )
      return

    # Create the occiclient objects in OcciClient:
    if self.__occiConfig.cloudDriver() == 'occi-0.8':
      if auth == 'userpasswd':
        from VMDIRAC.WorkloadManagementSystem.Client.Occi08 import OcciClient
        self.__cliocci = OcciClient(user, secret, self.__occiConfig.config(), self.__imageConfig.config())
      else:
        self.__errorStatus = "%s is not supported auth method for %s driver" % (auth,self.__occiConfig.cloudDriver())
        self.log.error( self.__errorStatus )
        return S_ERROR( self.__errorStatus )
    elif self.__occiConfig.cloudDriver() == 'occi-0.9':
      if auth == 'userpasswd':
        from VMDIRAC.WorkloadManagementSystem.Client.Occi09 import OcciClient
        self.__cliocci = OcciClient(user, secret, self.__occiConfig.config(), self.__imageConfig.config())
      else:
        self.__errorStatus = "%s is not supported auth method for %s driver" % (auth,self.__occiConfig.cloudDriver())
        self.log.error( self.__errorStatus )
        return S_ERROR( self.__errorStatus )
    elif self.__occiConfig.cloudDriver() == 'rocci-1.1':
      if auth == 'proxy':
        from VMDIRAC.WorkloadManagementSystem.Client.Rocci11 import OcciClient
        self.__cliocci = OcciClient(userCredPath, None, None, self.__occiConfig.config(), self.__imageConfig.config())
      elif auth == 'userpasswd':
        from VMDIRAC.WorkloadManagementSystem.Client.Rocci11 import OcciClient
        self.__cliocci = OcciClient(None, user, secret, self.__occiConfig.config(), self.__imageConfig.config())
      else:
        self.__errorStatus = "%s is not supported auth method for %s driver" % (auth,self.__occiConfig.cloudDriver())
        self.log.error( self.__errorStatus )
        return S_ERROR( self.__errorStatus )
    else:
      self.__errorStatus = "Driver %s not supported" % self.__cloudDriver
      self.log.error( self.__errorStatus )
      return S_ERROR( self.__errorStatus )

    # Check connection to the server
    request = self.__cliocci.check_connection()
    if request.returncode != 0:
      self.__errorStatus = "Can't connect to OCCI URI %s\n%s" % (self.__occiConfig.occiURI(), request.stdout)
      self.log.error( self.__errorStatus )
      return S_ERROR( self.__errorStatus )
    else:
      self.log.info( "Successful connection check to %s" % (self.__occiConfig.occiURI()) )

    return S_OK( request.stdout )

  def startNewInstance( self, cpuTime = None, submitPool = None, runningPodRequirements = None, instanceID = None ):
    """
    Prior to use, virtual machine images are uploaded to the OCCI cloud manager assigned an id (OII in a URI). 
    cpuTime and submitPool have been scheck ad VMDIRECTOR level, used for maintenance ONE 0.8 drivers
    """
    if self.__errorStatus:
      return S_ERROR( self.__errorStatus )

    self.log.info( "Starting new instance for DIRAC image: %s; to endpoint %s" % ( self.imageName, self.endPoint) )
    request = self.__cliocci.create_VMInstance(cpuTime,submitPool,runningPodRequirements,instanceID)
    if request.returncode != 0:
      self.__errorStatus = "Can't create instance for DIRAC image: %s; to endpoint %s" % ( self.imageName, self.endPoint) 
      self.log.error( self.__errorStatus )
      return S_ERROR( self.__errorStatus )

    if self.__occiConfig.cloudDriver() == 'rocci-1.1':
      firstcoma = request.stdout.find(",")
      idVM = request.stdout[0:firstcoma]
      last = len(request.stdout)
      firstcoma += 1
      publicIP = request.stdout[firstcoma:last]
      return S_OK( ( idVM, publicIP ) )
 
    return S_OK( request.stdout )

  def stopInstance( self, VMinstanceId ):
    """
    Simple call to terminate a VM based on its id
    """

    request = self.__cliocci.terminate_VMinstance( VMinstanceId )
    if request.returncode != 0:
      self.__errorStatus = "Can't delete VM instance ide %s from endpoint URL %s\n%s" % (VMinstanceId, self.__occiConfig.occiURI(), request.stdout)
      self.log.error( self.__errorStatus )
      return S_ERROR( self.__errorStatus )

    return S_OK( request.stdout )

  def getInstanceStatus( self, uniqueId ):
    """
    Given the node ID, returns the status. 

    :Parameters:
      **uniqueId** - `string`
        node ID, given by the OpenNebula service

    :return: S_OK | S_ERROR
    """

    request = self.__cliocci.getStatus_VMInstance( uniqueId )

    if request.returncode != 0:
      self.__errorStatus = "getInstanceStatus: %s, msg: %s" % (uniqueId, request.stderr) 
      self.log.error( self.__errorStatus )
      return S_ERROR( self.__errorStatus )

    return S_OK( request.stdout )

  def contextualizeInstance( self, uniqueId, public_ip, cpuTime, submitPool ):
    """
    This method is not a regular method in the sense that is not generic at all.
    It will be called only of those VMs which need after-booting contextualisation,
    for the time being, just ssh contextualisation.
    On the other hand, one can say this is the most generic method because you don't
    need any particular "golden" image, like HEPiX, just whatever linux image with 
    available ssh connectivity

    :Parameters:
      **uniqueId** - `string`
        node ID, given by the OpenNebula service
      **public_ip** - `string`
        public IP of the VM, needed for asynchronous contextualisation


    :return: S_OK | S_ERROR
    """

    return self.__cliocci.contextualize_VMInstance( uniqueId, public_ip, cpuTime, submitPool )