Răsfoiți Sursa

Added basic compute service support to ec2.

nuwan_ag 10 ani în urmă
părinte
comite
7a4398dfa3

+ 136 - 2
cloudbridge/providers/ec2/__init__.py

@@ -8,8 +8,15 @@ from boto.ec2.regioninfo import RegionInfo
 
 from cloudbridge.providers.base import BaseCloudProvider
 from cloudbridge.providers.base import BaseSecurityGroup
+from cloudbridge.providers.base import BaseKeyPair
 from cloudbridge.providers.interfaces import SecurityService
+from cloudbridge.providers.interfaces import ComputeService
 from cloudbridge.providers.interfaces import KeyPair
+from cloudbridge.providers.interfaces import MachineImage
+from cloudbridge.providers.interfaces import SecurityGroup
+from cloudbridge.providers.interfaces import PlacementZone
+from cloudbridge.providers.interfaces import InstanceType
+from cloudbridge.providers.interfaces import Instance
 
 
 class EC2CloudProviderV1(BaseCloudProvider):
@@ -32,7 +39,7 @@ class EC2CloudProviderV1(BaseCloudProvider):
         self.ec2_conn = self._connect_ec2()
 
         # Initialize provider services
-        self.compute = None  # EC2ComputeService(self)
+        self.compute = EC2ComputeService(self)
         self.images = None  # EC2ImageService(self)
         self.security = EC2SecurityService(self)
         self.block_store = None  # EC2BlockStore(self)
@@ -68,7 +75,7 @@ class EC2SecurityService(SecurityService):
         :return:  list of KeyPair objects
         """
         key_pairs = self.provider.ec2_conn.get_all_key_pairs()
-        return [KeyPair(kp.name) for kp in key_pairs]
+        return [BaseKeyPair(kp.name) for kp in key_pairs]
 
     def list_security_groups(self):
         """
@@ -79,3 +86,130 @@ class EC2SecurityService(SecurityService):
         """
         groups = self.provider.ec2_conn.get_all_security_groups()
         return [BaseSecurityGroup(group.name) for group in groups]
+
+
+class EC2Instance(Instance):
+
+    def __init__(self, provider, ec2_instance):
+        self.provider = provider
+        self._ec2_instance = ec2_instance
+
+    def instance_id(self):
+        """
+        Get the instance identifier.
+        """
+        return self._ec2_instance.id
+
+    @property
+    def name(self):
+        """
+        Get the instance name.
+        """
+        return self._ec2_instance.tags['Name']
+
+    @name.setter
+    def name(self, value):
+        """
+        Set the instance name.
+        """
+        self._ec2_instance.add_tag('Name', value)
+
+    def public_ips(self):
+        """
+        Get all the public IP addresses for this instance.
+        """
+        return [self._ec2_instance.ip_address]
+
+    def private_ips(self):
+        """
+        Get all the private IP addresses for this instance.
+        """
+        return [self._ec2_instance.private_ip_address]
+
+    def instance_type(self):
+        """
+        Get the instance type.
+        """
+        return [self._ec2_instance.instance_type]
+
+    def reboot(self):
+        """
+        Reboot this instance (using the cloud middleware API).
+        """
+        self._ec2_instance.reboot()
+
+    def terminate(self):
+        """
+        Permanently terminate this instance.
+        """
+        self._ec2_instance.terminate()
+
+    def image_id(self):
+        """
+        Get the image ID for this insance.
+        """
+        return self._ec2_instance.id
+
+    def placement_zone(self):
+        """
+        Get the placement zone where this instance is running.
+        """
+        return self._ec2_instance.id
+
+    def mac_address(self):
+        """
+        Get the MAC address for this instance.
+        """
+        raise NotImplementedError(
+            'mac_address not implemented by this provider')
+
+    def security_group_ids(self):
+        """
+        Get the security group IDs associated with this instance.
+        """
+        return [BaseSecurityGroup(group.name) for group in self._ec2_instance.groups]
+
+    def key_pair_name(self):
+        """
+        Get the name of the key pair associated with this instance.
+        """
+        return BaseKeyPair(self._ec2_instance.key_name)
+
+
+class EC2ComputeService(ComputeService):
+
+    def __init__(self, provider):
+        self.provider = provider
+
+    def create_instance(self, name, image, instance_type, zone=None, keypair=None, security_groups=None,
+                        user_data=None, block_device_mapping=None, network_interfaces=None, **kwargs):
+        """
+        Creates a new virtual machine instance.
+        """
+        image_id = image.image_id if isinstance(image, MachineImage) else image
+        instance_size = instance_type.name if isinstance(
+            instance_type,
+            InstanceType) else instance_type
+        zone_name = zone.name if isinstance(zone, PlacementZone) else zone
+        keypair_name = keypair.name if isinstance(keypair, KeyPair) else keypair
+        if security_groups:
+            if isinstance(security_groups, list) and isinstance(security_groups[0], SecurityGroup):
+                security_groups_list = [sg.name for sg in security_groups]
+            else:
+                security_groups_list = security_groups
+        else:
+            security_groups_list = None
+
+        reservation = self.provider.ec2_conn.run_instances(image_id=image_id,
+                                                           instance_type=instance_size,
+                                                           min_count=1,
+                                                           max_count=1,
+                                                           placement=zone_name,
+                                                           key_name=keypair_name,
+                                                           security_groups=security_groups_list,
+                                                           user_data=user_data
+                                                           )
+        if reservation:
+            instance = EC2Instance(self.provider, reservation.instances[0])
+            instance.name = name
+        return instance

+ 91 - 17
cloudbridge/providers/interfaces.py

@@ -200,10 +200,38 @@ class ComputeService(ProviderService):
         raise NotImplementedError(
             'list_regions not implemented by this provider')
 
-    def create_instance(self):
+    def create_instance(self, name, image, instance_type, zone=None, keypair=None, security_groups=None,
+                        user_data=None, block_device_mapping=None, network_interfaces=None, **kwargs):
         """
         Creates a new virtual machine instance.
 
