Nuwan Goonasekera 7 лет назад
Родитель
Сommit
f981ed9c12

+ 19 - 87
cloudbridge/cloud/providers/gce/helpers.py

@@ -1,6 +1,3 @@
-from collections import namedtuple
-import hashlib
-
 # based on http://stackoverflow.com/a/39126754
 # based on http://stackoverflow.com/a/39126754
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives import serialization as crypt_serialization
 from cryptography.hazmat.primitives import serialization as crypt_serialization
@@ -9,9 +6,6 @@ from cryptography.hazmat.primitives.asymmetric import rsa
 from retrying import retry
 from retrying import retry
 
 
 
 
-GCEKeyInfo = namedtuple('GCEKeyInfo', 'format public_key email')
-
-
 def gce_projects(provider):
 def gce_projects(provider):
     return provider.gce_compute.projects()
     return provider.gce_compute.projects()
 
 
@@ -42,17 +36,7 @@ def iter_all(resource, **kwargs):
         token = response['nextPageToken']
         token = response['nextPageToken']
 
 
 
 
-def _iter_gce_key_pairs(provider):
-    """
-    Iterates through the project's metadata, yielding a GCEKeyInfo object
-    for each entry in commonInstanceMetaData/items
-    """
-    metadata = _get_common_metadata(provider)
-    for kpinfo in _iter_gce_ssh_keys(metadata):
-        yield kpinfo
-
-
-def _get_common_metadata(provider):
+def get_common_metadata(provider):
     """
     """
     Get a project's commonInstanceMetadata entry
     Get a project's commonInstanceMetadata entry
     """
     """
@@ -61,40 +45,6 @@ def _get_common_metadata(provider):
     return metadata["commonInstanceMetadata"]
     return metadata["commonInstanceMetadata"]
 
 
 
 
-def _get_or_add_sshkey_entry(metadata):
-    """
-    Get the ssh-keys entry from commonInstanceMetadata/items.
-    If an entry does not exist, adds a new empty entry
-    """
-    sshkey_entry = None
-    entries = [item for item in metadata.get('items', [])
-               if item['key'] == 'ssh-keys']
-    if entries:
-        sshkey_entry = entries[0]
-    else:  # add a new entry
-        sshkey_entry = {'key': 'ssh-keys', 'value': ''}
-        if 'items' not in metadata:
-            metadata['items'] = [sshkey_entry]
-        else:
-            metadata['items'].append(sshkey_entry)
-    return sshkey_entry
-
-
-def _iter_gce_ssh_keys(metadata):
-    """
-    Iterates through the ssh keys given a commonInstanceMetadata dict,
-    yielding a GCEKeyInfo object for each entry in
-    commonInstanceMetaData/items
-    """
-    sshkeys = _get_or_add_sshkey_entry(metadata)["value"]
-    for key in sshkeys.split("\n"):
-        # elems should be "ssh-rsa <public_key> <email>"
-        elems = key.split(" ")
-        if elems and elems[0]:  # ignore blank lines
-            yield GCEKeyInfo(
-                    elems[0], elems[1].encode('ascii'), elems[2])
-
-
 def gce_metadata_save_op(provider, callback):
 def gce_metadata_save_op(provider, callback):
     """
     """
     Carries out a metadata save operation. In GCE, a fingerprint based
     Carries out a metadata save operation. In GCE, a fingerprint based
@@ -105,18 +55,11 @@ def gce_metadata_save_op(provider, callback):
     immediately afterwards, ensuring that update conflicts can be detected.
     immediately afterwards, ensuring that update conflicts can be detected.
     """
     """
     def _save_common_metadata(provider):
     def _save_common_metadata(provider):
-        metadata = _get_common_metadata(provider)
-        # add a new entry if one doesn't exist
-        sshkey_entry = _get_or_add_sshkey_entry(metadata)
-        gce_kp_list = callback(_iter_gce_ssh_keys(metadata))
-
-        entry = ""
-        for gce_kp in gce_kp_list:
-            entry = entry + u"{0} {1} {2}\n".format(gce_kp.format,
-                                                    gce_kp.public_key,
-                                                    gce_kp.email)
-        sshkey_entry["value"] = entry.rstrip()
-        # common_metadata will have the current fingerprint at this point
+        # get the latest metadata (so we get the latest fingerprint)
+        metadata = get_common_metadata(provider)
+        # allow callback to do processing on it
+        callback(metadata)
+        # save the metadata
         operation = gce_projects(provider).setCommonInstanceMetadata(
         operation = gce_projects(provider).setCommonInstanceMetadata(
             project=provider.project_name, body=metadata).execute()
             project=provider.project_name, body=metadata).execute()
         provider.wait_for_operation(operation)
         provider.wait_for_operation(operation)
