2012-09-11 13:16:30 +00:00

484 lines
17 KiB
PHP

<?php
/**
* @category Zend
* @package Zend_Cloud_Infrastructure
* @subpackage Adapter
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
require_once 'Zend/Service/Rackspace/Servers.php';
require_once 'Zend/Cloud/Infrastructure/Instance.php';
require_once 'Zend/Cloud/Infrastructure/InstanceList.php';
require_once 'Zend/Cloud/Infrastructure/Image.php';
require_once 'Zend/Cloud/Infrastructure/ImageList.php';
require_once 'Zend/Cloud/Infrastructure/Adapter/AbstractAdapter.php';
/**
* Rackspace servers adapter for infrastructure service
*
* @package Zend_Cloud_Infrastructure
* @subpackage Adapter
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Cloud_Infrastructure_Adapter_Rackspace extends Zend_Cloud_Infrastructure_Adapter_AbstractAdapter
{
/**
* RACKSPACE constants
*/
const RACKSPACE_USER = 'rackspace_user';
const RACKSPACE_KEY = 'rackspace_key';
const RACKSPACE_REGION = 'rackspace_region';
const RACKSPACE_ZONE_USA = 'USA';
const RACKSPACE_ZONE_UK = 'UK';
const MONITOR_CPU_SAMPLES = 3;
/**
* Rackspace Servers Instance
*
* @var Zend_Service_Rackspace_Servers
*/
protected $rackspace;
/**
* Rackspace access user
*
* @var string
*/
protected $accessUser;
/**
* Rackspace access key
*
* @var string
*/
protected $accessKey;
/**
* Rackspace Region
*
* @var string
*/
protected $region;
/**
* Flavors
*
* @var array
*/
protected $flavors;
/**
* Map array between Rackspace and Infrastructure status
*
* @var array
*/
protected $mapStatus = array (
'ACTIVE' => Zend_Cloud_Infrastructure_Instance::STATUS_RUNNING,
'SUSPENDED' => Zend_Cloud_Infrastructure_Instance::STATUS_STOPPED,
'BUILD' => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
'REBUILD' => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
'QUEUE_RESIZE' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
'PREP_RESIZE' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
'RESIZE' => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
'VERIFY_RESIZE' => Zend_Cloud_Infrastructure_Instance::STATUS_REBUILD,
'PASSWORD' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
'RESCUE' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
'REBOOT' => Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING,
'HARD_REBOOT' => Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING,
'SHARE_IP' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
'SHARE_IP_NO_CONFIG' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
'DELETE_IP' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
'UNKNOWN' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING
);
/**
* Constructor
*
* @param array|Zend_Config $options
* @return void
*/
public function __construct($options = array())
{
if (is_object($options)) {
if (method_exists($options, 'toArray')) {
$options= $options->toArray();
} elseif ($options instanceof Traversable) {
$options = iterator_to_array($options);
}
}
if (empty($options) || !is_array($options)) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('Invalid options provided');
}
if (!isset($options[self::RACKSPACE_USER])) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('Rackspace access user not specified!');
}
if (!isset($options[self::RACKSPACE_KEY])) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('Rackspace access key not specified!');
}
$this->accessUser = $options[self::RACKSPACE_USER];
$this->accessKey = $options[self::RACKSPACE_KEY];
if (isset($options[self::RACKSPACE_REGION])) {
switch ($options[self::RACKSPACE_REGION]) {
case self::RACKSPACE_ZONE_UK:
$this->region= Zend_Service_Rackspace_Servers::UK_AUTH_URL;
break;
case self::RACKSPACE_ZONE_USA:
$this->region = Zend_Service_Rackspace_Servers::US_AUTH_URL;
break;
default:
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('The region is not valid');
}
} else {
$this->region = Zend_Service_Rackspace_Servers::US_AUTH_URL;
}
try {
$this->rackspace = new Zend_Service_Rackspace_Servers($this->accessUser,$this->accessKey, $this->region);
} catch (Exception $e) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('Error on create: ' . $e->getMessage(), $e->getCode(), $e);
}
if (isset($options[self::HTTP_ADAPTER])) {
$this->rackspace->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
}
}
/**
* Convert the attributes of Rackspace server into attributes of Infrastructure
*
* @param array $attr
* @return array|boolean
*/
protected function convertAttributes($attr)
{
$result = array();
if (!empty($attr) && is_array($attr)) {
$result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ID] = $attr['id'];
$result[Zend_Cloud_Infrastructure_Instance::INSTANCE_NAME] = $attr['name'];
$result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STATUS] = $this->mapStatus[$attr['status']];
$result[Zend_Cloud_Infrastructure_Instance::INSTANCE_IMAGEID] = $attr['imageId'];
if ($this->region==Zend_Service_Rackspace_Servers::US_AUTH_URL) {
$result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE] = self::RACKSPACE_ZONE_USA;
} else {
$result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE] = self::RACKSPACE_ZONE_UK;
}
$result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM] = $this->flavors[$attr['flavorId']]['ram'];
$result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = $this->flavors[$attr['flavorId']]['disk'];
}
return $result;
}
/**
* Return a list of the available instancies
*
* @return InstanceList|boolean
*/
public function listInstances()
{
$this->adapterResult = $this->rackspace->listServers(true);
if ($this->adapterResult===false) {
return false;
}
$array= $this->adapterResult->toArray();
$result = array();
foreach ($array as $instance) {
$result[]= $this->convertAttributes($instance);
}
return new Zend_Cloud_Infrastructure_InstanceList($this, $result);
}
/**
* Return the status of an instance
*
* @param string
* @return string|boolean
*/
public function statusInstance($id)
{
$this->adapterResult = $this->rackspace->getServer($id);
if ($this->adapterResult===false) {
return false;
}
$array= $this->adapterResult->toArray();
return $this->mapStatus[$array['status']];
}
/**
* Return the public DNS name/Ip address of the instance
*
* @param string $id
* @return string|boolean
*/
public function publicDnsInstance($id)
{
$this->adapterResult = $this->rackspace->getServerPublicIp($id);
if (empty($this->adapterResult)) {
return false;
}
return $this->adapterResult[0];
}
/**
* Reboot an instance
*
* @param string $id
* @return boolean
*/
public function rebootInstance($id)
{
return $this->rackspace->rebootServer($id,true);
}
/**
* Create a new instance
*
* @param string $name
* @param array $options
* @return Instance|boolean
*/
public function createInstance($name, $options)
{
if (empty($name)) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('You must specify the name of the instance');
}
if (empty($options) || !is_array($options)) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('The options must be an array');
}
// @todo create an generic abstract definition for an instance?
$metadata= array();
if (isset($options['metadata'])) {
$metadata= $options['metadata'];
unset($options['metadata']);
}
$files= array();
if (isset($options['files'])) {
$files= $options['files'];
unset($options['files']);
}
$options['name']= $name;
$this->adapterResult = $this->rackspace->createServer($options,$metadata,$files);
if ($this->adapterResult===false) {
return false;
}
return new Zend_Cloud_Infrastructure_Instance($this, $this->convertAttributes($this->adapterResult->toArray()));
}
/**
* Stop an instance
*
* @param string $id
* @return boolean
*/
public function stopInstance($id)
{
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('The stopInstance method is not implemented in the adapter');
}
/**
* Start an instance
*
* @param string $id
* @return boolean
*/
public function startInstance($id)
{
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('The startInstance method is not implemented in the adapter');
}
/**
* Destroy an instance
*
* @param string $id
* @return boolean
*/
public function destroyInstance($id)
{
$this->adapterResult= $this->rackspace->deleteServer($id);
return $this->adapterResult;
}
/**
* Return a list of all the available instance images
*
* @return ImageList|boolean
*/
public function imagesInstance()
{
$this->adapterResult = $this->rackspace->listImages(true);
if ($this->adapterResult===false) {
return false;
}
$images= $this->adapterResult->toArray();
$result= array();
foreach ($images as $image) {
if (strtolower($image['status'])==='active') {
if (strpos($image['name'],'Windows')!==false) {
$platform = Zend_Cloud_Infrastructure_Image::IMAGE_WINDOWS;
} else {
$platform = Zend_Cloud_Infrastructure_Image::IMAGE_LINUX;
}
if (strpos($image['name'],'x64')!==false) {
$arch = Zend_Cloud_Infrastructure_Image::ARCH_64BIT;
} else {
$arch = Zend_Cloud_Infrastructure_Image::ARCH_32BIT;
}
$result[]= array (
Zend_Cloud_Infrastructure_Image::IMAGE_ID => $image['id'],
Zend_Cloud_Infrastructure_Image::IMAGE_NAME => $image['name'],
Zend_Cloud_Infrastructure_Image::IMAGE_DESCRIPTION => $image['name'],
Zend_Cloud_Infrastructure_Image::IMAGE_ARCHITECTURE => $arch,
Zend_Cloud_Infrastructure_Image::IMAGE_PLATFORM => $platform,
);
}
}
return new Zend_Cloud_Infrastructure_ImageList($result,$this->adapterResult);
}
/**
* Return all the available zones
*
* @return array
*/
public function zonesInstance()
{
return array(self::RACKSPACE_ZONE_USA,self::RACKSPACE_ZONE_UK);
}
/**
* Return the system information about the $metric of an instance
* NOTE: it works only for Linux servers
*
* @param string $id
* @param string $metric
* @param null|array $options
* @return array|boolean
*/
public function monitorInstance($id, $metric, $options = null)
{
if (!function_exists("ssh2_connect")) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('Monitor requires the PHP "SSH" extension (ext/ssh2)');
}
if (empty($id)) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('You must specify the id of the instance to monitor');
}
if (empty($metric)) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('You must specify the metric to monitor');
}
if (!in_array($metric,$this->validMetrics)) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception(sprintf('The metric "%s" is not valid', $metric));
}
if (!empty($options) && !is_array($options)) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('The options must be an array');
}
switch ($metric) {
case Zend_Cloud_Infrastructure_Instance::MONITOR_CPU:
$cmd= 'top -b -n '.self::MONITOR_CPU_SAMPLES.' | grep \'Cpu\'';
break;
case Zend_Cloud_Infrastructure_Instance::MONITOR_RAM:
$cmd= 'top -b -n 1 | grep \'Mem\'';
break;
case Zend_Cloud_Infrastructure_Instance::MONITOR_DISK:
$cmd= 'df --total | grep total';
break;
}
if (empty($cmd)) {
require_once 'Zend/Cloud/Infrastructure/Exception.php';
throw new Zend_Cloud_Infrastructure_Exception('The metric specified is not supported by the adapter');
}
$params= array(
Zend_Cloud_Infrastructure_Instance::SSH_USERNAME => $options['username'],
Zend_Cloud_Infrastructure_Instance::SSH_PASSWORD => $options['password']
);
$exec_time= time();
$result= $this->deployInstance($id,$params,$cmd);
if (empty($result)) {
return false;
}
$monitor = array();
$num = 0;
$average = 0;
$outputs= explode("\n",$result);
foreach ($outputs as $output) {
if (!empty($output)) {
switch ($metric) {
case Zend_Cloud_Infrastructure_Instance::MONITOR_CPU:
if (preg_match('/(\d+\.\d)%us/', $output,$match)) {
$usage = (float) $match[1];
}
break;
case Zend_Cloud_Infrastructure_Instance::MONITOR_RAM:
if (preg_match('/(\d+)k total/', $output,$match)) {
$total = (integer) $match[1];
}
if (preg_match('/(\d+)k used/', $output,$match)) {
$used = (integer) $match[1];
}
if ($total>0) {
$usage= (float) $used/$total;
}
break;
case Zend_Cloud_Infrastructure_Instance::MONITOR_DISK:
if (preg_match('/(\d+)%/', $output,$match)) {
$usage = (float) $match[1];
}
break;
}
$monitor['series'][] = array (
'timestamp' => $exec_time,
'value' => number_format($usage,2).'%'
);
$average += $usage;
$exec_time+= 60; // seconds
$num++;
}
}
if ($num>0) {
$monitor['average'] = number_format($average/$num,2).'%';
}
return $monitor;
}
/**
* Get the adapter
*
* @return Zend_Service_Rackspace_Servers
*/
public function getAdapter()
{
return $this->rackspace;
}
/**
* Get last HTTP request
*
* @return string
*/
public function getLastHttpRequest()
{
return $this->rackspace->getHttpClient()->getLastRequest();
}
/**
* Get the last HTTP response
*
* @return Zend_Http_Response
*/
public function getLastHttpResponse()
{
return $this->rackspace->getHttpClient()->getLastResponse();
}
}