File indexing completed on 2024-12-29 05:27:29

0001 <?php
0002 /**
0003  * @category   Zend
0004  * @package    Zend_Cloud_Infrastructure
0005  * @subpackage Adapter
0006  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0007  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0008  */
0009 
0010 // require_once 'Zend/Service/Rackspace/Servers.php';
0011 // require_once 'Zend/Cloud/Infrastructure/Instance.php';
0012 // require_once 'Zend/Cloud/Infrastructure/InstanceList.php';
0013 // require_once 'Zend/Cloud/Infrastructure/Image.php';
0014 // require_once 'Zend/Cloud/Infrastructure/ImageList.php';
0015 // require_once 'Zend/Cloud/Infrastructure/Adapter/AbstractAdapter.php';
0016 
0017 /**
0018  * Rackspace servers adapter for infrastructure service
0019  *
0020  * @package    Zend_Cloud_Infrastructure
0021  * @subpackage Adapter
0022  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0023  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0024  */
0025 class Zend_Cloud_Infrastructure_Adapter_Rackspace extends Zend_Cloud_Infrastructure_Adapter_AbstractAdapter
0026 {
0027     /**
0028      * RACKSPACE constants
0029      */
0030     const RACKSPACE_USER      = 'rackspace_user';
0031     const RACKSPACE_KEY       = 'rackspace_key';
0032     const RACKSPACE_REGION    = 'rackspace_region';
0033     const RACKSPACE_ZONE_USA  = 'USA';
0034     const RACKSPACE_ZONE_UK   = 'UK';
0035     const MONITOR_CPU_SAMPLES = 3;
0036     /**
0037      * Rackspace Servers Instance 
0038      * 
0039      * @var Zend_Service_Rackspace_Servers
0040      */
0041     protected $rackspace;
0042     /**
0043      * Rackspace access user
0044      * 
0045      * @var string 
0046      */
0047     protected $accessUser;
0048 
0049     /**
0050      * Rackspace access key
0051      * 
0052      * @var string 
0053      */
0054     protected $accessKey;
0055     /**
0056      * Rackspace Region
0057      * 
0058      * @var string 
0059      */
0060     protected $region;
0061     /**
0062      * Flavors
0063      * 
0064      * @var array 
0065      */
0066     protected $flavors;
0067     /**
0068      * Map array between Rackspace and Infrastructure status
0069      * 
0070      * @var array 
0071      */
0072     protected $mapStatus = array (
0073         'ACTIVE'             => Zend_Cloud_Infrastructure_Instance::STATUS_RUNNING,
0074         'SUSPENDED'          => Zend_Cloud_Infrastructure_Instance::STATUS_STOPPED,
0075         'BUILD'              => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
0076         'REBUILD'            => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
0077         'QUEUE_RESIZE'       => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
0078         'PREP_RESIZE'        => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
0079         'RESIZE'             => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
0080         'VERIFY_RESIZE'      => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
0081         'PASSWORD'           => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
0082         'RESCUE'             => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
0083         'REBOOT'             => Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING,
0084         'HARD_REBOOT'        => Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING,
0085         'SHARE_IP'           => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
0086         'SHARE_IP_NO_CONFIG' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
0087         'DELETE_IP'          => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
0088         'UNKNOWN'            => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING
0089     );
0090     /**
0091      * Constructor
0092      *
0093      * @param  array|Zend_Config $options
0094      * @return void
0095      */
0096     public function __construct($options = array())
0097     {
0098         if (is_object($options)) {
0099             if (method_exists($options, 'toArray')) {
0100                 $options= $options->toArray();
0101             } elseif ($options instanceof Traversable) {
0102                 $options = iterator_to_array($options);
0103             }
0104         }
0105         
0106         if (empty($options) || !is_array($options)) {
0107             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0108             throw new Zend_Cloud_Infrastructure_Exception('Invalid options provided');
0109         }
0110         
0111         if (!isset($options[self::RACKSPACE_USER])) {
0112             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0113             throw new Zend_Cloud_Infrastructure_Exception('Rackspace access user not specified!');
0114         }
0115 
0116         if (!isset($options[self::RACKSPACE_KEY])) {
0117             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0118             throw new Zend_Cloud_Infrastructure_Exception('Rackspace access key not specified!');
0119         }
0120         
0121         $this->accessUser = $options[self::RACKSPACE_USER];
0122         $this->accessKey  = $options[self::RACKSPACE_KEY];
0123         
0124         if (isset($options[self::RACKSPACE_REGION])) {
0125             switch ($options[self::RACKSPACE_REGION]) {
0126                 case self::RACKSPACE_ZONE_UK:
0127                     $this->region= Zend_Service_Rackspace_Servers::UK_AUTH_URL;
0128                     break;
0129                 case self::RACKSPACE_ZONE_USA:
0130                     $this->region = Zend_Service_Rackspace_Servers::US_AUTH_URL;
0131                     break;
0132                 default:
0133                     // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0134                     throw new Zend_Cloud_Infrastructure_Exception('The region is not valid');
0135             }
0136         } else {
0137             $this->region = Zend_Service_Rackspace_Servers::US_AUTH_URL;
0138         }
0139 
0140         try {
0141             $this->rackspace = new Zend_Service_Rackspace_Servers($this->accessUser,$this->accessKey, $this->region);
0142         } catch (Exception  $e) {
0143             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0144             throw new Zend_Cloud_Infrastructure_Exception('Error on create: ' . $e->getMessage(), $e->getCode(), $e);
0145         }
0146 
0147         if (isset($options[self::HTTP_ADAPTER])) {
0148             $this->rackspace->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
0149         }
0150         
0151     }
0152     /**
0153      * Convert the attributes of Rackspace server into attributes of Infrastructure
0154      * 
0155      * @param  array $attr
0156      * @return array|boolean 
0157      */
0158     protected function convertAttributes($attr)
0159     {
0160         $result = array();       
0161         if (!empty($attr) && is_array($attr)) {
0162             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ID]      = $attr['id'];
0163             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_NAME]    = $attr['name'];
0164             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STATUS]  = $this->mapStatus[$attr['status']];
0165             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_IMAGEID] = $attr['imageId'];
0166             if ($this->region==Zend_Service_Rackspace_Servers::US_AUTH_URL) {
0167                 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE] = self::RACKSPACE_ZONE_USA;
0168             } else {
0169                 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE] = self::RACKSPACE_ZONE_UK;
0170             }
0171             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM]     = $this->flavors[$attr['flavorId']]['ram'];
0172             $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = $this->flavors[$attr['flavorId']]['disk'];
0173         }
0174         return $result;
0175     }
0176     /**
0177      * Return a list of the available instancies
0178      *
0179      * @return InstanceList|boolean
0180      */ 
0181     public function listInstances() 
0182     {
0183         $this->adapterResult = $this->rackspace->listServers(true);
0184         if ($this->adapterResult===false) {
0185             return false;
0186         }
0187         $array= $this->adapterResult->toArray();
0188         $result = array();
0189         foreach ($array as $instance) {
0190             $result[]= $this->convertAttributes($instance);
0191         }
0192         return new Zend_Cloud_Infrastructure_InstanceList($this, $result);
0193     }
0194     /**
0195      * Return the status of an instance
0196      *
0197      * @param  string
0198      * @return string|boolean
0199      */ 
0200     public function statusInstance($id)
0201     {
0202         $this->adapterResult = $this->rackspace->getServer($id);
0203         if ($this->adapterResult===false) {
0204             return false;
0205         }
0206         $array= $this->adapterResult->toArray();
0207         return $this->mapStatus[$array['status']];
0208     }
0209     /**
0210      * Return the public DNS name/Ip address of the instance
0211      * 
0212      * @param  string $id
0213      * @return string|boolean 
0214      */
0215     public function publicDnsInstance($id) 
0216     {
0217         $this->adapterResult = $this->rackspace->getServerPublicIp($id);
0218         if (empty($this->adapterResult)) {
0219             return false;
0220         }    
0221         return $this->adapterResult[0];
0222     }
0223     /**
0224      * Reboot an instance
0225      *
0226      * @param string $id
0227      * @return boolean
0228      */ 
0229     public function rebootInstance($id)
0230     {
0231         return $this->rackspace->rebootServer($id,true);
0232     }
0233     /**
0234      * Create a new instance
0235      *
0236      * @param string $name
0237      * @param array $options
0238      * @return Instance|boolean
0239      */ 
0240     public function createInstance($name, $options)
0241     {
0242         if (empty($name)) {
0243             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0244             throw new Zend_Cloud_Infrastructure_Exception('You must specify the name of the instance');
0245         }
0246         if (empty($options) || !is_array($options)) {
0247             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0248             throw new Zend_Cloud_Infrastructure_Exception('The options must be an array');
0249         }
0250         // @todo create an generic abstract definition for an instance?
0251         $metadata= array();
0252         if (isset($options['metadata'])) {
0253             $metadata= $options['metadata'];
0254             unset($options['metadata']);
0255         }
0256         $files= array();
0257         if (isset($options['files'])) {
0258             $files= $options['files'];
0259             unset($options['files']);
0260         }
0261         $options['name']= $name;
0262         $this->adapterResult = $this->rackspace->createServer($options,$metadata,$files);
0263         if ($this->adapterResult===false) {
0264             return false;
0265         }
0266         return new Zend_Cloud_Infrastructure_Instance($this, $this->convertAttributes($this->adapterResult->toArray()));
0267     }
0268     /**
0269      * Stop an instance
0270      *
0271      * @param  string $id
0272      * @return boolean
0273      */ 
0274     public function stopInstance($id)
0275     {
0276         // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0277         throw new Zend_Cloud_Infrastructure_Exception('The stopInstance method is not implemented in the adapter');
0278     }
0279  
0280     /**
0281      * Start an instance
0282      *
0283      * @param  string $id
0284      * @return boolean
0285      */ 
0286     public function startInstance($id)
0287     {
0288         // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0289         throw new Zend_Cloud_Infrastructure_Exception('The startInstance method is not implemented in the adapter');
0290     }
0291  
0292     /**
0293      * Destroy an instance
0294      *
0295      * @param  string $id
0296      * @return boolean
0297      */ 
0298     public function destroyInstance($id)
0299     {
0300         $this->adapterResult= $this->rackspace->deleteServer($id);
0301         return $this->adapterResult;
0302     }
0303     /**
0304      * Return a list of all the available instance images
0305      *
0306      * @return ImageList|boolean
0307      */ 
0308     public function imagesInstance()
0309     {
0310         $this->adapterResult = $this->rackspace->listImages(true);
0311         if ($this->adapterResult===false) {
0312             return false;
0313         }
0314         
0315         $images= $this->adapterResult->toArray();
0316         $result= array();
0317         
0318         foreach ($images as $image) {
0319             if (strtolower($image['status'])==='active') {
0320                 if (strpos($image['name'],'Windows')!==false) {
0321                     $platform = Zend_Cloud_Infrastructure_Image::IMAGE_WINDOWS;
0322                 } else {
0323                     $platform = Zend_Cloud_Infrastructure_Image::IMAGE_LINUX;
0324                 }
0325                 if (strpos($image['name'],'x64')!==false) {
0326                     $arch = Zend_Cloud_Infrastructure_Image::ARCH_64BIT;
0327                 } else {
0328                     $arch = Zend_Cloud_Infrastructure_Image::ARCH_32BIT;
0329                 }
0330                 $result[]= array (
0331                     Zend_Cloud_Infrastructure_Image::IMAGE_ID           => $image['id'],
0332                     Zend_Cloud_Infrastructure_Image::IMAGE_NAME         => $image['name'],
0333                     Zend_Cloud_Infrastructure_Image::IMAGE_DESCRIPTION  => $image['name'],
0334                     Zend_Cloud_Infrastructure_Image::IMAGE_ARCHITECTURE => $arch,
0335                     Zend_Cloud_Infrastructure_Image::IMAGE_PLATFORM     => $platform,
0336                 );
0337             }
0338         }
0339         return new Zend_Cloud_Infrastructure_ImageList($result,$this->adapterResult);
0340     }
0341     /**
0342      * Return all the available zones
0343      * 
0344      * @return array
0345      */
0346     public function zonesInstance()
0347     {
0348         return array(self::RACKSPACE_ZONE_USA,self::RACKSPACE_ZONE_UK);
0349     }
0350     /**
0351      * Return the system information about the $metric of an instance
0352      * NOTE: it works only for Linux servers
0353      * 
0354      * @param  string $id
0355      * @param  string $metric
0356      * @param  null|array $options
0357      * @return array|boolean
0358      */ 
0359     public function monitorInstance($id, $metric, $options = null)
0360     {
0361         if (!function_exists("ssh2_connect")) {
0362             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0363             throw new Zend_Cloud_Infrastructure_Exception('Monitor requires the PHP "SSH" extension (ext/ssh2)');
0364         }
0365         if (empty($id)) {
0366             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0367             throw new Zend_Cloud_Infrastructure_Exception('You must specify the id of the instance to monitor');
0368         }
0369         if (empty($metric)) {
0370             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0371             throw new Zend_Cloud_Infrastructure_Exception('You must specify the metric to monitor');
0372         }
0373         if (!in_array($metric,$this->validMetrics)) {
0374             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0375             throw new Zend_Cloud_Infrastructure_Exception(sprintf('The metric "%s" is not valid', $metric));
0376         }
0377         if (!empty($options) && !is_array($options)) {
0378             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0379             throw new Zend_Cloud_Infrastructure_Exception('The options must be an array');
0380         }
0381         
0382         switch ($metric) {
0383             case Zend_Cloud_Infrastructure_Instance::MONITOR_CPU:
0384                 $cmd= 'top -b -n '.self::MONITOR_CPU_SAMPLES.' | grep \'Cpu\'';
0385                 break;
0386             case Zend_Cloud_Infrastructure_Instance::MONITOR_RAM:
0387                 $cmd= 'top -b -n 1 | grep \'Mem\'';
0388                 break;
0389             case Zend_Cloud_Infrastructure_Instance::MONITOR_DISK:
0390                 $cmd= 'df --total | grep total';
0391                 break;
0392         }
0393         if (empty($cmd)) {
0394             // require_once 'Zend/Cloud/Infrastructure/Exception.php';
0395             throw new Zend_Cloud_Infrastructure_Exception('The metric specified is not supported by the adapter');
0396         }
0397         
0398         $params= array(
0399             Zend_Cloud_Infrastructure_Instance::SSH_USERNAME => $options['username'],
0400             Zend_Cloud_Infrastructure_Instance::SSH_PASSWORD => $options['password']
0401         );
0402         $exec_time= time();
0403         $result= $this->deployInstance($id,$params,$cmd);
0404         
0405         if (empty($result)) {
0406             return false;
0407         }
0408 
0409         $monitor = array();
0410         $num     = 0;
0411         $average = 0;
0412 
0413         $outputs= explode("\n",$result);
0414         foreach ($outputs as $output) {
0415             if (!empty($output)) {
0416                 switch ($metric) {
0417                     case Zend_Cloud_Infrastructure_Instance::MONITOR_CPU:
0418                         if (preg_match('/(\d+\.\d)%us/', $output,$match)) {
0419                             $usage = (float) $match[1];
0420                         }
0421                         break;
0422                     case Zend_Cloud_Infrastructure_Instance::MONITOR_RAM:
0423                         if (preg_match('/(\d+)k total/', $output,$match)) {
0424                             $total = (integer) $match[1];
0425                         }
0426                         if (preg_match('/(\d+)k used/', $output,$match)) {
0427                             $used = (integer) $match[1];
0428                         }
0429                         if ($total>0) {
0430                             $usage= (float) $used/$total;
0431                         }    
0432                         break;
0433                     case Zend_Cloud_Infrastructure_Instance::MONITOR_DISK:
0434                         if (preg_match('/(\d+)%/', $output,$match)) {
0435                             $usage = (float) $match[1];
0436                         }
0437                         break;
0438                 }
0439                 
0440                 $monitor['series'][] = array (
0441                     'timestamp' => $exec_time,
0442                     'value'     => number_format($usage,2).'%'
0443                 );
0444                 
0445                 $average += $usage;
0446                 $exec_time+= 60; // seconds
0447                 $num++;
0448             }
0449         }
0450         
0451         if ($num>0) {
0452             $monitor['average'] = number_format($average/$num,2).'%';
0453         }
0454         return $monitor;
0455     }
0456     /**
0457      * Get the adapter 
0458      * 
0459      * @return Zend_Service_Rackspace_Servers
0460      */
0461     public function getAdapter()
0462     {
0463         return $this->rackspace;
0464     }
0465     /**
0466      * Get last HTTP request
0467      * 
0468      * @return string 
0469      */
0470     public function getLastHttpRequest()
0471     {
0472         return $this->rackspace->getHttpClient()->getLastRequest();
0473     }
0474     /**
0475      * Get the last HTTP response
0476      * 
0477      * @return Zend_Http_Response 
0478      */
0479     public function getLastHttpResponse()
0480     {
0481         return $this->rackspace->getHttpClient()->getLastResponse();
0482     }
0483 }