Преглед изворни кода

Implementation for interacting with key pairs (OpenStack and AWS)

Enis Afgan пре 10 година
родитељ
комит
7d3d8a8647

+ 18 - 1
cloudbridge/providers/aws/resources.py

@@ -244,7 +244,7 @@ class AWSInstance(BaseInstance):
         """
         Get the name of the key pair associated with this instance.
         """
-        return BaseKeyPair(self._ec2_instance.key_name)
+        return self._ec2_instance.key_name
 
     def create_image(self, name):
         """
@@ -418,6 +418,23 @@ class AWSSnapshot(BaseSnapshot):
                                                     self.name)
 
 
+class AWSKeyPair(BaseKeyPair):
+
+    def __init__(self, provider, key_pair):
+        super(AWSKeyPair, self).__init__(provider, key_pair)
+
+    @property
+    def material(self):
+        """
+        Unencrypted private key.
+
+        :rtype: str
+        :return: Unencrypted private key or ``None`` if not available.
+
+        """
+        return self._key_pair.material
+
+
 class AWSContainerObject(ContainerObject):
 
     def __init__(self, provider, key):

+ 71 - 12
cloudbridge/providers/aws/services.py

@@ -1,24 +1,25 @@
 """
-Services implemented by this provider
+Services implemented by the AWS provider.
 """
 
-from cloudbridge.providers.base import BaseKeyPair
-from cloudbridge.providers.base import BaseSecurityGroup
 from cloudbridge.providers.interfaces import BlockStoreService
 from cloudbridge.providers.interfaces import ComputeService
 from cloudbridge.providers.interfaces import ImageService
 from cloudbridge.providers.interfaces import InstanceType
 from cloudbridge.providers.interfaces import KeyPair
+from cloudbridge.providers.interfaces import KeyPairService
 from cloudbridge.providers.interfaces import MachineImage
 from cloudbridge.providers.interfaces import ObjectStoreService
 from cloudbridge.providers.interfaces import PlacementZone
 from cloudbridge.providers.interfaces import SecurityGroup
+from cloudbridge.providers.interfaces import SecurityGroupService
 from cloudbridge.providers.interfaces import SecurityService
 from cloudbridge.providers.interfaces import SnapshotService
 from cloudbridge.providers.interfaces import VolumeService
 
 from .resources import AWSContainer
 from .resources import AWSInstance
+from .resources import AWSKeyPair
 from .resources import AWSMachineImage
 from .resources import AWSSnapshot
 from .resources import AWSVolume
@@ -29,7 +30,37 @@ class AWSSecurityService(SecurityService):
     def __init__(self, provider):
         self.provider = provider
 
-    def list_key_pairs(self):
+        # Initialize provider services
+        self._key_pairs = AWSKeyPairService(provider)
+        self._security_groups = AWSSecurityGroupService(provider)
+
+    @property
+    def key_pairs(self):
+        """
+        Provides access to key pairs for this provider.
+
+        :rtype: ``object`` of :class:`.KeyPairService`
+        :return: a KeyPairService object
+        """
+        return self._key_pairs
+
+    @property
+    def security_groups(self):
+        """
+        Provides access to security groups for this provider.
+
+        :rtype: ``object`` of :class:`.SecurityGroupService`
+        :return: a SecurityGroupService object
+        """
+        return self._security_groups
+
+
+class AWSKeyPairService(KeyPairService):
+
+    def __init__(self, provider):
+        self.provider = provider
+
+    def list(self):
         """
         List all key pairs associated with this account.
 
