Browse Source

Cloud Infrastructure

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@24492 44c647ce-9c0f-0410-b52a-842ac1e357ba
ezimuel 14 years ago
parent
commit
ea1b788ce9

+ 167 - 0
library/Zend/Cloud/Infrastructure/Adapter.php

@@ -0,0 +1,167 @@
+<?php
+/**
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * Adapter interface for infrastructure service
+ * 
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Cloud_Infrastructure_Adapter 
+{ 
+    const HTTP_ADAPTER = 'http_adapter'; 
+
+    /**
+     * The max. amount of time, in seconds, to wait for a status change
+     */
+    const TIMEOUT_STATUS_CHANGE = 30;
+
+    /**
+     * The time step, in seconds, for the status change
+     */
+    const TIME_STEP_STATUS_CHANGE = 5;
+
+    /**
+     * Return a list of the available instances
+     *
+     * @return InstanceList
+     */ 
+    public function listInstances(); 
+ 
+    /**
+     * Return the status of an instance
+     *
+     * @param  string $id
+     * @return string
+     */ 
+    public function statusInstance($id); 
+
+    /**
+     * Wait for status $status with a timeout of $timeout seconds
+     * 
+     * @param  string $id
+     * @param  string $status
+     * @param  integer $timeout 
+     * @return boolean
+     */
+    public function waitStatusInstance($id, $status, $timeout = self::TIMEOUT_STATUS_CHANGE);
+    
+    /**
+     * Return the public DNS name of the instance
+     * 
+     * @param  string $id
+     * @return string|boolean 
+     */
+    public function publicDnsInstance($id);
+    
+    /**
+     * Reboot an instance
+     *
+     * @param string $id
+     * @return boolean
+     */ 
+    public function rebootInstance($id); 
+ 
+    /**
+     * Create a new instance
+     *
+     * @param  string $name
+     * @param  array $options
+     * @return boolean
+     */ 
+    public function createInstance($name, $options); 
+ 
+    /**
+     * Stop the execution of an instance
+     *
+     * @param  string $id
+     * @return boolean
+     */ 
+    public function stopInstance($id); 
+ 
+    /**
+     * Start the execution of an instance
+     *
+     * @param  string $id
+     * @return boolean
+     */ 
+    public function startInstance($id); 
+ 
+    /**
+     * Destroy an instance
+     *
+     * @param  string $id
+     * @return boolean
+     */ 
+    public function destroyInstance($id); 
+ 
+    /**
+     * Return all the available instances images
+     *
+     * @return ImageList
+     */ 
+    public function imagesInstance(); 
+    
+    /**
+     * Return all the available zones
+     * 
+     * @return array
+     */
+    public function zonesInstance();
+    
+    /**
+     * Return the system informations about the $metric of an instance
+     *
+     * @param  string $id
+     * @param  string $metric
+     * @param  array $options
+     * @return array
+     */ 
+    public function monitorInstance($id, $metric, $options = null); 
+ 
+    /**
+     * Run arbitrary shell script on an instance
+     *
+     * @param  string $id
+     * @param  array $param
+     * @param  string|array $cmd
+     * @return string|array
+     */ 
+    public function deployInstance($id, $param, $cmd);
+            
+    /**
+     * Get the adapter instance
+     * 
+     * @return object
+     */
+    public function getAdapter();
+    
+    /**
+     * Get the adapter result
+     * 
+     * @return array
+     */
+    public function getAdapterResult();
+    
+    /**
+     * Get the last HTTP response
+     * 
+     * @return Zend_Http_Response
+     */
+    public function getLastHttpResponse();
+    
+    /**
+     * Get the last HTTP request
+     * 
+     * @return string
+     */
+    public function getLastHttpRequest();
+} 

+ 175 - 0
library/Zend/Cloud/Infrastructure/Adapter/AbstractAdapter.php

@@ -0,0 +1,175 @@
+<?php
+/**
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage DocumentService
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+require_once 'Zend/Cloud/Infrastructure/Adapter.php';
+require_once 'Zend/Cloud/Infrastructure/Instance.php';
+
+/**
+ * Abstract infrastructure service adapter
+ *
+ * @category   Zend
+ * @package    Zend_Cloud_Infrastructure
+ * @subpackage Adapter
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Cloud_Infrastructure_Adapter_AbstractAdapter implements Zend_Cloud_Infrastructure_Adapter
+{
+    /**
+     * Store the last response from the adpter
+     * 
+     * @var array
+     */
+    protected $adapterResult;
+    
+    /**
+     * Valid metrics for monitor
+     * 
+     * @var array
+     */
+    protected $validMetrics = array(
+        Zend_Cloud_Infrastructure_Instance::MONITOR_CPU,
+        Zend_Cloud_Infrastructure_Instance::MONITOR_RAM,
+        Zend_Cloud_Infrastructure_Instance::MONITOR_DISK,
+        Zend_Cloud_Infrastructure_Instance::MONITOR_DISK_READ,
+        Zend_Cloud_Infrastructure_Instance::MONITOR_DISK_WRITE,
+        Zend_Cloud_Infrastructure_Instance::MONITOR_NETWORK_IN,
+        Zend_Cloud_Infrastructure_Instance::MONITOR_NETWORK_OUT,
+    );
+
+    /**
+     * Get the last result of the adapter
+     *
+     * @return array
+     */
+    public function getAdapterResult()
+    {
+        return $this->adapterResult;
+    }
+
+    /**
+     * Wait for status $status with a timeout of $timeout seconds
+     * 
+     * @param  string $id
+     * @param  string $status
+     * @param  integer $timeout 
+     * @return boolean
+     */
+    public function waitStatusInstance($id, $status, $timeout = self::TIMEOUT_STATUS_CHANGE)
+    {
+        if (empty($id) || empty($status)) {
+            return false;
+        }
+
+        $num = 0;
+        while (($num<$timeout) && ($this->statusInstance($id) != $status)) {
+            sleep(self::TIME_STEP_STATUS_CHANGE);
+            $num += self::TIME_STEP_STATUS_CHANGE;
+        }
+        return ($num < $timeout);
+    }
+
+    /**
+     * Run arbitrary shell script on an instance
+     *
+     * @param  string $id
+     * @param  array $param
+     * @param  string|array $cmd
+     * @return string|array
+     */ 
+    public function deployInstance($id, $params, $cmd)
+    {
+        if (!function_exists("ssh2_connect")) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('Deployment 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 instance where to deploy');
+        }
+
+        if (empty($cmd)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('You must specify the shell commands to run on the instance');
+        }
+
+        if (empty($params) 
+            || empty($params[Zend_Cloud_Infrastructure_Instance::SSH_USERNAME]) 
+            || (empty($params[Zend_Cloud_Infrastructure_Instance::SSH_PASSWORD]) 
+                && empty($params[Zend_Cloud_Infrastructure_Instance::SSH_KEY]))
+        ) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('You must specify the params for the SSH connection');
+        }
+
+        $host = $this->publicDnsInstance($id);
+        if (empty($host)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception(sprintf(
+                'The instance identified by "%s" does not exist', 
+                $id
+            ));
+        }
+
+        $conn = ssh2_connect($host);
+        if (!ssh2_auth_password($conn, $params[Zend_Cloud_Infrastructure_Instance::SSH_USERNAME], 
+                $params[Zend_Cloud_Infrastructure_Instance::SSH_PASSWORD])) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('SSH authentication failed');
+        }
+
+        if (is_array($cmd)) {
+            $result = array();
+            foreach ($cmd as $command) {
+                $stream      = ssh2_exec($conn, $command);
+                $errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
+
+                stream_set_blocking($errorStream, true);
+                stream_set_blocking($stream, true); 
+
+                $output = stream_get_contents($stream);
+                $error  = stream_get_contents($errorStream);
+                
+                if (empty($error)) {
+                    $result[$command] = $output;
+                } else {
+                    $result[$command] = $error;
+                }
+            }
+        } else {
+            $stream      = ssh2_exec($conn, $cmd);
+            $result      = stream_set_blocking($stream, true);
+            $errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
+
+            stream_set_blocking($errorStream, true);
+            stream_set_blocking($stream, true); 
+
+            $output = stream_get_contents($stream);
+            $error  = stream_get_contents($errorStream);
+            
+            if (empty($error)) {
+                $result = $output;
+            } else {
+                $result = $error;
+            }
+        }    
+        return $result;
+    }
+}

