Rackspace.php 17 KB

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