@@ -37,18 +68,46 @@ class AWSSecurityService(SecurityService):
         :return:  list of KeyPair objects
         """
         key_pairs = self.provider.ec2_conn.get_all_key_pairs()
-        return [BaseKeyPair(kp.name) for kp in key_pairs]
+        return [AWSKeyPair(self.provider, kp) for kp in key_pairs]
+
+    def create(self, key_name):
+        """
+        Create a new key pair.
+
+        :type key_name: str
+        :param key_name: The name of the key pair to be created.
+
+        :rtype: ``object`` of :class:`.KeyPair`
+        :return:  A keypair instance or None if one was not be created.
+        """
+        kp = self.provider.ec2_conn.create_key_pair(key_name)
+        if kp:
+            return AWSKeyPair(self.provider, kp)
+        return None
 
-    def list_security_groups(self):
+    def delete(self, key_name):
         """
-        List all security groups associated with this account.
+        Delete an existing key pair.
+
+        :type key_name: str
+        :param key_name: The name of the key pair to be deleted.
 
-        :rtype: ``list`` of :class:`.SecurityGroup`
-        :return:  list of SecurityGroup objects
+        :rtype: ``bool``
+        :return:  ``True`` if the key does not exist, ``False`` otherwise. Note
+                  that this implies that the key may not have been deleted by
+                  this method but instead has not existed in the first place.
         """
-        groups = self.provider.ec2_conn.get_all_security_groups()
-        return [BaseSecurityGroup(group.id, group.name, group.description)
-                for group in groups]
+        for kp in self.provider.ec2_conn.get_all_key_pairs():
+            if kp.name == key_name:
+                kp.delete()
+                return True
+        return True
+
+
+class AWSSecurityGroupService(SecurityGroupService):
+
+    def __init__(self, provider):
+        self.provider = provider
 
 
 class AWSBlockStoreService(BlockStoreService):

+ 12 - 8
cloudbridge/providers/base.py

@@ -180,23 +180,27 @@ class BaseSnapshot(BaseObjectLifeCycleMixin, Snapshot):
 
 class BaseKeyPair(KeyPair):
 
-    def __init__(self, name, material=None):
-        self._name = name
-        self._material = material
+    def __init__(self, provider, key_pair):
+        self.provider = provider
+        self._key_pair = key_pair
 
     @property
     def name(self):
         """
         Return the name of this key pair.
         """
-        return self._name
+        return self._key_pair.name
 
-    @property
-    def material(self):
+    def delete(self):
         """
-        Unencrypted private key.
+        Delete this KeyPair.
+
+        :rtype: bool
+        :return: True if successful, otherwise False.
         """
-        return self._material
+        # This implementation assumes the `delete` method exists across multiple
+        # providers.
+        self._key_pair.delete()
 
     def __repr__(self):
         return "<CBKeyPair: {0}>".format(self.name)

+ 1 - 1
cloudbridge/providers/interfaces/resources.py

@@ -534,7 +534,7 @@ class KeyPair(object):
         Unencrypted private key.
 
         :rtype: str
-        :return: Unencrypted private key.
+        :return: Unencrypted private key or ``None`` if not available.
         """
         raise NotImplementedError(
             'material not implemented by this provider')

+ 7 - 2
cloudbridge/providers/interfaces/services.py

@@ -414,10 +414,13 @@ class KeyPairService(ProviderService):
         raise NotImplementedError(
             'list_key_pairs not implemented by this provider')
 
-    def create(self):
+    def create(self, key_name):
         """
         Create a new keypair.
 
+        :type key_name: str
+        :param key_name: The name of the key pair to be created.
+
         :rtype: ``object`` of :class:`.KeyPair`
         :return:  A keypair instance
         """
@@ -432,7 +435,9 @@ class KeyPairService(ProviderService):
         :param key_name: The name of the key pair to be deleted.
 
         :rtype: ``bool``
-        :return:  ``True`` if successful, ``False`` otherwise
+        :return:  ``True`` if the key does not exist, ``False`` otherwise. Note
+                  that this implies that the key may not have been deleted by
+                  this method but instead has not existed at all.
         """
         raise NotImplementedError(
             'delete not implemented by this provider')

+ 20 - 1
cloudbridge/providers/openstack/resources.py

@@ -259,7 +259,7 @@ class OpenStackInstance(BaseInstance):
         """
         Get the name of the key pair associated with this instance.
         """
-        return BaseKeyPair(self._os_instance.key_name)
+        return self._os_instance.key_name
 
     def create_image(self, name):
         """