@@ -126,35 +69,24 @@ def gce_metadata_save_op(provider, callback):
     retry_decorator(_save_common_metadata)(provider)
     retry_decorator(_save_common_metadata)(provider)
 
 
 
 
-def gce_kp_to_id(gce_kp):
-    """
-    Accept a GCEKeyInfo object and return a unique
-    ID for it
-    """
-    md5 = hashlib.md5()
-    md5.update(gce_kp.public_key)
-    return md5.hexdigest()
-
-
 def modify_or_add_metadata_item(provider, key, value):
 def modify_or_add_metadata_item(provider, key, value):
-    metadata = _get_common_metadata(provider)
-    entries = [item for item in metadata.get('items', [])
-               if item['key'] == key]
-    if entries:
-        entries[-1]['value'] = value
-    else:
-        entry = {'key': key, 'value': value}
-        if 'items' not in metadata:
-            metadata['items'] = [entry]
+    def _update_metadata_key(metadata):
+        entries = [item for item in metadata.get('items', [])
+                   if item['key'] == key]
+        if entries:
+            entries[-1]['value'] = value
         else:
         else:
-            metadata['items'].append(entry)
-    operation = gce_projects(provider).setCommonInstanceMetadata(
-        project=provider.project_name, body=metadata).execute()
-    provider.wait_for_operation(operation)
+            entry = {'key': key, 'value': value}
+            if 'items' not in metadata:
+                metadata['items'] = [entry]
+            else:
+                metadata['items'].append(entry)
+
+    gce_metadata_save_op(provider, _update_metadata_key)
 
 
 
 
 def get_metadata_item_value(provider, key):
 def get_metadata_item_value(provider, key):
-    metadata = _get_common_metadata(provider)
+    metadata = get_common_metadata(provider)
     entries = [item['value'] for item in metadata.get('items', [])
     entries = [item['value'] for item in metadata.get('items', [])
                if item['key'] == key]
                if item['key'] == key]
     if entries:
     if entries:

+ 1 - 1
cloudbridge/cloud/providers/gce/resources.py

@@ -1351,7 +1351,7 @@ class GCENetwork(BaseNetwork):
     def label(self):
     def label(self):
         tag_name = "_".join(["network", self.name, "label"])
         tag_name = "_".join(["network", self.name, "label"])
         return helpers.get_metadata_item_value(self._provider, tag_name)
         return helpers.get_metadata_item_value(self._provider, tag_name)
-        ## TODO: Add removing metadata to delete function
+        # TODO: Add removing metadata to delete function
 
 
     @label.setter
     @label.setter
     def label(self, value):
     def label(self, value):

+ 76 - 8
cloudbridge/cloud/providers/gce/services.py

@@ -1,10 +1,11 @@
+import hashlib
 import logging
 import logging
 import time
 import time
 import uuid
 import uuid
+from collections import namedtuple
 
 
 import googleapiclient
 import googleapiclient
 
 
-
 import cloudbridge as cb
 import cloudbridge as cb
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ServerPagedResultList
 from cloudbridge.cloud.base.resources import ServerPagedResultList
@@ -68,9 +69,76 @@ class GCESecurityService(BaseSecurityService):
 
 
 class GCEKeyPairService(BaseKeyPairService):
 class GCEKeyPairService(BaseKeyPairService):
 
 
+    GCEKeyInfo = namedtuple('GCEKeyInfo', 'format public_key email')
+
     def __init__(self, provider):
     def __init__(self, provider):
         super(GCEKeyPairService, self).__init__(provider)
         super(GCEKeyPairService, self).__init__(provider)
 
 
