web/lib/Zend/Cloud/Infrastructure/Adapter/Rackspace.php
changeset 808 6b6c2214f778
child 1230 68c69c656a2c
equal deleted inserted replaced
807:877f952ae2bd 808:6b6c2214f778
       
     1 <?php
       
     2 /**
       
     3  * @category   Zend
       
     4  * @package    Zend_Cloud_Infrastructure
       
     5  * @subpackage Adapter
       
     6  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
       
     7  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
     8  */
       
     9 
       
    10 require_once 'Zend/Service/Rackspace/Servers.php';
       
    11 require_once 'Zend/Cloud/Infrastructure/Instance.php';
       
    12 require_once 'Zend/Cloud/Infrastructure/InstanceList.php';
       
    13 require_once 'Zend/Cloud/Infrastructure/Image.php';
       
    14 require_once 'Zend/Cloud/Infrastructure/ImageList.php';
       
    15 require_once 'Zend/Cloud/Infrastructure/Adapter/AbstractAdapter.php';
       
    16 
       
    17 /**
       
    18  * Rackspace servers adapter for infrastructure service
       
    19  *
       
    20  * @package    Zend_Cloud_Infrastructure
       
    21  * @subpackage Adapter
       
    22  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
       
    23  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    24  */
       
    25 class Zend_Cloud_Infrastructure_Adapter_Rackspace extends Zend_Cloud_Infrastructure_Adapter_AbstractAdapter
       
    26 {
       
    27     /**
       
    28      * RACKSPACE constants
       
    29      */
       
    30     const RACKSPACE_USER      = 'rackspace_user';
       
    31     const RACKSPACE_KEY       = 'rackspace_key';
       
    32     const RACKSPACE_REGION    = 'rackspace_region';
       
    33     const RACKSPACE_ZONE_USA  = 'USA';
       
    34     const RACKSPACE_ZONE_UK   = 'UK';
       
    35     const MONITOR_CPU_SAMPLES = 3;
       
    36     /**
       
    37      * Rackspace Servers Instance 
       
    38      * 
       
    39      * @var Zend_Service_Rackspace_Servers
       
    40      */
       
    41     protected $rackspace;
       
    42     /**
       
    43      * Rackspace access user
       
    44      * 
       
    45      * @var string 
       
    46      */
       
    47     protected $accessUser;
       
    48 
       
    49     /**
       
    50      * Rackspace access key
       
    51      * 
       
    52      * @var string 
       
    53      */
       
    54     protected $accessKey;
       
    55     /**
       
    56      * Rackspace Region
       
    57      * 
       
    58      * @var string 
       
    59      */
       
    60     protected $region;
       
    61     /**
       
    62      * Flavors
       
    63      * 
       
    64      * @var array 
       
    65      */
       
    66     protected $flavors;
       
    67     /**
       
    68      * Map array between Rackspace and Infrastructure status
       
    69      * 
       
    70      * @var array 
       
    71      */
       
    72     protected $mapStatus = array (
       
    73         'ACTIVE'             => Zend_Cloud_Infrastructure_Instance::STATUS_RUNNING,
       
    74         'SUSPENDED'          => Zend_Cloud_Infrastructure_Instance::STATUS_STOPPED,
       
    75         'BUILD'              => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
       
    76         'REBUILD'            => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
       
    77         'QUEUE_RESIZE'       => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
       
    78         'PREP_RESIZE'        => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
       
    79         'RESIZE'             => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
       
    80         'VERIFY_RESIZE'      => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
       
    81         'PASSWORD'           => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
       
    82         'RESCUE'             => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
       
    83         'REBOOT'             => Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING,
       
    84         'HARD_REBOOT'        => Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING,
       
    85         'SHARE_IP'           => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
       
    86         'SHARE_IP_NO_CONFIG' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
       
    87         'DELETE_IP'          => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
       
    88         'UNKNOWN'            => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING
       
    89     );
       
    90     /**
       
    91      * Constructor
       
    92      *
       
    93      * @param  array|Zend_Config $options
       
    94      * @return void
       
    95      */
       
    96     public function __construct($options = array())
       
    97     {
       
    98         if (is_object($options)) {
       
    99             if (method_exists($options, 'toArray')) {
       
   100                 $options= $options->toArray();
       
   101             } elseif ($options instanceof Traversable) {
       
   102                 $options = iterator_to_array($options);
       
   103             }
       
   104         }
       
   105         
       
   106         if (empty($options) || !is_array($options)) {
       
   107             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   108             throw new Zend_Cloud_Infrastructure_Exception('Invalid options provided');
       
   109         }
       
   110         
       
   111         if (!isset($options[self::RACKSPACE_USER])) {
       
   112             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   113             throw new Zend_Cloud_Infrastructure_Exception('Rackspace access user not specified!');
       
   114         }
       
   115 
       
   116         if (!isset($options[self::RACKSPACE_KEY])) {
       
   117             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   118             throw new Zend_Cloud_Infrastructure_Exception('Rackspace access key not specified!');
       
   119         }
       
   120         
       
   121         $this->accessUser = $options[self::RACKSPACE_USER];
       
   122         $this->accessKey  = $options[self::RACKSPACE_KEY];
       
   123         
       
   124         if (isset($options[self::RACKSPACE_REGION])) {
       
   125             switch ($options[self::RACKSPACE_REGION]) {
       
   126                 case self::RACKSPACE_ZONE_UK:
       
   127                     $this->region= Zend_Service_Rackspace_Servers::UK_AUTH_URL;
       
   128                     break;
       
   129                 case self::RACKSPACE_ZONE_USA:
       
   130                     $this->region = Zend_Service_Rackspace_Servers::US_AUTH_URL;
       
   131                     break;
       
   132                 default:
       
   133                     require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   134                     throw new Zend_Cloud_Infrastructure_Exception('The region is not valid');
       
   135             }
       
   136         } else {
       
   137             $this->region = Zend_Service_Rackspace_Servers::US_AUTH_URL;
       
   138         }
       
   139 
       
   140         try {
       
   141             $this->rackspace = new Zend_Service_Rackspace_Servers($this->accessUser,$this->accessKey, $this->region);
       
   142         } catch (Exception  $e) {
       
   143             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   144             throw new Zend_Cloud_Infrastructure_Exception('Error on create: ' . $e->getMessage(), $e->getCode(), $e);
       
   145         }
       
   146 
       
   147         if (isset($options[self::HTTP_ADAPTER])) {
       
   148             $this->rackspace->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
       
   149         }
       
   150         
       
   151     }
       
   152     /**
       
   153      * Convert the attributes of Rackspace server into attributes of Infrastructure
       
   154      * 
       
   155      * @param  array $attr
       
   156      * @return array|boolean 
       
   157      */
       
   158     protected function convertAttributes($attr)
       
   159     {
       
   160         $result = array();       
       
   161         if (!empty($attr) && is_array($attr)) {
       
   162             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ID]      = $attr['id'];
       
   163             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_NAME]    = $attr['name'];
       
   164             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STATUS]  = $this->mapStatus[$attr['status']];
       
   165             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_IMAGEID] = $attr['imageId'];
       
   166             if ($this->region==Zend_Service_Rackspace_Servers::US_AUTH_URL) {
       
   167                 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE] = self::RACKSPACE_ZONE_USA;
       
   168             } else {
       
   169                 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE] = self::RACKSPACE_ZONE_UK;
       
   170             }
       
   171             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM]     = $this->flavors[$attr['flavorId']]['ram'];
       
   172             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = $this->flavors[$attr['flavorId']]['disk'];
       
   173         }
       
   174         return $result;
       
   175     }
       
   176     /**
       
   177      * Return a list of the available instancies
       
   178      *
       
   179      * @return InstanceList|boolean
       
   180      */ 
       
   181     public function listInstances() 
       
   182     {
       
   183         $this->adapterResult = $this->rackspace->listServers(true);
       
   184         if ($this->adapterResult===false) {
       
   185             return false;
       
   186         }
       
   187         $array= $this->adapterResult->toArray();
       
   188         $result = array();
       
   189         foreach ($array as $instance) {
       
   190             $result[]= $this->convertAttributes($instance);
       
   191         }
       
   192         return new Zend_Cloud_Infrastructure_InstanceList($this, $result);
       
   193     }
       
   194     /**
       
   195      * Return the status of an instance
       
   196      *
       
   197      * @param  string
       
   198      * @return string|boolean
       
   199      */ 
       
   200     public function statusInstance($id)
       
   201     {
       
   202         $this->adapterResult = $this->rackspace->getServer($id);
       
   203         if ($this->adapterResult===false) {
       
   204             return false;
       
   205         }
       
   206         $array= $this->adapterResult->toArray();
       
   207         return $this->mapStatus[$array['status']];
       
   208     }
       
   209     /**
       
   210      * Return the public DNS name/Ip address of the instance
       
   211      * 
       
   212      * @param  string $id
       
   213      * @return string|boolean 
       
   214      */
       
   215     public function publicDnsInstance($id) 
       
   216     {
       
   217         $this->adapterResult = $this->rackspace->getServerPublicIp($id);
       
   218         if (empty($this->adapterResult)) {
       
   219             return false;
       
   220         }    
       
   221         return $this->adapterResult[0];
       
   222     }
       
   223     /**
       
   224      * Reboot an instance
       
   225      *
       
   226      * @param string $id
       
   227      * @return boolean
       
   228      */ 
       
   229     public function rebootInstance($id)
       
   230     {
       
   231         return $this->rackspace->rebootServer($id,true);
       
   232     }
       
   233     /**
       
   234      * Create a new instance
       
   235      *
       
   236      * @param string $name
       
   237      * @param array $options
       
   238      * @return Instance|boolean
       
   239      */ 
       
   240     public function createInstance($name, $options)
       
   241     {
       
   242         if (empty($name)) {
       
   243             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   244             throw new Zend_Cloud_Infrastructure_Exception('You must specify the name of the instance');
       
   245         }
       
   246         if (empty($options) || !is_array($options)) {
       
   247             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   248             throw new Zend_Cloud_Infrastructure_Exception('The options must be an array');
       
   249         }
       
   250         // @todo create an generic abstract definition for an instance?
       
   251         $metadata= array();
       
   252         if (isset($options['metadata'])) {
       
   253             $metadata= $options['metadata'];
       
   254             unset($options['metadata']);
       
   255         }
       
   256         $files= array();
       
   257         if (isset($options['files'])) {
       
   258             $files= $options['files'];
       
   259             unset($options['files']);
       
   260         }
       
   261         $options['name']= $name;
       
   262         $this->adapterResult = $this->rackspace->createServer($options,$metadata,$files);
       
   263         if ($this->adapterResult===false) {
       
   264             return false;
       
   265         }
       
   266         return new Zend_Cloud_Infrastructure_Instance($this, $this->convertAttributes($this->adapterResult->toArray()));
       
   267     }
       
   268     /**
       
   269      * Stop an instance
       
   270      *
       
   271      * @param  string $id
       
   272      * @return boolean
       
   273      */ 
       
   274     public function stopInstance($id)
       
   275     {
       
   276         require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   277         throw new Zend_Cloud_Infrastructure_Exception('The stopInstance method is not implemented in the adapter');
       
   278     }
       
   279  
       
   280     /**
       
   281      * Start an instance
       
   282      *
       
   283      * @param  string $id
       
   284      * @return boolean
       
   285      */ 
       
   286     public function startInstance($id)
       
   287     {
       
   288         require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   289         throw new Zend_Cloud_Infrastructure_Exception('The startInstance method is not implemented in the adapter');
       
   290     }
       
   291  
       
   292     /**
       
   293      * Destroy an instance
       
   294      *
       
   295      * @param  string $id
       
   296      * @return boolean
       
   297      */ 
       
   298     public function destroyInstance($id)
       
   299     {
       
   300         $this->adapterResult= $this->rackspace->deleteServer($id);
       
   301         return $this->adapterResult;
       
   302     }
       
   303     /**
       
   304      * Return a list of all the available instance images
       
   305      *
       
   306      * @return ImageList|boolean
       
   307      */ 
       
   308     public function imagesInstance()
       
   309     {
       
   310         $this->adapterResult = $this->rackspace->listImages(true);
       
   311         if ($this->adapterResult===false) {
       
   312             return false;
       
   313         }
       
   314         
       
   315         $images= $this->adapterResult->toArray();
       
   316         $result= array();
       
   317         
       
   318         foreach ($images as $image) {
       
   319             if (strtolower($image['status'])==='active') {
       
   320                 if (strpos($image['name'],'Windows')!==false) {
       
   321                     $platform = Zend_Cloud_Infrastructure_Image::IMAGE_WINDOWS;
       
   322                 } else {
       
   323                     $platform = Zend_Cloud_Infrastructure_Image::IMAGE_LINUX;
       
   324                 }
       
   325                 if (strpos($image['name'],'x64')!==false) {
       
   326                     $arch = Zend_Cloud_Infrastructure_Image::ARCH_64BIT;
       
   327                 } else {
       
   328                     $arch = Zend_Cloud_Infrastructure_Image::ARCH_32BIT;
       
   329                 }
       
   330                 $result[]= array (
       
   331                     Zend_Cloud_Infrastructure_Image::IMAGE_ID           => $image['id'],
       
   332                     Zend_Cloud_Infrastructure_Image::IMAGE_NAME         => $image['name'],
       
   333                     Zend_Cloud_Infrastructure_Image::IMAGE_DESCRIPTION  => $image['name'],
       
   334                     Zend_Cloud_Infrastructure_Image::IMAGE_ARCHITECTURE => $arch,
       
   335                     Zend_Cloud_Infrastructure_Image::IMAGE_PLATFORM     => $platform,
       
   336                 );
       
   337             }
       
   338         }
       
   339         return new Zend_Cloud_Infrastructure_ImageList($result,$this->adapterResult);
       
   340     }
       
   341     /**
       
   342      * Return all the available zones
       
   343      * 
       
   344      * @return array
       
   345      */
       
   346     public function zonesInstance()
       
   347     {
       
   348         return array(self::RACKSPACE_ZONE_USA,self::RACKSPACE_ZONE_UK);
       
   349     }
       
   350     /**
       
   351      * Return the system information about the $metric of an instance
       
   352      * NOTE: it works only for Linux servers
       
   353      * 
       
   354      * @param  string $id
       
   355      * @param  string $metric
       
   356      * @param  null|array $options
       
   357      * @return array|boolean
       
   358      */ 
       
   359     public function monitorInstance($id, $metric, $options = null)
       
   360     {
       
   361         if (!function_exists("ssh2_connect")) {
       
   362             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   363             throw new Zend_Cloud_Infrastructure_Exception('Monitor requires the PHP "SSH" extension (ext/ssh2)');
       
   364         }
       
   365         if (empty($id)) {
       
   366             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   367             throw new Zend_Cloud_Infrastructure_Exception('You must specify the id of the instance to monitor');
       
   368         }
       
   369         if (empty($metric)) {
       
   370             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   371             throw new Zend_Cloud_Infrastructure_Exception('You must specify the metric to monitor');
       
   372         }
       
   373         if (!in_array($metric,$this->validMetrics)) {
       
   374             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   375             throw new Zend_Cloud_Infrastructure_Exception(sprintf('The metric "%s" is not valid', $metric));
       
   376         }
       
   377         if (!empty($options) && !is_array($options)) {
       
   378             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   379             throw new Zend_Cloud_Infrastructure_Exception('The options must be an array');
       
   380         }
       
   381         
       
   382         switch ($metric) {
       
   383             case Zend_Cloud_Infrastructure_Instance::MONITOR_CPU:
       
   384                 $cmd= 'top -b -n '.self::MONITOR_CPU_SAMPLES.' | grep \'Cpu\'';
       
   385                 break;
       
   386             case Zend_Cloud_Infrastructure_Instance::MONITOR_RAM:
       
   387                 $cmd= 'top -b -n 1 | grep \'Mem\'';
       
   388                 break;
       
   389             case Zend_Cloud_Infrastructure_Instance::MONITOR_DISK:
       
   390                 $cmd= 'df --total | grep total';
       
   391                 break;
       
   392         }
       
   393         if (empty($cmd)) {
       
   394             require_once 'Zend/Cloud/Infrastructure/Exception.php';
       
   395             throw new Zend_Cloud_Infrastructure_Exception('The metric specified is not supported by the adapter');
       
   396         }
       
   397         
       
   398         $params= array(
       
   399             Zend_Cloud_Infrastructure_Instance::SSH_USERNAME => $options['username'],
       
   400             Zend_Cloud_Infrastructure_Instance::SSH_PASSWORD => $options['password']
       
   401         );
       
   402         $exec_time= time();
       
   403         $result= $this->deployInstance($id,$params,$cmd);
       
   404         
       
   405         if (empty($result)) {
       
   406             return false;
       
   407         }
       
   408 
       
   409         $monitor = array();
       
   410         $num     = 0;
       
   411         $average = 0;
       
   412 
       
   413         $outputs= explode("\n",$result);
       
   414         foreach ($outputs as $output) {
       
   415             if (!empty($output)) {
       
   416                 switch ($metric) {
       
   417                     case Zend_Cloud_Infrastructure_Instance::MONITOR_CPU:
       
   418                         if (preg_match('/(\d+\.\d)%us/', $output,$match)) {
       
   419                             $usage = (float) $match[1];
       
   420                         }
       
   421                         break;
       
   422                     case Zend_Cloud_Infrastructure_Instance::MONITOR_RAM:
       
   423                         if (preg_match('/(\d+)k total/', $output,$match)) {
       
   424                             $total = (integer) $match[1];
       
   425                         }
       
   426                         if (preg_match('/(\d+)k used/', $output,$match)) {
       
   427                             $used = (integer) $match[1];
       
   428                         }
       
   429                         if ($total>0) {
       
   430                             $usage= (float) $used/$total;
       
   431                         }    
       
   432                         break;
       
   433                     case Zend_Cloud_Infrastructure_Instance::MONITOR_DISK:
       
   434                         if (preg_match('/(\d+)%/', $output,$match)) {
       
   435                             $usage = (float) $match[1];
       
   436                         }
       
   437                         break;
       
   438                 }
       
   439                 
       
   440                 $monitor['series'][] = array (
       
   441                     'timestamp' => $exec_time,
       
   442                     'value'     => number_format($usage,2).'%'
       
   443                 );
       
   444                 
       
   445                 $average += $usage;
       
   446                 $exec_time+= 60; // seconds
       
   447                 $num++;
       
   448             }
       
   449         }
       
   450         
       
   451         if ($num>0) {
       
   452             $monitor['average'] = number_format($average/$num,2).'%';
       
   453         }
       
   454         return $monitor;
       
   455     }
       
   456     /**
       
   457      * Get the adapter 
       
   458      * 
       
   459      * @return Zend_Service_Rackspace_Servers
       
   460      */
       
   461     public function getAdapter()
       
   462     {
       
   463         return $this->rackspace;
       
   464     }
       
   465     /**
       
   466      * Get last HTTP request
       
   467      * 
       
   468      * @return string 
       
   469      */
       
   470     public function getLastHttpRequest()
       
   471     {
       
   472         return $this->rackspace->getHttpClient()->getLastRequest();
       
   473     }
       
   474     /**
       
   475      * Get the last HTTP response
       
   476      * 
       
   477      * @return Zend_Http_Response 
       
   478      */
       
   479     public function getLastHttpResponse()
       
   480     {
       
   481         return $this->rackspace->getHttpClient()->getLastResponse();
       
   482     }
       
   483 }