+ 496 - 0
library/Zend/Cloud/Infrastructure/Adapter/Ec2.php

@@ -0,0 +1,496 @@
+<?php
+/**
+ * @category   Zend
+ * @package    Zend_Cloud_Infrastructure
+ * @subpackage Adapter
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+require_once 'Zend/Service/Amazon/Ec2/Instance.php';
+require_once 'Zend/Service/Amazon/Ec2/Image.php';
+require_once 'Zend/Service/Amazon/Ec2/Availabilityzones.php';
+require_once 'Zend/Service/Amazon/Ec2/CloudWatch.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';
+
+/**
+ * Amazon EC2 adapter for infrastructure service
+ *
+ * @package    Zend_Cloud_Infrastructure
+ * @subpackage Adapter
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cloud_Infrastructure_Adapter_Ec2 extends Zend_Cloud_Infrastructure_Adapter_AbstractAdapter
+{
+    /**
+     * AWS constants
+     */
+    const AWS_ACCESS_KEY     = 'aws_accesskey';
+    const AWS_SECRET_KEY     = 'aws_secretkey';
+    const AWS_REGION         = 'aws_region';
+    const AWS_SECURITY_GROUP = 'securityGroup';
+
+    /**
+     * Ec2 Instance 
+     * 
+     * @var Ec2Instance
+     */
+    protected $ec2;
+
+    /**
+     * Ec2 Image
+     * 
+     * @var Ec2Image
+     */
+    protected $ec2Image;
+
+    /**
+     * Ec2 Zone
+     * 
+     * @var Ec2Zone
+     */
+    protected $ec2Zone;
+
+    /**
+     * Ec2 Monitor 
+     * 
+     * @var Ec2Monitor
+     */
+    protected $ec2Monitor;
+
+    /**
+     * AWS Access Key
+     * 
+     * @var string 
+     */
+    protected $accessKey;
+
+    /**
+     * AWS Access Secret
+     * 
+     * @var string 
+     */
+    protected $accessSecret;
+
+    /**
+     * Region zone 
+     * 
+     * @var string 
+     */
+    protected $region;
+
+    /**
+     * Map array between EC2 and Infrastructure status
+     * 
+     * @var array 
+     */
+    protected $mapStatus = array (
+        'running'       => Zend_Cloud_Infrastructure_Instance::STATUS_RUNNING,
+        'terminated'    => Zend_Cloud_Infrastructure_Instance::STATUS_TERMINATED,
+        'pending'       => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
+        'shutting-down' => Zend_Cloud_Infrastructure_Instance::STATUS_SHUTTING_DOWN,
+        'stopping'      => Zend_Cloud_Infrastructure_Instance::STATUS_PENDING,
+        'stopped'       => Zend_Cloud_Infrastructure_Instance::STATUS_STOPPED,
+        'rebooting'     => Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING,
+    );
+
+    /**
+     * Map monitor metrics between Infrastructure and EC2
+     * 
+     * @var array 
+     */
+    protected $mapMetrics= array (
+        Zend_Cloud_Infrastructure_Instance::MONITOR_CPU         => 'CPUUtilization',
+        Zend_Cloud_Infrastructure_Instance::MONITOR_DISK_READ   => 'DiskReadBytes',
+        Zend_Cloud_Infrastructure_Instance::MONITOR_DISK_WRITE  => 'DiskWriteBytes',
+        Zend_Cloud_Infrastructure_Instance::MONITOR_NETWORK_IN  => 'NetworkIn',
+        Zend_Cloud_Infrastructure_Instance::MONITOR_NETWORK_OUT => 'NetworkOut',
+    );
+
+    /**
+     * 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::AWS_ACCESS_KEY]) 
+            || !isset($options[self::AWS_SECRET_KEY])
+        ) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('AWS keys not specified!');
+        }
+
+        $this->accessKey    = $options[self::AWS_ACCESS_KEY];
+        $this->accessSecret = $options[self::AWS_SECRET_KEY];
+        $this->region       = '';
+
+        if (isset($options[self::AWS_REGION])) {
+            $this->region= $options[self::AWS_REGION];
+        }
+
+        try {
+            $this->ec2 = new Zend_Service_Amazon_Ec2_Instance($options[self::AWS_ACCESS_KEY], $options[self::AWS_SECRET_KEY], $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->ec2->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
+        }
+    }
+
+    /**
+     * Convert the attributes of EC2 into attributes of Infrastructure
+     * 
+     * @param  array $attr
+     * @return array|boolean 
+     */
+    private function convertAttributes($attr)
+    {
+        $result = array();       
+        if (!empty($attr) && is_array($attr)) {
+            $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ID]         = $attr['instanceId'];
+            $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STATUS]     = $this->mapStatus[$attr['instanceState']['name']];
+            $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_IMAGEID]    = $attr['imageId'];
+            $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE]       = $attr['availabilityZone'];
+            $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_LAUNCHTIME] = $attr['launchTime'];
+
+            switch ($attr['instanceType']) {
+                case Zend_Service_Amazon_Ec2_Instance::MICRO:
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU]     = '1 virtual core';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM]     = '613MB';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '0GB';
+                    break;
+                case Zend_Service_Amazon_Ec2_Instance::SMALL:
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU]     = '1 virtual core';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM]     = '1.7GB';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '160GB';
+                    break;
+                case Zend_Service_Amazon_Ec2_Instance::LARGE:
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU]     = '2 virtual core';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM]     = '7.5GB';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '850GB';
+                    break;
+                case Zend_Service_Amazon_Ec2_Instance::XLARGE:
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU]     = '4 virtual core';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM]     = '15GB';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '1690GB';
+                    break;
+                case Zend_Service_Amazon_Ec2_Instance::HCPU_MEDIUM:
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU]     = '2 virtual core';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM]     = '1.7GB';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '350GB';
+                    break;
+                case Zend_Service_Amazon_Ec2_Instance::HCPU_XLARGE:
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_CPU]     = '8 virtual core';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_RAM]     = '7GB';
+                    $result[Zend_Cloud_Infrastructure_Instance::INSTANCE_STORAGE] = '1690GB';
+                    break;
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * Return a list of the available instancies
+     *
+     * @return Zend_Cloud_Infrastructure_InstanceList
+     */ 
+    public function listInstances() 
+    {
+        $this->adapterResult = $this->ec2->describe();
+
+        $result = array();
+        foreach ($this->adapterResult['instances'] 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->ec2->describe($id);
+        if (empty($this->adapterResult['instances'])) {
+            return false;
+        }    
+        $result = $this->adapterResult['instances'][0];
+        return $this->mapStatus[$result['instanceState']['name']];
+    }
+
+    /**
+     * Return the public DNS name of the instance
+     * 
+     * @param  string $id
+     * @return string|boolean 
+     */
+    public function publicDnsInstance($id) 
+    {
+        $this->adapterResult = $this->ec2->describe($id);
+        if (empty($this->adapterResult['instances'])) {
+            return false;
+        }    
+        $result = $this->adapterResult['instances'][0];
+        return $result['dnsName'];
+    }
+
+    /**
+     * Reboot an instance
+     *
+     * @param string $id
+     * @return boolean
+     */ 
+    public function rebootInstance($id)
+    {
+        $this->adapterResult= $this->ec2->reboot($id);
+        return $this->adapterResult;
+    }
+
+    /**
+     * Create a new instance
+     *
+     * @param string $name
+     * @param array $options
+     * @return Instance|boolean
+     */ 
+    public function createInstance($name, $options)
+    {
+        // @todo instance's name management?
+        $this->adapterResult = $this->ec2->run($options);
+        if (empty($this->adapterResult['instances'])) {
+            return false;
+        }
+        $this->error= false;
+        return new Zend_Cloud_Infrastructure_Instance($this, $this->convertAttributes($this->adapterResult['instances'][0]));
+    }
+
+    /**
+     * 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->ec2->terminate($id);
+        return (!empty($this->adapterResult));
+    }
+ 
+    /**
+     * Return a list of all the available instance images
+     *
+     * @return ImageList
+     */ 
+    public function imagesInstance()
+    {
+        if (!isset($this->ec2Image)) {
+            $this->ec2Image = new Zend_Service_Amazon_Ec2_Image($this->accessKey, $this->accessSecret, $this->region);
+        }
+
+        $this->adapterResult = $this->ec2Image->describe();
+                
+        $images = array();
+
+        foreach ($this->adapterResult as $result) {
+            switch (strtolower($result['platform'])) {
+                case 'windows' :
+                    $platform = Zend_Cloud_Infrastructure_Image::IMAGE_WINDOWS;
+                    break;
+                default:
+                    $platform = Zend_Cloud_Infrastructure_Image::IMAGE_LINUX;
+                    break;
+            }
+
+            $images[]= array (
+                Zend_Cloud_Infrastructure_Image::IMAGE_ID           => $result['imageId'],
+                Zend_Cloud_Infrastructure_Image::IMAGE_NAME         => '',
+                Zend_Cloud_Infrastructure_Image::IMAGE_DESCRIPTION  => $result['imageLocation'],
+                Zend_Cloud_Infrastructure_Image::IMAGE_OWNERID      => $result['imageOwnerId'],
+                Zend_Cloud_Infrastructure_Image::IMAGE_ARCHITECTURE => $result['architecture'],
+                Zend_Cloud_Infrastructure_Image::IMAGE_PLATFORM     => $platform,
+            );
+        }
+        return new Zend_Cloud_Infrastructure_ImageList($images,$this->ec2Image);
+    }
+
+    /**
+     * Return all the available zones
+     * 
+     * @return array
+     */
+    public function zonesInstance()
+    {
+        if (!isset($this->ec2Zone)) {
+            $this->ec2Zone = new Zend_Service_Amazon_Ec2_AvailabilityZones($this->accessKey,$this->accessSecret,$this->region);
+        }
+        $this->adapterResult = $this->ec2Zone->describe();
+
+        $zones = array();
+        foreach ($this->adapterResult as $zone) {
+            if (strtolower($zone['zoneState']) === 'available') {
+                $zones[] = array (
+                    Zend_Cloud_Infrastructure_Instance::INSTANCE_ZONE => $zone['zoneName'],
+                );
+            }
+        }
+        return $zones;
+    }
+
+    /**
+     * Return the system information about the $metric of an instance
+     * 
+     * @param  string $id
+     * @param  string $metric
+     * @param  null|array $options
+     * @return array
+     */ 
+    public function monitorInstance($id, $metric, $options = null)
+    {
+        if (empty($id) || empty($metric)) {
+            return false;
+        }
+
+        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');
+        }
+
+        if (!empty($options) 
+            && (empty($options[Zend_Cloud_Infrastructure_Instance::MONITOR_START_TIME]) 
+                || empty($options[Zend_Cloud_Infrastructure_Instance::MONITOR_END_TIME]))
+        ) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception(sprintf(
+                'The options array must contain: "%s" and "%s"',
+                $options[Zend_Cloud_Infrastructure_Instance::MONITOR_START_TIME],
+                $options[Zend_Cloud_Infrastructure_Instance::MONITOR_END_TIME]
+            ));
+        }      
+
+        if (!isset($this->ec2Monitor)) {
+            $this->ec2Monitor = new Zend_Service_Amazon_Ec2_CloudWatch($this->accessKey, $this->accessSecret, $this->region);
+        }
+
+        $param = array(
+            'MeasureName' => $this->mapMetrics[$metric],
+            'Statistics'  => array('Average'),
+            'Dimensions'  => array('InstanceId' => $id),
+        );
+
+        if (!empty($options)) {
+            $param['StartTime'] = $options[Zend_Cloud_Infrastructure_Instance::MONITOR_START_TIME];
+            $param['EndTime']   = $options[Zend_Cloud_Infrastructure_Instance::MONITOR_END_TIME];
+        }
+
+        $this->adapterResult = $this->ec2Monitor->getMetricStatistics($param);
+
+        $monitor             = array();
+        $num                 = 0;
+        $average             = 0;
+
+        if (!empty($this->adapterResult['datapoints'])) {
+            foreach ($this->adapterResult['datapoints'] as $result) {
+                $monitor['series'][] = array (
+                    'timestamp' => $result['Timestamp'],
+                    'value'     => $result['Average'],
+                );
+                $average += $result['Average'];
+                $num++;
+            }
+        }
+
+        if ($num > 0) {
+            $monitor['average'] = $average / $num;
+        }
+
+        return $monitor;
+    }
+
+    /**
+     * Get the adapter 
+     * 
+     * @return Zend_Service_Amazon_Ec2_Instance
+     */
+    public function getAdapter()
+    {
+        return $this->ec2;
+    }
+
+    /**
+     * Get last HTTP request
+     * 
+     * @return string 
+     */
+    public function getLastHttpRequest()
+    {
+        return $this->ec2->getHttpClient()->getLastRequest();
+    }
+
+    /**
+     * Get the last HTTP response
+     * 
+     * @return Zend_Http_Response 
+     */
+    public function getLastHttpResponse()
+    {
+        return $this->ec2->getHttpClient()->getLastResponse();
+    }
+}