+        :type  name: ``str``
+        :param name: The name of the virtual machine instance
+
+        :type  image: ``MachineImage`` or ``str``
+        :param image: The MachineImage object or id to boot the virtual machine with
+
+        :type  instance_type: ``InstanceType`` or ``str``
+        :param instance_type: The InstanceType or name, specifying the size of the instance to boot into
+
+        :type  zone: ``Zone`` or ``str``
+        :param zone: The Zone or its name, where the instance should be placed.
+
+        :type  keypair: ``KeyPair`` or ``str``
+        :param keypair: The KeyPair object or its name, to set for the instance
+
+        :type  security_groups: A ``list`` of ``SecurityGroup`` objects or a list of ``str`` names
+        :param security_groups: A list of SecurityGroup objects or a list of SecurityGroup names, which should be assigned to this instance.
+
+        :type  user_data: ``str``
+        :param user_data: An extra userdata object which is compatible with the provider.
+
+        :type  block_device_mapping: ``BlockDeviceMapping`` object
+        :param block_device_mapping: A ``BlockDeviceMapping`` object which describes additional block device mappings for this instance
+
+        :type  network_interfaces: ``NetworkInterfaceList`` object
+        :param network_interfaces: A ``NetworkInterfaceList`` object which describes network interfaces for this instance
+
         :rtype: `object`` of :class:`.Instance`
         :return:  an instance of Instance class
         """
@@ -459,7 +487,7 @@ class Instance(object):
         raise NotImplementedError(
             'private_ips not implemented by this provider')
 
-    def type(self):
+    def instance_type(self):
         """
         Get the instance type.
 
@@ -500,9 +528,9 @@ class Instance(object):
         raise NotImplementedError(
             'image_id not implemented by this provider')
 
-    def placement(self):
+    def placement_zone(self):
         """
-        Get the placement where this instance is running.
+        Get the placement zone where this instance is running.
 
         :rtype: str
         :return: Region/zone/placement where this instance is running.
@@ -541,6 +569,39 @@ class Instance(object):
             'key_pair_name not implemented by this provider')
 
 
+class MachineImage(object):
+
+    def image_id(self):
+        """
+        Get the image identifier.
+
+        :rtype: ``str``
+        :return: ID for this instance as returned by the cloud middleware.
+        """
+        raise NotImplementedError(
+            'MachineImage.image_id not implemented by this provider')
+
+    def name(self):
+        """
+        Get the image name.
+
+        :rtype: ``str``
+        :return: Name for this image as returned by the cloud middleware.
+        """
+        raise NotImplementedError(
+            'MachineImage.name not implemented by this provider')
+
+    def description(self):
+        """
+        Get the image description.
+
+        :rtype: ``str``
+        :return: Description for this image as returned by the cloud middleware
+        """
+        raise NotImplementedError(
+            'MachineImage.description not implemented by this provider')
+
+
 class Volume(object):
 
     def attach(self, instance_id, device):
@@ -683,22 +744,35 @@ class Region(object):
 
 class KeyPair(object):
 
-    def __init__(self, name, material=None):
-        self.name = name
-        self.material = material
+    def name(self):
+        raise NotImplementedError(
+            'name not implemented by this provider')
+
+    def meterial(self):
+        raise NotImplementedError(
+            'meterial not implemented by this provider')
 
-    def __repr__(self):
-        return "CloudBridge KeyPair:{0}".format(self.name)
 
-    # def name(self):
-    #     """
-    #     Return the name of this key pair.
+class PlacementZone(object):
 
-    #     :rtype: str
-    #     :return: A name of this ssh key pair
-    #     """
-    #     raise NotImplementedError(
-    #         'name not implemented by this provider')
+    """
+    A placement zone object.
+    """
+
+    def name(self):
+        raise NotImplementedError(
+            'PlacementZone.name not implemented by this provider')
+
+
+class InstanceType(object):
+
+    """
+    An instance type object.
+    """
+
+    def name(self):
+        raise NotImplementedError(
+            'InstanceType.name not implemented by this provider')
 
 
 class SecurityGroup(object):

+ 6 - 2
cloudbridge/providers/openstack/__init__.py

@@ -10,7 +10,10 @@ from keystoneclient.auth.identity import Password
 
 from cloudbridge.providers.base import BaseCloudProvider
 from cloudbridge.providers.base import BaseSecurityGroup
+from cloudbridge.providers.base import BaseKeyPair
+from cloudbridge.providers.interfaces import Instance
 from cloudbridge.providers.interfaces import SecurityService
+from cloudbridge.providers.interfaces import ComputeService
 from cloudbridge.providers.interfaces import KeyPair
 
 
@@ -40,7 +43,8 @@ class OpenStackCloudProviderV1(BaseCloudProvider):
         """
         Get an openstack client object for the given cloud.
         """
-        return nova_client.Client(self.api_version, self.username, self.password, self.tenant_name, self.auth_url)
+        return nova_client.Client(
+            self.api_version, self.username, self.password, self.tenant_name, self.auth_url)
 
     def _connect_keystone(self):
         """
@@ -70,7 +74,7 @@ class OpenStackSecurityService(SecurityService):
         :return:  list of KeyPair objects
         """
         key_pairs = self.provider.nova.keypairs.list()
-        return [KeyPair(kp.id) for kp in key_pairs]
+        return [BaseKeyPair(kp.id) for kp in key_pairs]
 
     def list_security_groups(self):
         """

+ 4 - 2
test/__init__.py

@@ -23,12 +23,14 @@ unittest and nose2's automatic discovery.
 """
 
 from test.helpers import ProviderTestCaseGenerator
-from test.test_provider_security_service import ProviderSecurityServiceTestCase
 from test.test_provider_interface import ProviderInterfaceTestCase
+from test.test_provider_security_service import ProviderSecurityServiceTestCase
+from test.test_compute_service import ProviderComputeServiceTestCase
 
 PROVIDER_TESTS = [
+    ProviderInterfaceTestCase,
     ProviderSecurityServiceTestCase,
-    ProviderInterfaceTestCase
+    ProviderComputeServiceTestCase
 ]
 
 

+ 21 - 0
test/test_compute_service.py

@@ -0,0 +1,21 @@
+from cloudbridge.providers import interfaces
+from test.helpers import ProviderTestBase
+
+
+class ProviderComputeServiceTestCase(ProviderTestBase):
+
+    def __init__(self, methodName, provider):
+        super(ProviderComputeServiceTestCase, self).__init__(
+            methodName=methodName, provider=provider)
+
+    def test_create_instance(self):
+        # Need a less dangerous test
+        #         instance = self.provider.compute.create_instance(
+        #             "HelloBridgeCloud",
+        #             "ami-d85e75b0",
+        #             "t1.micro")
+        #
+        #         self.assertIsInstance(instance, interfaces.Instance)
+        #         self.assertEqual(instance.name, "HelloBridgeCloud")
+        #         instance.terminate()
+        pass