Browse Source

keypairs implementation

madhugilla 9 years ago
parent
commit
823739635f

+ 29 - 0
azure_integration_test/test_integration_azure_key_pair_service.py

@@ -0,0 +1,29 @@
+import uuid
+
+import azure_integration_test.helpers as helpers
+
+from azure_integration_test.helpers import ProviderTestBase
+
+
+class AzureIntegrationKeyPairServiceTestCase(ProviderTestBase):
+    @helpers.skipIfNoService(['security.key_pairs'])
+    def test_azure_key_pair_service(self):
+
+        key_pair_name = '{0}'.format(uuid.uuid4())
+
+        key_pair_create = self.provider.security.key_pairs.\
+            create(key_pair_name)
+        print(key_pair_create.__dict__)
+        self.assertIsNotNone(key_pair_create)
+
+        key_pair_list = self.provider.security.key_pairs.list()
+        print(str(key_pair_list))
+        self.assertTrue(len(key_pair_list) > 0)
+
+        key_pair_find = self.provider.security.key_pairs.find(key_pair_name)
+        print(key_pair_find.__dict__)
+        self.assertTrue(len(key_pair_find) > 0)
+
+        key_pair_get = self.provider.security.key_pairs.get(key_pair_name)
+        print(key_pair_get.__dict__)
+        self.assertIsNotNone(key_pair_get)

+ 2 - 2
azure_test/test_azure_instance_service.py

@@ -37,8 +37,8 @@ class AzureInstanceServiceTestCase(ProviderTestBase):
         self.assertIsNotNone(img)
 
         # TODO: Add logic to get key pair
-        key_pair = None
-        # self.assertIsNotNone(key_pair)
+        key_pair = self.provider.security.key_pairs.get('KeyPair1')
+        self.assertIsNotNone(key_pair)
 
         inst_type = [t for t in self.provider.compute.instance_types.list()
                      if t.name == 'Standard_DS1_v2'][0]

+ 50 - 0
azure_test/test_azure_key_pair_service.py

@@ -0,0 +1,50 @@
+import azure_test.helpers as helpers
+from azure_test.helpers import ProviderTestBase
+
+
+class AzureKeyPairServiceTestCase(ProviderTestBase):
+    @helpers.skipIfNoService(['security.key_pairs'])
+    def test_azure_keypair_create(self):
+        key_pair_create = self.provider.security.key_pairs.create('NewKeyPair')
+        print("Create Key Pair - " + str(key_pair_create))
+        self.assertIsNotNone(key_pair_create)
+        self.assertIsNotNone(key_pair_create)
+        self.assertIsNotNone(key_pair_create.id)
+        self.assertIsNotNone(key_pair_create.key)
+        self.assertIsNotNone(key_pair_create.material)
+
+    @helpers.skipIfNoService(['security.key_pairs'])
+    def test_azure_keypair_create_Exist(self):
+        with self.assertRaises(Exception) as context:
+            self.provider.security.key_pairs.create('KeyPair1')
+            self.assertTrue(
+                'Keypair already exists' in context.exception)
+
+    @helpers.skipIfNoService(['security.key_pairs'])
+    def test_azure_keypair_list(self):
+        key_pair_list = self.provider.security.key_pairs.list()
+        print("List Key Pairs - " + str(key_pair_list))
+        self.assertTrue(key_pair_list.total_results > 0)
+
+    @helpers.skipIfNoService(['security.key_pairs'])
+    def test_azure_keypair_get_exist_and_delete(self):
+        keypair_id = 'KeyPair1'
+        keypair_get = self.provider.security.key_pairs.get(keypair_id)
+        print("Get Key Pair - " + str(keypair_get))
+        self.assertIsNotNone(keypair_get)
+        keypair_get.delete()
+
+    @helpers.skipIfNoService(['security.key_pairs'])
+    def test_azure_keypair_get_notExist(self):
+        keypair_id = 'KeyPairNotExist'
+        keypair_get_not_exist = self.provider.security.\
+            key_pairs.get(keypair_id)
+        print("Get Key Pair Not Exist - " + str(keypair_get_not_exist))
+        self.assertIsNone(keypair_get_not_exist)
+
+    @helpers.skipIfNoService(['security.key_pairs'])
+    def test_azure_keypair_find(self):
+        keypair_name = 'KeyPair1'
+        keypair_find = self.provider.security.key_pairs.find(keypair_name)
+        print("Find Key Pair - " + str(keypair_find))
+        self.assertTrue(len(keypair_find) > 0)