+ 484 - 0
library/Zend/Cloud/Infrastructure/Adapter/Rackspace.php

@@ -0,0 +1,484 @@
+<?php
+/**
+ * @category   Zend
+ * @package    Zend_Cloud_Infrastructure
+ * @subpackage Adapter
+ * @copyright  Copyright (c) 2005-2011 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-2011 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]);
+        }
+        
+        $this->flavors= $this->rackspace->listFlavors(true);
+    }
+    /**
+     * 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();
+    }
+}

+ 25 - 0
library/Zend/Cloud/Infrastructure/Exception.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * Exception
+ * 
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * Zend_Cloud_Exception
+ */
+require_once 'Zend/Cloud/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cloud_Infrastructure_Exception extends Zend_Cloud_Exception
+{}

+ 65 - 0
library/Zend/Cloud/Infrastructure/Factory.php

@@ -0,0 +1,65 @@
+<?php
+/**
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+require_once 'Zend/Cloud/AbstractFactory.php';
+
+
+/**
+ * Factory for infrastructure adapters
+ * 
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cloud_Infrastructure_Factory extends Zend_Cloud_AbstractFactory
+{
+    const INFRASTRUCTURE_ADAPTER_KEY = 'infrastructure_adapter';
+
+    /**
+     * @var string Interface which adapter must implement to be considered valid
+     */
+    protected static $_adapterInterface = 'Zend_Cloud_Infrastructure_Adapter';
+
+    /**
+     * Constructor
+     *
+     * Private ctor - should not be used
+     *
+     * @return void
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * Retrieve an adapter instance
+     *
+     * @param  array $options
+     * @return void
+     */
+    public static function getAdapter($options = array())
+    {
+        $adapter = parent::_getAdapter(self::INFRASTRUCTURE_ADAPTER_KEY, $options);
+
+        if (!$adapter) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception(sprintf(
+                'Class must be specified using the "%s" key',
+                self::INFRASTRUCTURE_ADAPTER_KEY
+            ));
+        } elseif (!$adapter instanceof self::$_adapterInterface) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception(sprintf(
+                'Adapter must implement "%s"', self::$_adapterInterface
+            ));
+        }
+        return $adapter;
+    }
+}

