|
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/Amazon/Ec2/Instance.php'; |
|
11 require_once 'Zend/Service/Amazon/Ec2/Image.php'; |
|
12 require_once 'Zend/Service/Amazon/Ec2/Availabilityzones.php'; |
|
13 require_once 'Zend/Service/Amazon/Ec2/CloudWatch.php'; |
|
14 require_once 'Zend/Cloud/Infrastructure/Instance.php'; |
|
15 require_once 'Zend/Cloud/Infrastructure/InstanceList.php'; |
|
16 require_once 'Zend/Cloud/Infrastructure/Image.php'; |
|
17 require_once 'Zend/Cloud/Infrastructure/ImageList.php'; |
|
18 require_once 'Zend/Cloud/Infrastructure/Adapter/AbstractAdapter.php'; |
|
19 |
|
20 /** |
|
21 * Amazon EC2 adapter for infrastructure service |
|
22 * |
|
23 * @package Zend_Cloud_Infrastructure |
|
24 * @subpackage Adapter |
|
25 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) |
|
26 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
27 */ |
|
28 class Zend_Cloud_Infrastructure_Adapter_Ec2 extends Zend_Cloud_Infrastructure_Adapter_AbstractAdapter |
|
29 { |
|
30 /** |
|
31 * AWS constants |
|
32 */ |
|
33 const AWS_ACCESS_KEY = 'aws_accesskey'; |
|
34 const AWS_SECRET_KEY = 'aws_secretkey'; |
|
35 const AWS_REGION = 'aws_region'; |
|
36 const AWS_SECURITY_GROUP = 'securityGroup'; |
|
37 |
|
38 /** |
|
39 * Ec2 Instance |
|
40 * |
|
41 * @var Ec2Instance |
|
42 */ |
|
43 protected $ec2; |
|
44 |
|
45 /** |
|
46 * Ec2 Image |
|
47 * |
|
48 * @var Ec2Image |
|
49 */ |
|
50 protected $ec2Image; |
|
51 |
|
52 /** |
|
53 * Ec2 Zone |
|
54 * |
|
55 * @var Ec2Zone |
|
56 */ |
|
57 protected $ec2Zone; |
|
58 |
|
59 /** |
|
60 * Ec2 Monitor |
|
61 * |
|
62 * @var Ec2Monitor |
|
63 */ |
|
64 protected $ec2Monitor; |
|
65 |
|
66 /** |
|
67 * AWS Access Key |
|
68 * |
|
69 * @var string |
|
70 */ |
|
71 protected $accessKey; |
|
72 |
|
73 /** |
|
74 * AWS Access Secret |
|
75 * |
|
76 * @var string |
|
77 */ |
|
78 protected $accessSecret; |
|
79 |
|
80 /** |
|
81 * Region zone |
|
82 * |
|
83 * @var string |
|
84 */ |
|
85 protected $region; |
|
86 |
|
87 /** |
|
88 * Map array between EC2 and Infrastructure status |
|
89 * |
|
90 * @var array |
|
91 */ |
|
92 protected $mapStatus = array ( |
|
93 'running' => Zend_Cloud_Infrastructure_Instance::STATUS_RUNNING, |
|
94 'terminated' => Zend_Cloud_Infrastructure_Instance::STATUS_TERMINATED, |
|
95 'pending' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING, |
|
96 'shutting-down' => Zend_Cloud_Infrastructure_Instance::STATUS_SHUTTING_DOWN, |
|
97 'stopping' => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING, |
|
98 'stopped' => Zend_Cloud_Infrastructure_Instance::STATUS_STOPPED, |
|
99 'rebooting' => Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING, |
|
100 ); |
|
101 |
|
102 /** |
|
103 * Map monitor metrics between Infrastructure and EC2 |
|
104 * |
|
105 * @var array |
|
106 */ |
|
107 protected $mapMetrics= array ( |
|
108 Zend_Cloud_Infrastructure_Instance::MONITOR_CPU => 'CPUUtilization', |
|
109 Zend_Cloud_Infrastructure_Instance::MONITOR_DISK_READ => 'DiskReadBytes', |
|
110 Zend_Cloud_Infrastructure_Instance::MONITOR_DISK_WRITE => 'DiskWriteBytes', |
|
111 Zend_Cloud_Infrastructure_Instance::MONITOR_NETWORK_IN => 'NetworkIn', |
|
112 Zend_Cloud_Infrastructure_Instance::MONITOR_NETWORK_OUT => 'NetworkOut', |
|
113 ); |
|
114 |
|
115 /** |
|
116 * Constructor |
|
117 * |
|
118 * @param array|Zend_Config $options |
|
119 * @return void |
|
120 */ |
|
121 public function __construct($options = array()) |
|
122 { |
|
123 if (is_object($options)) { |
|
124 if (method_exists($options, 'toArray')) { |
|
125 $options= $options->toArray(); |
|
126 } elseif ($options instanceof Traversable) { |
|
127 $options = iterator_to_array($options); |
|
128 } |
|
129 } |
|
130 |
|
131 if (empty($options) || !is_array($options)) { |
|
132 require_once 'Zend/Cloud/Infrastructure/Exception.php'; |
|
133 throw new Zend_Cloud_Infrastructure_Exception('Invalid options provided'); |
|
134 } |
|
135 |
|
136 if (!isset($options[self::AWS_ACCESS_KEY]) |
|
137 || !isset($options[self::AWS_SECRET_KEY]) |
|
138 ) { |
|
139 require_once 'Zend/Cloud/Infrastructure/Exception.php'; |
|
140 throw new Zend_Cloud_Infrastructure_Exception('AWS keys not specified!'); |
|
141 } |
|
142 |
|
143 $this->accessKey = $options[self::AWS_ACCESS_KEY]; |
|
144 $this->accessSecret = $options[self::AWS_SECRET_KEY]; |
|
145 $this->region = ''; |
|
146 |
|
147 if (isset($options[self::AWS_REGION])) { |
|
148 $this->region= $options[self::AWS_REGION]; |
|
149 } |
|
150 |
|
151 try { |
|
152 $this->ec2 = new Zend_Service_Amazon_Ec2_Instance($options[self::AWS_ACCESS_KEY], $options[self::AWS_SECRET_KEY], $this->region); |
|
153 } catch (Exception $e) { |
|
154 require_once 'Zend/Cloud/Infrastructure/Exception.php'; |
|
155 throw new Zend_Cloud_Infrastructure_Exception('Error on create: ' . $e->getMessage(), $e->getCode(), $e); |
|
156 } |
|
157 |
|
158 if (isset($options[self::HTTP_ADAPTER])) { |
|
159 $this->ec2->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]); |
|
160 } |
|
161 } |
|
162 |
|
163 /** |
|
164 * Convert the attributes of EC2 into attributes of Infrastructure |
|
165 * |
|
166 * @param array $attr |
|
167 * @return array|boolean |
|
168 */ |
|
169 private function convertAttributes($attr) |
|
170 { |
|
171 $result = array(); |
|
172 if (!empty($attr) && is_array($attr)) { |
|
173 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ID] = $attr['instanceId']; |
|
174 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STATUS] = $this->mapStatus[$attr['instanceState']['name']]; |
|
175 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_IMAGEID] = $attr['imageId']; |
|
176 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE] = $attr['availabilityZone']; |
|
177 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_LAUNCHTIME] = $attr['launchTime']; |
|
178 |
|
179 switch ($attr['instanceType']) { |
|
180 case Zend_Service_Amazon_Ec2_Instance::MICRO: |
|
181 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU] = '1 virtual core'; |
|
182 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM] = '613MB'; |
|
183 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '0GB'; |
|
184 break; |
|
185 case Zend_Service_Amazon_Ec2_Instance::SMALL: |
|
186 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU] = '1 virtual core'; |
|
187 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM] = '1.7GB'; |
|
188 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '160GB'; |
|
189 break; |
|
190 case Zend_Service_Amazon_Ec2_Instance::LARGE: |
|
191 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU] = '2 virtual core'; |
|
192 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM] = '7.5GB'; |
|
193 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '850GB'; |
|
194 break; |
|
195 case Zend_Service_Amazon_Ec2_Instance::XLARGE: |
|
196 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU] = '4 virtual core'; |
|
197 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM] = '15GB'; |
|
198 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '1690GB'; |
|
199 break; |
|
200 case Zend_Service_Amazon_Ec2_Instance::HCPU_MEDIUM: |
|
201 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU] = '2 virtual core'; |
|
202 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM] = '1.7GB'; |
|
203 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '350GB'; |
|
204 break; |
|
205 case Zend_Service_Amazon_Ec2_Instance::HCPU_XLARGE: |
|
206 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU] = '8 virtual core'; |
|
207 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM] = '7GB'; |
|
208 $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '1690GB'; |
|
209 break; |
|
210 } |
|
211 } |
|
212 return $result; |
|
213 } |
|
214 |
|
215 /** |
|
216 * Return a list of the available instancies |
|
217 * |
|
218 * @return Zend_Cloud_Infrastructure_InstanceList |
|
219 */ |
|
220 public function listInstances() |
|
221 { |
|
222 $this->adapterResult = $this->ec2->describe(); |
|
223 |
|
224 $result = array(); |
|
225 foreach ($this->adapterResult['instances'] as $instance) { |
|
226 $result[]= $this->convertAttributes($instance); |
|
227 } |
|
228 return new Zend_Cloud_Infrastructure_InstanceList($this, $result); |
|
229 } |
|
230 |
|
231 /** |
|
232 * Return the status of an instance |
|
233 * |
|
234 * @param string |
|
235 * @return string|boolean |
|
236 */ |
|
237 public function statusInstance($id) |
|
238 { |
|
239 $this->adapterResult = $this->ec2->describe($id); |
|
240 if (empty($this->adapterResult['instances'])) { |
|
241 return false; |
|
242 } |
|
243 $result = $this->adapterResult['instances'][0]; |
|
244 return $this->mapStatus[$result['instanceState']['name']]; |
|
245 } |
|
246 |
|
247 /** |
|
248 * Return the public DNS name of the instance |
|
249 * |
|
250 * @param string $id |
|
251 * @return string|boolean |
|
252 */ |
|
253 public function publicDnsInstance($id) |
|
254 { |
|
255 $this->adapterResult = $this->ec2->describe($id); |
|
256 if (empty($this->adapterResult['instances'])) { |
|
257 return false; |
|
258 } |
|
259 $result = $this->adapterResult['instances'][0]; |
|
260 return $result['dnsName']; |
|
261 } |
|
262 |
|
263 /** |
|
264 * Reboot an instance |
|
265 * |
|
266 * @param string $id |
|
267 * @return boolean |
|
268 */ |
|
269 public function rebootInstance($id) |
|
270 { |
|
271 $this->adapterResult= $this->ec2->reboot($id); |
|
272 return $this->adapterResult; |
|
273 } |
|
274 |
|
275 /** |
|
276 * Create a new instance |
|
277 * |
|
278 * @param string $name |
|
279 * @param array $options |
|
280 * @return Instance|boolean |
|
281 */ |
|
282 public function createInstance($name, $options) |
|
283 { |
|
284 // @todo instance's name management? |
|
285 $this->adapterResult = $this->ec2->run($options); |
|
286 if (empty($this->adapterResult['instances'])) { |
|
287 return false; |
|
288 } |
|
289 $this->error= false; |
|
290 return new Zend_Cloud_Infrastructure_Instance($this, $this->convertAttributes($this->adapterResult['instances'][0])); |
|
291 } |
|
292 |
|
293 /** |
|
294 * Stop an instance |
|
295 * |
|
296 * @param string $id |
|
297 * @return boolean |
|
298 */ |
|
299 public function stopInstance($id) |
|
300 { |
|
301 require_once 'Zend/Cloud/Infrastructure/Exception.php'; |
|
302 throw new Zend_Cloud_Infrastructure_Exception('The stopInstance method is not implemented in the adapter'); |
|
303 } |
|
304 |
|
305 /** |
|
306 * Start an instance |
|
307 * |
|
308 * @param string $id |
|
309 * @return boolean |
|
310 */ |
|
311 public function startInstance($id) |
|
312 { |
|
313 require_once 'Zend/Cloud/Infrastructure/Exception.php'; |
|
314 throw new Zend_Cloud_Infrastructure_Exception('The startInstance method is not implemented in the adapter'); |
|
315 } |
|
316 |
|
317 /** |
|
318 * Destroy an instance |
|
319 * |
|
320 * @param string $id |
|
321 * @return boolean |
|
322 */ |
|
323 public function destroyInstance($id) |
|
324 { |
|
325 $this->adapterResult = $this->ec2->terminate($id); |
|
326 return (!empty($this->adapterResult)); |
|
327 } |
|
328 |
|
329 /** |
|
330 * Return a list of all the available instance images |
|
331 * |
|
332 * @return ImageList |
|
333 */ |
|
334 public function imagesInstance() |
|
335 { |
|
336 if (!isset($this->ec2Image)) { |
|
337 $this->ec2Image = new Zend_Service_Amazon_Ec2_Image($this->accessKey, $this->accessSecret, $this->region); |
|
338 } |
|
339 |
|
340 $this->adapterResult = $this->ec2Image->describe(); |
|
341 |
|
342 $images = array(); |
|
343 |
|
344 foreach ($this->adapterResult as $result) { |
|
345 switch (strtolower($result['platform'])) { |
|
346 case 'windows' : |
|
347 $platform = Zend_Cloud_Infrastructure_Image::IMAGE_WINDOWS; |
|
348 break; |
|
349 default: |
|
350 $platform = Zend_Cloud_Infrastructure_Image::IMAGE_LINUX; |
|
351 break; |
|
352 } |
|
353 |
|
354 $images[]= array ( |
|
355 Zend_Cloud_Infrastructure_Image::IMAGE_ID => $result['imageId'], |
|
356 Zend_Cloud_Infrastructure_Image::IMAGE_NAME => '', |
|
357 Zend_Cloud_Infrastructure_Image::IMAGE_DESCRIPTION => $result['imageLocation'], |
|
358 Zend_Cloud_Infrastructure_Image::IMAGE_OWNERID => $result['imageOwnerId'], |
|
359 Zend_Cloud_Infrastructure_Image::IMAGE_ARCHITECTURE => $result['architecture'], |
|
360 Zend_Cloud_Infrastructure_Image::IMAGE_PLATFORM => $platform, |
|
361 ); |
|
362 } |
|
363 return new Zend_Cloud_Infrastructure_ImageList($images,$this->ec2Image); |
|
364 } |
|
365 |
|
366 /** |
|
367 * Return all the available zones |
|
368 * |
|
369 * @return array |
|
370 */ |
|
371 public function zonesInstance() |
|
372 { |
|
373 if (!isset($this->ec2Zone)) { |
|
374 $this->ec2Zone = new Zend_Service_Amazon_Ec2_AvailabilityZones($this->accessKey,$this->accessSecret,$this->region); |
|
375 } |
|
376 $this->adapterResult = $this->ec2Zone->describe(); |
|
377 |
|
378 $zones = array(); |
|
379 foreach ($this->adapterResult as $zone) { |
|
380 if (strtolower($zone['zoneState']) === 'available') { |
|
381 $zones[] = array ( |
|
382 Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE => $zone['zoneName'], |
|
383 ); |
|
384 } |
|
385 } |
|
386 return $zones; |
|
387 } |
|
388 |
|
389 /** |
|
390 * Return the system information about the $metric of an instance |
|
391 * |
|
392 * @param string $id |
|
393 * @param string $metric |
|
394 * @param null|array $options |
|
395 * @return array |
|
396 */ |
|
397 public function monitorInstance($id, $metric, $options = null) |
|
398 { |
|
399 if (empty($id) || empty($metric)) { |
|
400 return false; |
|
401 } |
|
402 |
|
403 if (!in_array($metric,$this->validMetrics)) { |
|
404 require_once 'Zend/Cloud/Infrastructure/Exception.php'; |
|
405 throw new Zend_Cloud_Infrastructure_Exception(sprintf( |
|
406 'The metric "%s" is not valid', |
|
407 $metric |
|
408 )); |
|
409 } |
|
410 |
|
411 if (!empty($options) && !is_array($options)) { |
|
412 require_once 'Zend/Cloud/Infrastructure/Exception.php'; |
|
413 throw new Zend_Cloud_Infrastructure_Exception('The options must be an array'); |
|
414 } |
|
415 |
|
416 if (!empty($options) |
|
417 && (empty($options[Zend_Cloud_Infrastructure_Instance::MONITOR_START_TIME]) |
|
418 || empty($options[Zend_Cloud_Infrastructure_Instance::MONITOR_END_TIME])) |
|
419 ) { |
|
420 require_once 'Zend/Cloud/Infrastructure/Exception.php'; |
|
421 throw new Zend_Cloud_Infrastructure_Exception(sprintf( |
|
422 'The options array must contain: "%s" and "%s"', |
|
423 $options[Zend_Cloud_Infrastructure_Instance::MONITOR_START_TIME], |
|
424 $options[Zend_Cloud_Infrastructure_Instance::MONITOR_END_TIME] |
|
425 )); |
|
426 } |
|
427 |
|
428 if (!isset($this->ec2Monitor)) { |
|
429 $this->ec2Monitor = new Zend_Service_Amazon_Ec2_CloudWatch($this->accessKey, $this->accessSecret, $this->region); |
|
430 } |
|
431 |
|
432 $param = array( |
|
433 'MeasureName' => $this->mapMetrics[$metric], |
|
434 'Statistics' => array('Average'), |
|
435 'Dimensions' => array('InstanceId' => $id), |
|
436 ); |
|
437 |
|
438 if (!empty($options)) { |
|
439 $param['StartTime'] = $options[Zend_Cloud_Infrastructure_Instance::MONITOR_START_TIME]; |
|
440 $param['EndTime'] = $options[Zend_Cloud_Infrastructure_Instance::MONITOR_END_TIME]; |
|
441 } |
|
442 |
|
443 $this->adapterResult = $this->ec2Monitor->getMetricStatistics($param); |
|
444 |
|
445 $monitor = array(); |
|
446 $num = 0; |
|
447 $average = 0; |
|
448 |
|
449 if (!empty($this->adapterResult['datapoints'])) { |
|
450 foreach ($this->adapterResult['datapoints'] as $result) { |
|
451 $monitor['series'][] = array ( |
|
452 'timestamp' => $result['Timestamp'], |
|
453 'value' => $result['Average'], |
|
454 ); |
|
455 $average += $result['Average']; |
|
456 $num++; |
|
457 } |
|
458 } |
|
459 |
|
460 if ($num > 0) { |
|
461 $monitor['average'] = $average / $num; |
|
462 } |
|
463 |
|
464 return $monitor; |
|
465 } |
|
466 |
|
467 /** |
|
468 * Get the adapter |
|
469 * |
|
470 * @return Zend_Service_Amazon_Ec2_Instance |
|
471 */ |
|
472 public function getAdapter() |
|
473 { |
|
474 return $this->ec2; |
|
475 } |
|
476 |
|
477 /** |
|
478 * Get last HTTP request |
|
479 * |
|
480 * @return string |
|
481 */ |
|
482 public function getLastHttpRequest() |
|
483 { |
|
484 return $this->ec2->getHttpClient()->getLastRequest(); |
|
485 } |
|
486 |
|
487 /** |
|
488 * Get the last HTTP response |
|
489 * |
|
490 * @return Zend_Http_Response |
|
491 */ |
|
492 public function getLastHttpResponse() |
|
493 { |
|
494 return $this->ec2->getHttpClient()->getLastResponse(); |
|
495 } |
|
496 } |