@@ -458,3 +458,22 @@ class OpenStackSnapshot(BaseSnapshot):
 
     def __repr__(self):
         return "<CB-OSSnapshot: {0} ({1}>".format(self.snapshot_id, self.name)
+
+
+class OpenStackKeyPair(BaseKeyPair):
+
+    def __init__(self, provider, key_pair):
+        super(OpenStackKeyPair, self).__init__(provider, key_pair)
+
+    @property
+    def material(self):
+        """
+        Unencrypted private key.
+
+        :rtype: str
+        :return: Unencrypted private key or ``None`` if not available.
+
+        """
+        if hasattr(self._key_pair, 'private_key'):
+            return self._key_pair.private_key
+        return None

+ 37 - 3
cloudbridge/providers/openstack/services.py

@@ -1,10 +1,9 @@
 """
-Services implemented by this provider
+Services implemented by the OpenStack provider.
 """
 from cinderclient.exceptions import NotFound as CinderNotFound
 from novaclient.exceptions import NotFound as NovaNotFound
 
-from cloudbridge.providers.base import BaseKeyPair
 from cloudbridge.providers.base import BaseSecurityGroup
 from cloudbridge.providers.interfaces import BlockStoreService
 from cloudbridge.providers.interfaces import ComputeService
@@ -23,6 +22,7 @@ from cloudbridge.providers.interfaces import VolumeService
 
 from .resources import OpenStackInstance
 from .resources import OpenStackInstanceType
+from .resources import OpenStackKeyPair
 from .resources import OpenStackMachineImage
 from .resources import OpenStackRegion
 # from .resources import OpenStackSecurityGroup
@@ -73,7 +73,41 @@ class OpenStackKeyPairService(KeyPairService):
         :return:  list of KeyPair objects
         """
         key_pairs = self.provider.nova.keypairs.list()
-        return [BaseKeyPair(kp.id) for kp in key_pairs]
+        return [OpenStackKeyPair(self.provider, kp) for kp in key_pairs]
+
+    def create(self, key_name):
+        """
+        Create a new key pair.
+
+        :type key_name: str
+        :param key_name: The name of the key pair to be created.
+
+        :rtype: ``object`` of :class:`.KeyPair`
+        :return:  A keypair instance or None if one was not be created.
+        """
+        kp = self.provider.nova.keypairs.create(key_name)
+        if kp:
+            return OpenStackKeyPair(self.provider, kp)
+        return None
+
+    def delete(self, key_name):
+        """
+        Delete an existing key pair.
+
+        :type key_name: str
+        :param key_name: The name of the key pair to be deleted.
+
+        :rtype: ``bool``
+        :return:  ``True`` if the key does not exist, ``False`` otherwise. Note
+                  that this implies that the key may not have been deleted by
+                  this method but instead has not existed in the first place.
+        """
+        try:
+            kp = self.provider.nova.keypairs.find(name=key_name)
+            kp.delete()
+            return True
+        except NovaNotFound:
+            return True
 
 
 class OpenStackSecurityGroupService(SecurityGroupService):

+ 1 - 1
test/test_provider_security_service.py

@@ -9,7 +9,7 @@ class ProviderSecurityServiceTestCase(ProviderTestBase):
             methodName=methodName, provider=provider)
 
     def test_list_key_pairs(self):
-        key_pairs = self.provider.security.list_key_pairs()
+        key_pairs = self.provider.security.key_pairs.list()
         # Assume there's always one keypair at least
         self.assertIsInstance(key_pairs[0], interfaces.KeyPair)
         self.assertIsNotNone(key_pairs[0].name)