+ 176 - 0
library/Zend/Cloud/Infrastructure/Image.php

@@ -0,0 +1,176 @@
+<?php
+/**
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * Instance of an infrastructure service
+ * 
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cloud_Infrastructure_Image 
+{
+    const IMAGE_ID           = 'imageId';
+    const IMAGE_OWNERID      = 'ownerId';
+    const IMAGE_NAME         = 'name';
+    const IMAGE_DESCRIPTION  = 'description';
+    const IMAGE_PLATFORM     = 'platform';
+    const IMAGE_ARCHITECTURE = 'architecture';
+    const ARCH_32BIT         = 'i386';
+    const ARCH_64BIT         = 'x86_64';
+    const IMAGE_WINDOWS      = 'windows';
+    const IMAGE_LINUX        = 'linux';
+
+    /**
+     * Image's attributes
+     * 
+     * @var array
+     */
+    protected $attributes = array();
+
+    /**
+     * The Image adapter (if exists)
+     * 
+     * @var object
+     */
+    protected $adapter;
+
+    /**
+     * Required attributes
+     * 
+     * @var array
+     */
+    protected $attributeRequired = array(
+        self::IMAGE_ID, 
+        self::IMAGE_DESCRIPTION, 
+        self::IMAGE_PLATFORM, 
+        self::IMAGE_ARCHITECTURE,
+    );
+
+    /**
+     * Constructor
+     * 
+     * @param array $data
+     * @param object $adapter 
+     */
+    public function __construct($data, $adapter = null) 
+    {
+        if (is_object($data)) {
+            if (method_exists($data, 'toArray')) {
+                $data= $data->toArray();
+            } elseif ($data instanceof Traversable) {
+                $data = iterator_to_array($data);
+            }
+        }
+        
+        if (empty($data) || !is_array($data)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('You must pass an array of parameters');
+        }
+
+        foreach ($this->attributeRequired as $key) {
+            if (empty($data[$key])) {
+                require_once 'Zend/Cloud/Infrastructure/Exception.php';
+                throw new Zend_Cloud_Infrastructure_Exception(sprintf(
+                    'The param "%s" is a required parameter for class %s',
+                    $key,
+                    __CLASS__
+                ));
+            }
+        }
+
+        $this->attributes = $data;
+        $this->adapter    = $adapter;
+    }
+
+    /**
+     * Get Attribute with a specific key
+     *
+     * @param array $data
+     * @return misc|boolean
+     */
+    public function getAttribute($key) 
+    {
+        if (!empty($this->attributes[$key])) {
+            return $this->attributes[$key];
+        }
+        return false;
+    }
+
+    /**
+     * Get all the attributes
+     * 
+     * @return array
+     */
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Get the image ID
+     * 
+     * @return string
+     */
+    public function getId()
+    {
+        return $this->attributes[self::IMAGE_ID];
+    }
+
+    /**
+     * Get the Owner ID
+     * 
+     * @return string
+     */
+    public function getOwnerId()
+    {
+        return $this->attributes[self::IMAGE_OWNERID];
+    }
+
+    /**
+     * Get the name
+     * 
+     * @return string 
+     */
+    public function getName()
+    {
+        return $this->attributes[self::IMAGE_NAME];
+    }
+
+    /**
+     * Get the description
+     * 
+     * @return string 
+     */
+    public function getDescription()
+    {
+        return $this->attributes[self::IMAGE_DESCRIPTION];
+    }
+
+    /**
+     * Get the platform
+     * 
+     * @return string 
+     */
+    public function getPlatform()
+    {
+        return $this->attributes[self::IMAGE_PLATFORM];
+    }
+
+    /**
+     * Get the architecture
+     * 
+     * @return string 
+     */
+    public function getArchitecture()
+    {
+        return $this->attributes[self::IMAGE_ARCHITECTURE];
+    }
+}

+ 218 - 0
library/Zend/Cloud/Infrastructure/ImageList.php

@@ -0,0 +1,218 @@
+<?php
+/**
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+
+require_once 'Zend/Cloud/Infrastructure/Image.php';
+
+/**
+ * List of images
+ *
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cloud_Infrastructure_ImageList implements Countable, Iterator, ArrayAccess
+{
+    /**
+     * @var array Array of Zend_Cloud_Infrastructure_Image
+     */
+    protected $images = array();
+
+    /**
+     * @var int Iterator key
+     */
+    protected $iteratorKey = 0;
+
+    /**
+     * The Image adapter (if exists)
+     * 
+     * @var object
+     */
+    protected $adapter;
+
+    /**
+     * Constructor
+     *
+     * @param  array $list
+     * @param  null|object $adapter
+     * @return boolean
+     */
+    public function __construct($images, $adapter = null)
+    {
+        if (empty($images) || !is_array($images)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception(__CLASS__ . ' expects an array of images');
+        }
+
+        $this->adapter = $adapter;
+        $this->constructFromArray($images);
+    }
+
+    /**
+     * Transforms the Array to array of Instances
+     *
+     * @param  array $list
+     * @return void
+     */
+    protected function constructFromArray(array $list)
+    {
+        foreach ($list as $image) {
+            $this->addImage(new Zend_Cloud_Infrastructure_Image($image, $this->adapter));
+        }
+    }
+
+    /**
+     * Add an image
+     *
+     * @param  Image
+     * @return ImageList
+     */
+    protected function addImage(Zend_Cloud_Infrastructure_Image $image)
+    {
+        $this->images[] = $image;
+        return $this;
+    }
+
+    /**
+     * Return number of images
+     *
+     * Implement Countable::count()
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->images);
+    }
+
+    /**
+     * Return the current element
+     *
+     * Implement Iterator::current()
+     *
+     * @return Image
+     */
+    public function current()
+    {
+        return $this->images[$this->iteratorKey];
+    }
+
+    /**
+     * Return the key of the current element
+     *
+     * Implement Iterator::key()
+     *
+     * @return int
+     */
+    public function key()
+    {
+        return $this->iteratorKey;
+    }
+
+    /**
+     * Move forward to next element
+     *
+     * Implement Iterator::next()
+     *
+     * @return void
+     */
+    public function next()
+    {
+        $this->iteratorKey++;
+    }
+
+    /**
+     * Rewind the Iterator to the first element
+     *
+     * Implement Iterator::rewind()
+     *
+     * @return void
+     */
+    public function rewind()
+    {
+        $this->iteratorKey = 0;
+    }
+
+    /**
+     * Check if there is a current element after calls to rewind() or next()
+     *
+     * Implement Iterator::valid()
+     *
+     * @return bool
+     */
+    public function valid()
+    {
+        $numItems = $this->count();
+        if ($numItems > 0 && $this->iteratorKey < $numItems) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Whether the offset exists
+     *
+     * Implement ArrayAccess::offsetExists()
+     *
+     * @param   int     $offset
+     * @return  bool
+     */
+    public function offsetExists($offset)
+    {
+        return ($offset < $this->count());
+    }
+
+    /**
+     * Return value at given offset
+     *
+     * Implement ArrayAccess::offsetGet()
+     *
+     * @param   int     $offset
+     * @throws  Zend_Cloud_Infrastructure_Exception
+     * @return  Image
+     */
+    public function offsetGet($offset)
+    {
+        if (!$this->offsetExists($offset)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('Illegal index');
+        }
+        return $this->images[$offset];
+    }
+
+    /**
+     * Throws exception because all values are read-only
+     *
+     * Implement ArrayAccess::offsetSet()
+     *
+     * @param   int     $offset
+     * @param   string  $value
+     * @throws  Zend_Cloud_Infrastructure_Exception
+     */
+    public function offsetSet($offset, $value)
+    {
+        require_once 'Zend/Cloud/Infrastructure/Exception.php';
+        throw new Zend_Cloud_Infrastructure_Exception('You are trying to set read-only property');
+    }
+
+    /**
+     * Throws exception because all values are read-only
+     *
+     * Implement ArrayAccess::offsetUnset()
+     *
+     * @param   int     $offset
+     * @throws  Zend_Cloud_Infrastructure_Exception
+     */
+    public function offsetUnset($offset)
+    {
+        require_once 'Zend/Cloud/Infrastructure/Exception.php';
+        throw new Zend_Cloud_Infrastructure_Exception('You are trying to unset read-only property');
+    }
+}