+    def _iter_gce_key_pairs(self, provider):
+        """
+        Iterates through the project's metadata, yielding a GCEKeyInfo object
+        for each entry in commonInstanceMetaData/items
+        """
+        metadata = helpers.get_common_metadata(provider)
+        for kpinfo in self._iter_gce_ssh_keys(metadata):
+            yield kpinfo
+
+    def _get_or_add_sshkey_entry(self, metadata):
+        """
+        Get the ssh-keys entry from commonInstanceMetadata/items.
+        If an entry does not exist, adds a new empty entry
+        """
+        sshkey_entry = None
+        entries = [item for item in metadata.get('items', [])
+                   if item['key'] == 'ssh-keys']
+        if entries:
+            sshkey_entry = entries[0]
+        else:  # add a new entry
+            sshkey_entry = {'key': 'ssh-keys', 'value': ''}
+            if 'items' not in metadata:
+                metadata['items'] = [sshkey_entry]
+            else:
+                metadata['items'].append(sshkey_entry)
+        return sshkey_entry
+
+    def _iter_gce_ssh_keys(self, metadata):
+        """
+        Iterates through the ssh keys given a commonInstanceMetadata dict,
+        yielding a GCEKeyInfo object for each entry in
+        commonInstanceMetaData/items
+        """
+        sshkeys = self._get_or_add_sshkey_entry(metadata)["value"]
+        for key in sshkeys.split("\n"):
+            # elems should be "ssh-rsa <public_key> <email>"
+            elems = key.split(" ")
+            if elems and elems[0]:  # ignore blank lines
+                yield GCEKeyPairService.GCEKeyInfo(
+                        elems[0], elems[1].encode('ascii'), elems[2])
+
+    def update_kps_in_metadata(self, provider, callback):
+        def _process_kps_from_metadata(metadata):
+            # add a new entry if one doesn't exist
+            sshkey_entry = self._get_or_add_sshkey_entry(metadata)
+            gce_kp_list = callback(self._iter_gce_ssh_keys(metadata))
+
+            entry = ""
+            for gce_kp in gce_kp_list:
+                entry = entry + u"{0} {1} {2}\n".format(gce_kp.format,
+                                                        gce_kp.public_key,
+                                                        gce_kp.email)
+            sshkey_entry["value"] = entry.rstrip()
+
+        helpers.gce_metadata_save_op(provider, _process_kps_from_metadata)
+
+    def gce_kp_to_id(self, gce_kp):
+        """
+        Accept a GCEKeyInfo object and return a unique
+        ID for it
+        """
+        md5 = hashlib.md5()
+        md5.update(gce_kp.public_key)
+        return md5.hexdigest()
+
     def get(self, key_pair_id):
     def get(self, key_pair_id):
         """
         """
         Returns a KeyPair given its ID.
         Returns a KeyPair given its ID.
@@ -83,8 +151,8 @@ class GCEKeyPairService(BaseKeyPairService):
 
 
     def list(self, limit=None, marker=None):
     def list(self, limit=None, marker=None):
         key_pairs = []
         key_pairs = []
-        for gce_kp in helpers._iter_gce_key_pairs(self.provider):
-            kp_id = helpers.gce_kp_to_id(gce_kp)
+        for gce_kp in self._iter_gce_key_pairs(self.provider):
+            kp_id = self.gce_kp_to_id(gce_kp)
             key_pairs.append(GCEKeyPair(self.provider, kp_id, gce_kp.email))
             key_pairs.append(GCEKeyPair(self.provider, kp_id, gce_kp.email))
         return ClientPagedResultList(self.provider, key_pairs,
         return ClientPagedResultList(self.provider, key_pairs,
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
@@ -121,7 +189,7 @@ class GCEKeyPairService(BaseKeyPairService):
         parts = public_key_material.split(b' ')
         parts = public_key_material.split(b' ')
         if len(parts) == 2:
         if len(parts) == 2:
             public_key_material = parts[1]
             public_key_material = parts[1]
-        kp_info = helpers.GCEKeyInfo(
+        kp_info = GCEKeyPairService.GCEKeyInfo(
             '%s:ssh-rsa' % name, public_key_material, name)
             '%s:ssh-rsa' % name, public_key_material, name)
 
 
         def _add_kp(gce_kp_generator):
         def _add_kp(gce_kp_generator):
@@ -132,8 +200,8 @@ class GCEKeyPairService(BaseKeyPairService):
                 kp_list.append(gce_kp)
                 kp_list.append(gce_kp)
             return kp_list
             return kp_list
 
 
-        helpers.gce_metadata_save_op(self.provider, _add_kp)
-        return GCEKeyPair(self.provider, helpers.gce_kp_to_id(kp_info), name,
+        self.update_kps_in_metadata(self.provider, _add_kp)
+        return GCEKeyPair(self.provider, self.gce_kp_to_id(kp_info), name,
                           private_key)
                           private_key)
 
 
     def delete(self, key_pair_id):
     def delete(self, key_pair_id):
@@ -141,13 +209,13 @@ class GCEKeyPairService(BaseKeyPairService):
         def _delete_key(gce_kp_generator):
         def _delete_key(gce_kp_generator):
             kp_list = []
             kp_list = []
             for gce_kp in gce_kp_generator:
             for gce_kp in gce_kp_generator:
-                if helpers.gce_kp_to_id(gce_kp) == key_pair_id:
+                if self.gce_kp_to_id(gce_kp) == key_pair_id:
                     continue
                     continue
                 else:
                 else:
                     kp_list.append(gce_kp)
                     kp_list.append(gce_kp)
             return kp_list
             return kp_list
 
 
-        helpers.gce_metadata_save_op(self.provider, _delete_key)
+        self.update_kps_in_metadata(self.provider, _delete_key)
 
 
 
 
 class GCEVMFirewallService(BaseVMFirewallService):
 class GCEVMFirewallService(BaseVMFirewallService):