+ 45 - 0
cloudbridge/cloud/providers/azure/azure_client.py

@@ -9,6 +9,7 @@ from azure.mgmt.resource import ResourceManagementClient
 from azure.mgmt.resource.subscriptions import SubscriptionClient
 from azure.mgmt.storage import StorageManagementClient
 from azure.storage.blob import BlockBlobService
+from azure.storage.table import TableService
 
 log = logging.getLogger(__name__)
 
@@ -30,6 +31,7 @@ class AzureClient(object):
         self._compute_client = None
         self._access_key_result = None
         self._block_blob_service = None
+        self._table_service = None
 
         log.debug("azure subscription : %s", self.subscription_id)
 
@@ -101,6 +103,18 @@ class AzureClient(object):
                 self.access_key_result.keys[0].value)
         return self._block_blob_service
 
+    @property
+    def table_service(self):
+        if not self._table_service:
+            self._table_service = TableService(
+                self.storage_account,
+                self.access_key_result.keys[0].value)
+        if not self._table_service. \
+                exists(table_name=self.public_key_storage_table_name):
+            self._table_service.create_table(
+                self.public_key_storage_table_name)
+        return self._table_service
+
     def get_resource_group(self, name):
         return self.resource_client.resource_groups.get(name)
 
@@ -435,3 +449,34 @@ class AzureClient(object):
         self.network_management_client. \
             public_ip_addresses.delete(self.resource_group,
                                        public_ip_name).wait()
+
+    def create_public_key(self, entity):
+
+        return self.table_service. \
+            insert_or_replace_entity(self.public_key_storage_table_name,
+                                     entity)
+
+    def get_public_key(self, name):
+        entities = self.table_service. \
+            query_entities(self.public_key_storage_table_name,
+                           "Name eq '{0}'".format(name), num_results=1)
+
+        return entities.items[0] if len(entities.items) > 0 else None
+
+    def delete_public_key(self, entity):
+        self.table_service.delete_entity(self.public_key_storage_table_name,
+                                         entity.PartitionKey, entity.RowKey)
+
+    def list_public_keys(self, partition_key):
+        items = []
+        next_marker = None
+        while True:
+            entities = self.table_service. \
+                query_entities(self.public_key_storage_table_name,
+                               "PartitionKey eq '{0}'".format(partition_key),
+                               marker=next_marker, num_results=1)
+            items.extend(entities.items)
+            next_marker = entities.next_marker
+            if not next_marker:
+                break
+        return items

+ 29 - 0
cloudbridge/cloud/providers/azure/helpers.py

@@ -1,3 +1,8 @@
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
+
+
 def filter(list_items, filters):
     filtered_list = []
     if filters:
@@ -22,3 +27,27 @@ def parse_url(template_url, original_url):
             d.update({k[1:-1]: v})
 
     return d
+
+
+def gen_key_pair():
+    # generate private/public key pair
+    key = rsa.generate_private_key(backend=default_backend(),
+                                   public_exponent=65537,
+                                   key_size=2048)
+
+    # get public key in OpenSSH format
+    public_key = key.public_key().\
+        public_bytes(serialization.Encoding.OpenSSH,
+                     serialization.PublicFormat.OpenSSH)
+
+    # get private key in PEM container format
+    pem = key.\
+        private_bytes(encoding=serialization.Encoding.PEM,
+                      format=serialization.PrivateFormat.TraditionalOpenSSL,
+                      encryption_algorithm=serialization.NoEncryption())
+
+    # decode to printable strings
+    private_key_str = pem.decode('utf-8')
+    public_key_str = public_key.decode('utf-8')
+
+    return (private_key_str, public_key_str)

+ 37 - 0
cloudbridge/cloud/providers/azure/mock_azure_client.py

@@ -19,6 +19,7 @@ from azure.mgmt.resource.subscriptions.models import Location
 from azure.mgmt.storage.models import StorageAccount
 from azure.storage.blob.models import Blob, BlobProperties, \
     Container
+from azure.storage.table.models import Entity
 
 from msrestazure.azure_exceptions import CloudError
 
@@ -896,3 +897,39 @@ class MockAzureClient:
         response = Response()
         response.status_code = 404
         raise CloudError(response=response, error='Resource Not found')