+ 322 - 0
library/Zend/Cloud/Infrastructure/Instance.php

@@ -0,0 +1,322 @@
+<?php
+/**
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * Instance of an infrastructure service
+ *
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cloud_Infrastructure_Instance 
+{
+    const STATUS_RUNNING       = 'running';
+    const STATUS_STOPPED       = 'stopped';
+    const STATUS_SHUTTING_DOWN = 'shutting-down';
+    const STATUS_REBOOTING     = 'rebooting';
+    const STATUS_TERMINATED    = 'terminated';
+    const STATUS_PENDING       = 'pending';
+    const STATUS_REBUILD       = 'rebuild';
+    const INSTANCE_ID          = 'id';
+    const INSTANCE_IMAGEID     = 'imageId';
+    const INSTANCE_NAME        = 'name';
+    const INSTANCE_STATUS      = 'status';
+    const INSTANCE_PUBLICDNS   = 'publicDns';
+    const INSTANCE_CPU         = 'cpu';
+    const INSTANCE_RAM         = 'ram';
+    const INSTANCE_STORAGE     = 'storageSize';
+    const INSTANCE_ZONE        = 'zone';
+    const INSTANCE_LAUNCHTIME  = 'launchTime';
+    const MONITOR_CPU          = 'CpuUsage';
+    const MONITOR_RAM          = 'RamUsage';
+    const MONITOR_NETWORK_IN   = 'NetworkIn';
+    const MONITOR_NETWORK_OUT  = 'NetworkOut';
+    const MONITOR_DISK         = 'DiskUsage';
+    const MONITOR_DISK_WRITE   = 'DiskWrite';
+    const MONITOR_DISK_READ    = 'DiskRead';
+    const MONITOR_START_TIME   = 'StartTime';
+    const MONITOR_END_TIME     = 'EndTime';
+    const SSH_USERNAME         = 'username';
+    const SSH_PASSWORD         = 'password';
+    const SSH_PRIVATE_KEY      = 'privateKey';
+    const SSH_PUBLIC_KEY       = 'publicKey';
+    const SSH_PASSPHRASE       = 'passphrase';
+
+    /**
+     * @var Zend_Cloud_Infrastructure_Adapter
+     */
+    protected $adapter;
+
+    /**
+     * Instance's attribute
+     * 
+     * @var array 
+     */
+    protected $attributes;
+
+    /**
+     * Attributes required for an instance
+     * 
+     * @var array 
+     */
+    protected $attributeRequired = array(
+        self::INSTANCE_ID,
+        self::INSTANCE_STATUS,
+        self::INSTANCE_IMAGEID,
+        self::INSTANCE_ZONE,
+        self::INSTANCE_RAM,
+        self::INSTANCE_STORAGE,
+    );
+
+    /**
+     * Constructor
+     * 
+     * @param  Adapter $adapter
+     * @param  array $data 
+     * @return void
+     */
+    public function __construct($adapter, $data = null)
+    {
+        if (!($adapter instanceof Zend_Cloud_Infrastructure_Adapter)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception("You must pass a Zend_Cloud_Infrastructure_Adapter instance");
+        }
+
+        if (is_object($data)) {
+            if (method_exists($data, 'toArray')) {
+                $data= $data->toArray();
+            } elseif ($data instanceof Traversable) {
+                $data = iterator_to_array($data);
+            }
+        }
+        
+        if (empty($data) || !is_array($data)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception("You must pass an array of parameters");
+        }
+
+        foreach ($this->attributeRequired as $key) {
+            if (empty($data[$key])) {
+                require_once 'Zend/Cloud/Infrastructure/Exception.php';
+                throw new Zend_Cloud_Infrastructure_Exception(sprintf(
+                    'The param "%s" is a required param for %s', 
+                    $key,
+                    __CLASS__
+                ));
+            }
+        }
+
+        $this->adapter    = $adapter;
+        $this->attributes = $data;
+    }
+
+    /**
+     * Get Attribute with a specific key
+     *
+     * @param array $data
+     * @return misc|false
+     */
+    public function getAttribute($key) 
+    {
+        if (!empty($this->attributes[$key])) {
+            return $this->attributes[$key];
+        }
+        return false;
+    }
+
+    /**
+     * Get all the attributes
+     * 
+     * @return array
+     */
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Get the instance's id
+     * 
+     * @return string 
+     */
+    public function getId()
+    {
+        return $this->attributes[self::INSTANCE_ID];
+    }
+
+    /**
+     * Get the instance's image id
+     * 
+     * @return string 
+     */
+    public function getImageId()
+    {
+        return $this->attributes[self::INSTANCE_IMAGEID];
+    }
+
+    /**
+     * Get the instance's name
+     * 
+     * @return string 
+     */
+    public function getName()
+    {
+        return $this->attributes[self::INSTANCE_NAME];
+    }
+
+    /**
+     * Get the status of the instance
+     * 
+     * @return string|boolean 
+     */
+    public function getStatus()
+    {
+        return $this->adapter->statusInstance($this->attributes[self::INSTANCE_ID]);
+    }
+
+    /**
+     * Wait for status $status with a timeout of $timeout seconds
+     * 
+     * @param  string $status
+     * @param  integer $timeout 
+     * @return boolean
+     */
+    public function waitStatus($status, $timeout = Adapter::TIMEOUT_STATUS_CHANGE)
+    {
+        return $this->adapter->waitStatusInstance($this->attributes[self::INSTANCE_ID], $status, $timeout);
+    }
+
+    /**
+     * Get the public DNS of the instance
+     * 
+     * @return string 
+     */
+    public function getPublicDns()
+    {
+        if (!isset($this->attributes[self::INSTANCE_PUBLICDNS])) {
+            $this->attributes[self::INSTANCE_PUBLICDNS] =  $this->adapter->publicDnsInstance($this->attributes[self::INSTANCE_ID]);
+        }
+        return $this->attributes[self::INSTANCE_PUBLICDNS];
+    }
+
+    /**
+     * Get the instance's CPU
+     * 
+     * @return string
+     */
+    public function getCpu()
+    {
+        return $this->attributes[self::INSTANCE_CPU];
+    }
+
+    /**
+     * Get the instance's RAM size
+     * 
+     * @return string
+     */
+    public function getRamSize()
+    {
+        return $this->attributes[self::INSTANCE_RAM];
+    }
+
+    /**
+     * Get the instance's storage size
+     * 
+     * @return string
+     */
+    public function getStorageSize()
+    {
+        return $this->attributes[self::INSTANCE_STORAGE];
+    }
+
+    /**
+     * Get the instance's zone
+     * 
+     * @return string 
+     */
+    public function getZone()
+    {
+        return $this->attributes[self::INSTANCE_ZONE];
+    }
+
+    /**
+     * Get the instance's launch time
+     * 
+     * @return string
+     */
+    public function getLaunchTime()
+    {
+        return $this->attributes[self::INSTANCE_LAUNCHTIME];
+    }
+
+    /**
+     * Reboot the instance
+     * 
+     * @return boolean 
+     */
+    public function reboot()
+    {
+        return $this->adapter->rebootInstance($this->attributes[self::INSTANCE_ID]);
+    }
+
+    /**
+     * Stop the instance
+     * 
+     * @return boolean 
+     */
+    public function stop()
+    {
+        return $this->adapter->stopInstance($this->attributes[self::INSTANCE_ID]);
+    }
+
+    /**
+     * Start the instance
+     * 
+     * @return boolean 
+     */
+    public function start()
+    {
+        return $this->adapter->startInstance($this->attributes[self::INSTANCE_ID]);
+    }
+
+    /**
+     * Destroy the instance
+     * 
+     * @return boolean 
+     */
+    public function destroy()
+    {
+        return $this->adapter->destroyInstance($this->attributes[self::INSTANCE_ID]);
+    }
+
+    /**
+     * Return the system informations about the $metric of an instance
+     * 
+     * @param  string $metric
+     * @param  null|array $options
+     * @return array|boolean
+     */ 
+    public function monitor($metric, $options = null)
+    {
+        return $this->adapter->monitorInstance($this->attributes[self::INSTANCE_ID], $metric, $options);
+    }
+
+    /**
+     * Run arbitrary shell script on the instance
+     *
+     * @param  array $param
+     * @param  string|array $cmd
+     * @return string|array
+     */
+    public function deploy($params, $cmd)
+    {
+        return $this->adapter->deployInstance($this->attributes[self::INSTANCE_ID], $params, $cmd);
+    }
+}

