|
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 } |