+
+    keyEntity1 = Entity()
+    keyEntity1.PartitionKey = '00000000-0000-0000-0000-000000000000'
+    keyEntity1.RowKey = str(uuid.uuid4())
+    keyEntity1.Name = 'KeyPair1'
+    keyEntity1.Key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ+G1hl'
+
+    keyEntity2 = Entity()
+    keyEntity2.PartitionKey = '00000000-0000-0000-0000-000000000000'
+    keyEntity2.RowKey = str(uuid.uuid4())
+    keyEntity2.Name = 'KeyPair2'
+    keyEntity2.Key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ+G1h2'
+
+    key_entities = [keyEntity1, keyEntity2]
+
+    def create_public_key(self, entity):
+        newKey = Entity()
+        newKey.PartitionKey = entity['PartitionKey']
+        newKey.RowKey = entity['RowKey']
+        newKey.Name = entity['Name']
+        newKey.Key = entity['Key']
+        self.key_entities.append(newKey)
+
+    def get_public_key(self, key_name):
+        for key in self.key_entities:
+            if key.Name == key_name:
+                return key
+        response = Response()
+        response.status_code = 404
+        raise AzureException()
+
+    def delete_public_key(self, entity):
+        self.key_entities.remove(entity)
+
+    def list_public_keys(self, partition_key):
+        return self.key_entities

+ 7 - 1
cloudbridge/cloud/providers/azure/provider.py

@@ -49,6 +49,10 @@ class AzureCloudProvider(BaseCloudProvider):
             'azure_vm_default_user_name', os.environ.get
             ('AZURE_VM_DEFAULT_USER_NAME', 'cbuser'))
 
+        self.public_key_storage_table_name = self._get_config_value(
+            'azure_public_key_storage_table_name', os.environ.get
+            ('AZURE_PUBLIC_KEY_STORAGE_TABLE_NAME', 'cbcerts'))
+
         self._mock_azure_client = azureclient
         self._azure_client = None
 
@@ -93,7 +97,9 @@ class AzureCloudProvider(BaseCloudProvider):
                 'azure_tenant': self.tenant,
                 'azure_region_name': self.region_name,
                 'azure_resource_group': self.resource_group,
-                'azure_storage_account': self.storage_account
+                'azure_storage_account': self.storage_account,
+                'azure_public_key_storage_table_name':
+                    self.public_key_storage_table_name
             }
 
             self._azure_client = \

+ 43 - 1
cloudbridge/cloud/providers/azure/resources.py

@@ -13,7 +13,7 @@ from azure.mgmt.network.models import NetworkSecurityGroup
 
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo, \
     BaseBucket, BaseBucketObject, BaseFloatingIP, \
-    BaseInstance, BaseInstanceType, \
+    BaseInstance, BaseInstanceType, BaseKeyPair,\
     BaseLaunchConfig, BaseMachineImage, BaseNetwork, \
     BasePlacementZone, BaseRegion, BaseSecurityGroup, \
     BaseSecurityGroupRule, BaseSnapshot, BaseSubnet, \
@@ -1530,3 +1530,45 @@ class AzureInstanceType(BaseInstanceType):
                     'max_data_disk_count':
                     self._inst_type.max_data_disk_count
                }
+
+
+class AzureKeyPair(BaseKeyPair):
+
+    def __init__(self, provider, key_pair):
+        super(AzureKeyPair, self).__init__(provider, key_pair)
+        self._material = None
+
+    @property
+    def id(self):
+        return self._key_pair.Name
+
+    @property
+    def name(self):
+        return self._key_pair.Name
+
+    @property
+    def key(self):
+        return self._key_pair.Key
+
+    @property
+    def material(self):
+        """
+        Unencrypted private key.
+
+        :rtype: str
+        :return: Unencrypted private key or ``None`` if not available.
+
+        """
+        return self._material
+
+    @material.setter
+    def material(self, value):
+        self._material = value
+
+    def delete(self):
+        try:
+            self._provider.azure_client.\
+                delete_public_key(self._key_pair)
+            return True
+        except CloudError:
+            return False

+ 61 - 5
cloudbridge/cloud/providers/azure/services.py

@@ -6,7 +6,8 @@ from azure.common import AzureException
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.services import BaseBlockStoreService, \
     BaseComputeService, BaseImageService, BaseInstanceService, \