+ 219 - 0
library/Zend/Cloud/Infrastructure/InstanceList.php

@@ -0,0 +1,219 @@
+<?php
+/**
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+require_once 'Zend/Cloud/Infrastructure/Instance.php';
+
+/**
+ * List of instances
+ *
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cloud_Infrastructure_InstanceList implements Countable, Iterator, ArrayAccess
+{
+    /**
+     * @var array Array of Zend_Cloud_Infrastructure_Instance
+     */
+    protected $instances = array();
+
+    /**
+     * @var int Iterator key
+     */
+    protected $iteratorKey = 0;
+
+    /**
+     * @var Zend_Cloud_Infrastructure_Adapter
+     */
+    protected $adapter;
+
+    /**
+     * Constructor
+     *
+     * @param  Adapter $adapter
+     * @param  array $instances
+     * @return void
+     */
+    public function __construct($adapter, array $instances = null)
+    {
+        if (!($adapter instanceof Zend_Cloud_Infrastructure_Adapter)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('You must pass a Zend_Cloud_Infrastructure_Adapter');
+        }
+        if (empty($instances)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('You must pass an array of Instances');
+        }
+
+        $this->adapter = $adapter;
+        $this->constructFromArray($instances);
+    }
+
+    /**
+     * Transforms the Array to array of Instances
+     *
+     * @param  array $list
+     * @return void
+     */
+    protected function constructFromArray(array $list)
+    {
+        foreach ($list as $instance) {
+            $this->addInstance(new Zend_Cloud_Infrastructure_Instance($this->adapter,$instance));
+        }
+    }
+
+    /**
+     * Add an instance
+     *
+     * @param  Instance
+     * @return InstanceList
+     */
+    protected function addInstance(Zend_Cloud_Infrastructure_Instance $instance)
+    {
+        $this->instances[] = $instance;
+        return $this;
+    }
+
+    /**
+     * Return number of instances
+     *
+     * Implement Countable::count()
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->instances);
+    }
+
+    /**
+     * Return the current element
+     *
+     * Implement Iterator::current()
+     *
+     * @return Instance
+     */
+    public function current()
+    {
+        return $this->instances[$this->iteratorKey];
+    }
+
+    /**
+     * Return the key of the current element
+     *
+     * Implement Iterator::key()
+     *
+     * @return int
+     */
+    public function key()
+    {
+        return $this->iteratorKey;
+    }
+
+    /**
+     * Move forward to next element
+     *
+     * Implement Iterator::next()
+     *
+     * @return void
+     */
+    public function next()
+    {
+        $this->iteratorKey++;
+    }
+
+    /**
+     * Rewind the Iterator to the first element
+     *
+     * Implement Iterator::rewind()
+     *
+     * @return void
+     */
+    public function rewind()
+    {
+        $this->iteratorKey = 0;
+    }
+
+    /**
+     * Check if there is a current element after calls to rewind() or next()
+     *
+     * Implement Iterator::valid()
+     *
+     * @return bool
+     */
+    public function valid()
+    {
+        $numItems = $this->count();
+        if ($numItems > 0 && $this->iteratorKey < $numItems) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Whether the offset exists
+     *
+     * Implement ArrayAccess::offsetExists()
+     *
+     * @param  int $offset
+     * @return bool
+     */
+    public function offsetExists($offset)
+    {
+        return ($offset < $this->count());
+    }
+
+    /**
+     * Return value at given offset
+     *
+     * Implement ArrayAccess::offsetGet()
+     *
+     * @param  int $offset
+     * @return Instance
+     * @throws Zend_Cloud_Infrastructure_Exception
+     */
+    public function offsetGet($offset)
+    {
+        if (!$this->offsetExists($offset)) {
+            require_once 'Zend/Cloud/Infrastructure/Exception.php';
+            throw new Zend_Cloud_Infrastructure_Exception('Illegal index');
+        }
+        return $this->instances[$offset];
+    }
+
+    /**
+     * Throws exception because all values are read-only
+     *
+     * Implement ArrayAccess::offsetSet()
+     *
+     * @param   int     $offset
+     * @param   string  $value
+     * @throws  Zend_Cloud_Infrastructure_Exception
+     */
+    public function offsetSet($offset, $value)
+    {
+        require_once 'Zend/Cloud/Infrastructure/Exception.php';
+        throw new Zend_Cloud_Infrastructure_Exception('You are trying to set read-only property');
+    }
+
+    /**
+     * Throws exception because all values are read-only
+     *
+     * Implement ArrayAccess::offsetUnset()
+     *
+     * @param   int     $offset
+     * @throws  Zend_Cloud_Infrastructure_Exception
+     */
+    public function offsetUnset($offset)
+    {
+        require_once 'Zend/Cloud/Infrastructure/Exception.php';
+        throw new Zend_Cloud_Infrastructure_Exception('You are trying to unset read-only property');
+    }
+}

+ 2 - 2
library/Zend/Service/Amazon/Ec2/Abstract.php

@@ -142,7 +142,7 @@ abstract class Zend_Service_Amazon_Ec2_Abstract extends Zend_Service_Amazon_Abst
     /**
      * Sends a HTTP request to the queue service using Zend_Http_Client
      *
-     * @param array $params         List of parameters to send with the request
+     * @param  array $params List of parameters to send with the request
      * @return Zend_Service_Amazon_Ec2_Response
      * @throws Zend_Service_Amazon_Ec2_Exception
      */
@@ -166,7 +166,7 @@ abstract class Zend_Service_Amazon_Ec2_Abstract extends Zend_Service_Amazon_Abst
             $request->setParameterPost($params);
 
             $httpResponse = $request->request();
-
+            
 
         } catch (Zend_Http_Client_Exception $zhce) {
             $message = 'Error in request to AWS service: ' . $zhce->getMessage();

+ 4 - 1
library/Zend/Service/Amazon/Ec2/Instance.php

@@ -38,6 +38,10 @@ require_once 'Zend/Service/Amazon/Ec2/Abstract.php';
 class Zend_Service_Amazon_Ec2_Instance extends Zend_Service_Amazon_Ec2_Abstract
 {
     /**
+     * Constant for Micro Instance Type
+     */
+    const MICRO = 't1.micro';
+    /**
      * Constant for Small Instance TYpe
      */
     const SMALL = 'm1.small';
@@ -168,7 +172,6 @@ class Zend_Service_Amazon_Ec2_Instance extends Zend_Service_Amazon_Ec2_Abstract
         if(isset($options['monitor']) && $options['monitor'] === true) {
             $params['Monitoring.Enabled'] = true;
         }
-
         $response = $this->sendRequest($params);
         $xpath = $response->getXPath();
 

+ 1 - 1
library/Zend/Service/Amazon/Ec2/Response.php

@@ -118,7 +118,7 @@ class Zend_Service_Amazon_Ec2_Response {
         } catch (Zend_Http_Exception $e) {
             $body = false;
         }
-
+        
         if ($this->_document === null) {
             if ($body !== false) {
                 // turn off libxml error handling

+ 74 - 0
tests/Zend/Cloud/Infrastructure/Adapter/AllTests.php

@@ -0,0 +1,74 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cloud_DocumentService
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Zend_Cloud_Infrastructure_Adapter_AllTests::main');
+}
+
+/**
+ * @see Zend_Cloud_Infrastructure_Adapter_Ec2
+ */
+require_once 'Zend/Cloud/Infrastructure/Adapter/Ec2Test.php';
+
+/**
+ * @see Zend_Cloud_Infrastructure_Adapter_Rackspace
+ */
+require_once 'Zend/Cloud/Infrastructure/Adapter/RackspaceTest.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Cloud_Infrastructure_Adapter
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cloud_Infrastructure_Adapter_AllTests
+{
+    /**
+     * Runs this test suite
+     *
+     * @return void
+     */
+    public static function main()
+    {
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    /**
+     * Creates and returns this test suite
+     *
+     * @return PHPUnit_Framework_TestSuite
+     */
+    public static function suite()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('Zend Framework - Zend_Cloud');
+
+        $suite->addTestSuite('Zend_Cloud_Infrastructure_Adapter_Ec2Test');
+        $suite->addTestSuite('Zend_Cloud_Infrastructure_Adapter_RackspaceTest');
+
+        return $suite;
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Zend_Cloud_Infrastructure_Adapter_AllTests::main') {
+    Zend_Cloud_Infrastructure_Adapter_AllTests::main();
+}

+ 61 - 0
tests/Zend/Cloud/Infrastructure/AllTests.php

@@ -0,0 +1,61 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Zend_Cloud_Infrastructure_AllTests::main');
+}
+
+require_once 'Zend/Cloud/Infrastructure/Adapter/AllTests.php';
+
+/**
+ * @see Zend_Cloud_Infrastructure_FactoryTest
+ */
+require_once 'Zend/Cloud/Infrastructure/FactoryTest.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Cloud
+ */
+class Zend_Cloud_Infrastructure_AllTests
+{
+    public static function main()
+    {
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    public static function suite()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('Zend Framework - Zend_Cloud_Infrastructure');
+
+        $suite->addTestSuite('Zend_Cloud_Infrastructure_FactoryTest');
+        $suite->addTest(Zend_Cloud_Infrastructure_Adapter_AllTests::suite());
+
+        return $suite;
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Zend_Cloud_Infrastructure_AllTests::main') {
+    Zend_Cloud_Infrastructure_AllTests::main();
+}

+ 90 - 0
tests/Zend/Cloud/Infrastructure/FactoryTest.php

@@ -0,0 +1,90 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+// Call Zend_Cloud_Infrastructure_FactoryTest::main() if this source file is executed directly.
+if (!defined("PHPUnit_MAIN_METHOD")) {
+    define("PHPUnit_MAIN_METHOD", "Zend_Cloud_Infrastructure_FactoryTest::main");
+}
+
+/**
+ * @see Zend_Config_Ini
+ */
+require_once 'Zend/Config/Ini.php';
+
+/**
+ * @see Zend_Cloud_Infrastructure_Factory
+ */
+require_once 'Zend/Cloud/Infrastructure/Factory.php';
+
+/**
+ * @see Zend_Cloud_Infrastructure_Adapter_Ec2
+ */
+require_once 'Zend/Cloud/Infrastructure/Adapter/Ec2.php';
+
+/**
+ * Test class for Zend_Cloud_Infrastructure_Factory
+ *
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Cloud
+ */
+class Zend_Cloud_Infrastructure_FactoryTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * Runs the test methods of this class.
+     *
+     * @return void
+     */
+    public static function main()
+    {
+        $suite  = new PHPUnit_Framework_TestSuite(__CLASS__);
+        $result = PHPUnit_TextUI_TestRunner::run($suite);
+    }
+
+    public function testGetInfrastructureAdapterKey()
+    {
+        $this->assertTrue(is_string(Zend_Cloud_Infrastructure_Factory::INFRASTRUCTURE_ADAPTER_KEY));
+    }
+
+    public function testGetAdapterWithConfig() {
+        // EC2 adapter
+        $Ec2Adapter = Zend_Cloud_Infrastructure_Factory::getAdapter(
+                                    new Zend_Config(Zend_Cloud_Infrastructure_Adapter_Ec2Test::getConfigArray())
+                                );
+
+        $this->assertEquals('Zend_Cloud_Infrastructure_Adapter_Ec2', get_class($Ec2Adapter));
+        
+        // Rackspace adapter
+        $rackspaceAdapter = Zend_Cloud_Infrastructure_Factory::getAdapter(
+                                    new Zend_Config(Zend_Cloud_Infrastructure_Adapter_RackspaceTest::getConfigArray())
+                                );
+
+        $this->assertEquals('Zend_Cloud_Infrastructure_Adapter_Rackspace', get_class($rackspaceAdapter));
+    }
+}
+
+// Call Zend_Cloud_Infrastructure_FactoryTest::main() if this source file is executed directly.
+if (PHPUnit_MAIN_METHOD == "Zend_Cloud_Infrastructure_FactoryTest::main") {
+    Zend_Cloud_Infrastructure_FactoryTest::main();
+}

+ 295 - 0
tests/Zend/Cloud/Infrastructure/TestCase.php

@@ -0,0 +1,295 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage Infrastructure
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * @see Zend_Cloud_Infrastructure_Adapter
+ */
+require_once 'Zend/Cloud/Infrastructure/Adapter.php';
+
+/**
+ * @see Zend_Cloud_Infrastructure_Instance
+ */
+require_once 'Zend/Cloud/Infrastructure/Instance.php';
+
+
+/**
+ * This class forces the adapter tests to implement tests for all methods on
+ * Zend_Cloud_Infrastructure.
+ *
+ * @category   Zend
+ * @package    Zend_Cloud
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Cloud_Infrastructure_TestCase extends PHPUnit_Framework_TestCase
+{
+    /**
+     * Reference to Document adapter to test
+     *
+     * @var Zend_Cloud_Infrastructure
+     */
+    protected $_commonInfrastructure;
+
+    protected $_dummyCollectionNamePrefix = 'TestCollection';
+
+    protected $_dummyDataPrefix = 'TestData';
+
+    protected $_clientType = 'stdClass';
+
+    const ID_FIELD = "__id";
+
+    /**
+     * Config object
+     *
+     * @var Zend_Config
+     */
+
+    protected $_config;
+
+    /**
+     * Period to wait for propagation in seconds
+     * Should be set by adapter
+     *
+     * @var int
+     */
+    protected $_waitPeriod = 1;
+
+    public function testInfrastructure()
+    {
+        $this->assertTrue($this->_commonInfrastructure instanceof Zend_Cloud_Infrastructure_Adapter);
+    }
+
+    public function testGetClient()
+    {
+    	$this->assertTrue(is_a($this->_commonInfrastructure->getClient(), $this->_clientType));
+    }
+
+    /**
+     * Test all the constants of the class
+     */
+    public function testConstants()
+    {
+        $this->assertEquals('running', Zend_Cloud_Infrastructure_Instance::STATUS_RUNNING);
+        $this->assertEquals('stopped', Zend_Cloud_Infrastructure_Instance::STATUS_STOPPED);
+        $this->assertEquals('shutting-down', Zend_Cloud_Infrastructure_Instance::STATUS_SHUTTING_DOWN);
+        $this->assertEquals('rebooting', Zend_Cloud_Infrastructure_Instance::STATUS_REBOOTING);
+        $this->assertEquals('terminated', Zend_Cloud_Infrastructure_Instance::STATUS_TERMINATED);
+        $this->assertEquals('id', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_ID);
+        $this->assertEquals('imageId', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_IMAGEID);
+        $this->assertEquals('name', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_NAME);
+        $this->assertEquals('status', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_STATUS);
+        $this->assertEquals('publicDns', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_PUBLICDNS);
+        $this->assertEquals('cpu', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_CPU);
+        $this->assertEquals('ram', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_RAM);
+        $this->assertEquals('storageSize', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_STORAGE);
+        $this->assertEquals('zone', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_ZONE);
+        $this->assertEquals('launchTime', Zend_Cloud_Infrastructure_Instance::Zend_Cloud_Infrastructure_Instance_LAUNCHTIME);
+        $this->assertEquals('CpuUsage', Zend_Cloud_Infrastructure_Instance::MONITOR_CPU);
+        $this->assertEquals('NetworkIn', Zend_Cloud_Infrastructure_Instance::MONITOR_NETWORK_IN);
+        $this->assertEquals('NetworkOut', Zend_Cloud_Infrastructure_Instance::MONITOR_NETWORK_OUT);
+        $this->assertEquals('DiskWrite', Zend_Cloud_Infrastructure_Instance::MONITOR_DISK_WRITE);
+        $this->assertEquals('DiskRead', Zend_Cloud_Infrastructure_Instance::MONITOR_DISK_READ);
+        $this->assertEquals('StartTime', Zend_Cloud_Infrastructure_Instance::MONITOR_START_TIME);
+        $this->assertEquals('EndTime', Zend_Cloud_Infrastructure_Instance::MONITOR_END_TIME);
+        $this->assertEquals('username', Zend_Cloud_Infrastructure_Instance::SSH_USERNAME);
+        $this->assertEquals('password', Zend_Cloud_Infrastructure_Instance::SSH_PASSWORD);
+        $this->assertEquals('privateKey', Zend_Cloud_Infrastructure_Instance::SSH_PRIVATE_KEY);
+        $this->assertEquals('publicKey', Zend_Cloud_Infrastructure_Instance::SSH_PUBLIC_KEY);
+        $this->assertEquals('passphrase', Zend_Cloud_Infrastructure_Instance::SSH_PASSPHRASE);
+    }
+
+    /**
+     * Test construct with missing params
+     */
+    public function testConstructExceptionMissingParams() 
+    {
+        $this->setExpectedException(
+            'Zend\Cloud\Infrastructure\Exception\InvalidArgumentException',
+            'You must pass an array of params'
+        );
+        $instance = new Zend_Cloud_Infrastructure_Instance(self::$adapter,array());
+    }
+
+    /**
+     * Test construct with invalid keys in the params
+     */
+    public function testConstructExceptionInvalidKeys()
+    {
+        $this->setExpectedException(
+            'Zend\Cloud\Infrastructure\Exception\InvalidArgumentException',
+            'The param "'.Zend_Cloud_Infrastructure_Instance::INSTANCE_ID.'" is a required param for Zend\Cloud\Infrastructure\Instance'
+        );
+        $instance = new Zend_Cloud_Infrastructure_Instance(self::$adapter,array('foo'=>'bar'));
+    }
+
+    /**
+     * Test get Id
+     */
+    public function testGetId()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getId());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_ID));
+    }
+
+    /**
+     * Test get Image Id
+     */
+    public function testGetImageId()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getImageId());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_IMAGEID));
+    }
+
+    /**
+     * Test get name
+     */
+    public function testGetName()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getName());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_NAME));
+    }
+
+    /**
+     * Test get status
+     */
+    public function testGetStatus()
+    {
+        $this->assertEquals('ZendTest\Cloud\Infrastructure\TestAsset\MockAdapter::statusInstance',$this->_commonInfrastructure->getStatus());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_STATUS));
+    }
+
+    /**
+     * Test get public DNS
+     */
+    public function testGetPublicDns()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getPublicDns());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_PUBLICDNS));
+    }
+
+    /**
+     * Test get CPU
+     */
+    public function testGetCpu()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getCpu());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_CPU));
+    }
+
+    /**
+     * Test get RAM size
+     */
+    public function testGetRam()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getRamSize());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_RAM));
+    }
+
+    /**
+     * Test get storage size (disk)
+     */
+    public function testGetStorageSize()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getStorageSize());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_STORAGE));
+    }
+
+    /**
+     * Test get zone
+     */
+    public function testGetZone()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getZone());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_ZONE));
+    }
+
+    /**
+     * Test get the launch time of the instance
+     */
+    public function testGetLaunchTime()
+    {
+        $this->assertEquals('foo',$this->_commonInfrastructure->getLaunchTime());
+        $this->assertEquals('foo',$this->_commonInfrastructure->getAttribute(Instance::INSTANCE_LAUNCHTIME));
+    }
+
+    /**
+     * Test reboot
+     */
+    public function testReboot()
+    {
+        $this->assertEquals('ZendTest\Cloud\Infrastructure\TestAsset\MockAdapter::rebootInstance',$this->_commonInfrastructure->reboot());
+    }
+
+    /**
+     * Test stop
+     */
+    public function testStop()
+    {
+        $this->assertEquals('ZendTest\Cloud\Infrastructure\TestAsset\MockAdapter::stopInstance',$this->_commonInfrastructure->stop());
+    }
+
+    /**
+     * Test start
+     */
+    public function testStart()
+    {
+        $this->assertEquals('ZendTest\Cloud\Infrastructure\TestAsset\MockAdapter::startInstance',$this->_commonInfrastructure->start());
+    }
+
+    /**
+     * Test destroy
+     */
+    public function testDestroy()
+    {
+        $this->assertEquals('ZendTest\Cloud\Infrastructure\TestAsset\MockAdapter::destroyInstance',$this->_commonInfrastructure->destroy());
+    }
+
+    /**
+     * Test monitor
+     */
+    public function testMonitor()
+    {
+        $this->assertEquals('ZendTest\Cloud\Infrastructure\TestAsset\MockAdapter::monitorInstance',$this->_commonInfrastructure->monitor('foo'));
+    }
+
+    /**
+     * Test deploy
+     */
+    public function testDeploy()
+    {
+        $this->assertEquals('ZendTest\Cloud\Infrastructure\TestAsset\MockAdapter::deployInstance',$this->_commonInfrastructure->deploy('foo','bar'));
+    }
+
+    public function setUp()
+    {
+        $this->_config = $this->_getConfig();
+        $this->_commonInfrastructure = Zend_Cloud_Infrastructure_Factory::getAdapter($this->_config);
+        parent::setUp();
+    }
+
+    abstract protected function _getConfig();
+
+    protected function _wait() {
+        sleep($this->_waitPeriod);
+    }
+
+}