-    BaseInstanceTypesService, BaseNetworkService, BaseObjectStoreService,\
+    BaseInstanceTypesService, BaseKeyPairService, \
+    BaseNetworkService, BaseObjectStoreService,\
     BaseRegionService, BaseSecurityGroupService, BaseSecurityService, \
     BaseSnapshotService, BaseSubnetService, BaseVolumeService
 from cloudbridge.cloud.interfaces import InvalidConfigurationException
@@ -20,7 +21,7 @@ from cloudbridge.cloud.providers.azure import helpers as azure_helpers
 from msrestazure.azure_exceptions import CloudError
 
 from .resources import AzureBucket, AzureFloatingIP, \
-    AzureInstance, AzureInstanceType, \
+    AzureInstance, AzureInstanceType, AzureKeyPair,\
     AzureLaunchConfig, AzureMachineImage, \
     AzureNetwork, AzureRegion, AzureSecurityGroup, \
     AzureSnapshot, AzureSubnet, AzureVolume
@@ -33,7 +34,7 @@ class AzureSecurityService(BaseSecurityService):
         super(AzureSecurityService, self).__init__(provider)
 
         # Initialize provider services
-        # self._key_pairs = AzureKeyPairService(provider)
+        self._key_pairs = AzureKeyPairService(provider)
         self._security_groups = AzureSecurityGroupService(provider)
 
     @property
@@ -44,8 +45,7 @@ class AzureSecurityService(BaseSecurityService):
         :rtype: ``object`` of :class:`.KeyPairService`
         :return: a KeyPairService object
         """
-        raise NotImplementedError('AzureSecurityService '
-                                  'not implemented this property')
+        return self._key_pairs
 
     @property
     def security_groups(self):
@@ -109,6 +109,62 @@ class AzureSecurityGroupService(BaseSecurityGroupService):
             return False
 
 
+class AzureKeyPairService(BaseKeyPairService):
+    PARTITION_KEY = '00000000-0000-0000-0000-000000000000'
+
+    def __init__(self, provider):
+        super(AzureKeyPairService, self).__init__(provider)
+
+    def get(self, key_pair_id):
+        try:
+            key_pair = self.provider.azure_client.\
+                get_public_key(key_pair_id)
+
+            if key_pair:
+                return AzureKeyPair(self.provider, key_pair)
+            return None
+        except AzureException as error:
+            log.exception(error)
+            return None
+
+    def list(self, limit=None, marker=None):
+        key_pairs = [AzureKeyPair(self.provider, key_pair) for key_pair in
+                     self.provider.azure_client.
+                     list_public_keys(AzureKeyPairService.PARTITION_KEY)]
+        return ClientPagedResultList(self.provider, key_pairs, limit, marker)
+
+    def find(self, name, limit=None, marker=None):
+        key_pair = self.get(name)
+        return ClientPagedResultList(self.provider,
+                                     [key_pair] if key_pair else [],
+                                     limit, marker)
+
+    def create(self, name):
+
+        key_pair = self.get(name)
+
+        if key_pair:
+            raise Exception(
+                'Keypair already exists with name {0}'.format(name))
+
+        private_key_str, public_key_str = azure_helpers.gen_key_pair()
+
+        entity = {
+                  'PartitionKey': AzureKeyPairService.PARTITION_KEY,
+                  'RowKey': str(uuid.uuid4()),
+                  'Name': name,
+                  'Key': public_key_str
+                 }
+
+        self.provider.azure_client.create_public_key(entity)
+
+        key_pair = self.get(name)
+
+        key_pair.material = private_key_str
+
+        return key_pair
+
+
 class AzureObjectStoreService(BaseObjectStoreService):
     def __init__(self, provider):
         super(AzureObjectStoreService, self).__init__(provider)

+ 0 - 5
test/test_azure_security_service.py

@@ -3,11 +3,6 @@ from azure_test.helpers import ProviderTestBase
 
 
 class AzureSecurityServiceTestCase(ProviderTestBase):
-    @helpers.skipIfNoService(['security.security_groups'])
-    def test_azure_security_key_paires(self):
-        with self.assertRaises(NotImplementedError):
-            self.key_pairs = self.provider.security.key_pairs
-
     @helpers.skipIfNoService(['security.security_groups'])
     def test_azure_security_group_create(self):
         name = "testCreateSecGroup3"