Browse Source

Made azure load initial instance properties lazily

Nuwan Goonasekera 8 năm trước cách đây
mục cha
commit
d4b2413446

+ 41 - 9
cloudbridge/cloud/providers/azure/azure_client.py

@@ -16,10 +16,22 @@ from . import helpers as azure_helpers
 
 log = logging.getLogger(__name__)
 
+NETWORK_INTERFACE_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
+                                'resourceGroups/{resourceGroupName}' \
+                                '/providers/Microsoft.Network/' \
+                                'networkInterfaces/{networkInterfaceName}'
 PUBLIC_IP_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups' \
                         '/{resourceGroupName}/providers/Microsoft.Network' \
                         '/publicIPAddresses/{publicIpAddressName}'
+VM_FIREWALL_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
+                             'resourceGroups/{resourceGroupName}/' \
+                             'providers/Microsoft.Network/' \
+                             'networkSecurityGroups/' \
+                             '{networkSecurityGroupName}'
+
+NETWORK_INTERFACE_NAME = 'networkInterfaceName'
 PUBLIC_IP_NAME = 'publicIpAddressName'
+VM_FIREWALL_NAME = 'networkSecurityGroupName'
 
 
 class AzureClient(object):
@@ -170,9 +182,12 @@ class AzureClient(object):
         return self.network_management_client.security_rules. \
             delete(self.resource_group, vm_firewall, name).result()
 
-    def get_vm_firewall(self, name):
+    def get_vm_firewall(self, fw_id):
+        url_params = azure_helpers.parse_url(VM_FIREWALL_RESOURCE_ID,
+                                             fw_id)
+        fw_name = url_params.get(VM_FIREWALL_NAME)
         return self.network_management_client.network_security_groups. \
-            get(self.resource_group, name)
+            get(self.resource_group, fw_name)
 
     def delete_vm_firewall(self, name):
         delete_async = self.network_management_client \
@@ -282,9 +297,12 @@ class AzureClient(object):
                              public_ip_name,
                              public_ip_parameters).result()
 
-    def get_floating_ip(self, name):
+    def get_floating_ip(self, public_ip_id):
+        url_params = azure_helpers.parse_url(PUBLIC_IP_RESOURCE_ID,
+                                             public_ip_id)
+        public_ip_name = url_params.get(PUBLIC_IP_NAME)
         return self.network_management_client. \
-            public_ip_addresses.get(self.resource_group, name)
+            public_ip_addresses.get(self.resource_group, public_ip_name)
 
     def delete_floating_ip(self, public_ip_id):
         url_params = azure_helpers.parse_url(PUBLIC_IP_RESOURCE_ID,
@@ -449,16 +467,25 @@ class AzureClient(object):
             create_or_update(self.resource_group,
                              vm_name, tags).result()
 
-    def delete_nic(self, nic_name):
+    def delete_nic(self, nic_id):
+        nic_params = azure_helpers.\
+            parse_url(NETWORK_INTERFACE_RESOURCE_ID, nic_id)
+        nic_name = nic_params.get(NETWORK_INTERFACE_NAME)
         self.network_management_client. \
             network_interfaces.delete(self.resource_group,
                                       nic_name).wait()
 
-    def get_nic(self, name):
+    def get_nic(self, nic_id):
+        nic_params = azure_helpers.\
+            parse_url(NETWORK_INTERFACE_RESOURCE_ID, nic_id)
+        nic_name = nic_params.get(NETWORK_INTERFACE_NAME)
         return self.network_management_client. \
-            network_interfaces.get(self.resource_group, name)
+            network_interfaces.get(self.resource_group, nic_name)
 
-    def update_nic(self, nic_name, params):
+    def update_nic(self, nic_id, params):
+        nic_params = azure_helpers.\
+            parse_url(NETWORK_INTERFACE_RESOURCE_ID, nic_id)
+        nic_name = nic_params.get(NETWORK_INTERFACE_NAME)
         async_nic_creation = self.network_management_client. \
             network_interfaces.create_or_update(
                 self.resource_group,
@@ -469,7 +496,12 @@ class AzureClient(object):
         return nic_info
 
     def create_nic(self, nic_name, params):
-        return self.update_nic(nic_name, params)
+        return self.network_management_client. \
+            network_interfaces.create_or_update(
+                self.resource_group,
+                nic_name,
+                params
+            ).result()
 
     def create_public_key(self, entity):
         return self.table_service. \

+ 52 - 81
cloudbridge/cloud/providers/azure/resources.py

@@ -3,7 +3,6 @@ DataTypes used by this provider
 """
 import collections
 import logging
-import time
 import uuid
 
 from azure.common import AzureException
@@ -30,10 +29,6 @@ from . import helpers as azure_helpers
 
 log = logging.getLogger(__name__)
 
-NETWORK_INTERFACE_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
-                                'resourceGroups/{resourceGroupName}' \
-                                '/providers/Microsoft.Network/' \
-                                'networkInterfaces/{networkInterfaceName}'
 SUBNET_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
                      '{resourceGroupName}/providers/Microsoft.Network' \
                      '/virtualNetworks/{virtualNetworkName}/subnets' \
@@ -41,11 +36,6 @@ SUBNET_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
 VOLUME_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
                      '{resourceGroupName}/providers/Microsoft.Compute/' \
                      'disks/{diskName}'
-VM_FIREWALL_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
-                             'resourceGroups/{resourceGroupName}/' \
-                             'providers/Microsoft.Network/' \
-                             'networkSecurityGroups/' \
-                             '{networkSecurityGroupName}'
 SNAPSHOT_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
                        '{resourceGroupName}/providers/Microsoft.Compute/' \
                        'snapshots/{snapshotName}'
@@ -57,11 +47,9 @@ INSTANCE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
                        'virtualMachines/{vmName}'
 
 NETWORK_NAME = 'virtualNetworkName'
-NETWORK_INTERFACE_NAME = 'networkInterfaceName'
 IMAGE_NAME = 'imageName'
 VM_NAME = 'vmName'
 VOLUME_NAME = 'diskName'
-VM_FIREWALL_NAME = 'networkSecurityGroupName'
 SNAPSHOT_NAME = 'snapshotName'
 
 
@@ -83,7 +71,7 @@ class AzureVMFirewall(BaseVMFirewall):
 
     @property
     def id(self):
-        return self._vm_firewall.name
+        return self._vm_firewall.id
 
     @property
     def name(self):
@@ -1237,42 +1225,24 @@ class AzureInstance(BaseInstance):
         super(AzureInstance, self).__init__(provider)
         self._vm = vm_instance
         self._update_state()
-        self._get_network_attributes()
         if not self._vm.tags:
             self._vm.tags = {}
 
-    def _get_network_attributes(self):
-        """
-        This method used identify the public , private ip addresses
-        and security groups associated with network interfaces.
-        :return:
-        """
-        self._private_ips = []
-        self._public_ips = []
-        self._vm_firewall_ids = []
-        self._public_ip_ids = []
-        self._nic_ids = []
-        for nic in self._vm.network_profile.network_interfaces:
-            nic_params = azure_helpers.\
-                parse_url(NETWORK_INTERFACE_RESOURCE_ID, nic.id)
-            nic_name = nic_params.get(NETWORK_INTERFACE_NAME)
-            self._nic_ids.append(nic_name)
-            nic = self._provider.azure_client.get_nic(nic_name)
-            if nic.network_security_group:
-                fw_params = azure_helpers. \
-                    parse_url(VM_FIREWALL_RESOURCE_ID,
-                              nic.network_security_group.id)
-                self._vm_firewall_ids.\
-                    append(fw_params.get(VM_FIREWALL_NAME))
-            if nic.ip_configurations:
-                for ip_config in nic.ip_configurations:
-                    self._private_ips.append(ip_config.private_ip_address)
-                    if ip_config.public_ip_address:
-                        public_ip = self._provider.azure_client.\
-                            get_floating_ip(ip_config.public_ip_address.id)
-                        self._public_ip_ids.append(
-                            ip_config.public_ip_address.id)
-                        self._public_ips.append(public_ip.ip_address)
+    @property
+    def _nic_ids(self):
+        return (nic.id for nic in self._vm.network_profile.network_interfaces)
+
+    @property
+    def _nics(self):
+        return (self._provider.azure_client.get_nic(nic_id)
+                for nic_id in self._nic_ids)
+
+    @property
+    def _public_ip_ids(self):
+        return (ip_config.public_ip_address
+                for nic in self._nics
+                for ip_config in nic.ip_configurations
+                if nic.ip_configurations and ip_config.public_ip_address)
 
     @property
     def id(self):
@@ -1310,14 +1280,18 @@ class AzureInstance(BaseInstance):
         """
         Get all the public IP addresses for this instance.
         """
-        return self._public_ips
+        return [self._provider.azure_client.get_floating_ip(pip).ip_address
+                for pip in self._public_ip_ids]
 
     @property
     def private_ips(self):
         """
         Get all the private IP addresses for this instance.
         """
-        return self._private_ips
+        return [ip_config.private_ip_address
+                for nic in self._nics
+                for ip_config in nic.ip_configurations
+                if nic.ip_configurations and ip_config.private_ip_address]
 
     @property
     def vm_type_id(self):
@@ -1350,10 +1324,10 @@ class AzureInstance(BaseInstance):
         """
         self._provider.azure_client.deallocate_vm(self.id)
         self._provider.azure_client.delete_vm(self.id)
-        for nic_id in self._nic_ids:
-            self._provider.azure_client.delete_nic(nic_id)
         for public_ip_id in self._public_ip_ids:
             self._provider.azure_client.delete_floating_ip(public_ip_id)
+        for nic_id in self._nic_ids:
+            self._provider.azure_client.delete_nic(nic_id)
         for data_disk in self._vm.storage_profile.data_disks:
             if data_disk.managed_disk:
                 disk_params = azure_helpers.\
@@ -1396,11 +1370,13 @@ class AzureInstance(BaseInstance):
     @property
     def vm_firewalls(self):
         return [self._provider.security.vm_firewalls.get(group_id)
-                for group_id in self._vm_firewall_ids]
+                for group_id in self.vm_firewall_ids]
 
     @property
     def vm_firewall_ids(self):
-        return self._vm_firewall_ids
+        return [nic.network_security_group.id
+                for nic in self._nics
+                if nic.network_security_group]
 
     @property
     def key_pair_name(self):
@@ -1411,16 +1387,15 @@ class AzureInstance(BaseInstance):
 
     def create_image(self, name, private_key_path=None):
         """
-        Create a new image based on this instance.
-        Documentation for create image available at
-        https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image  # noqa
-        In azure, need to deprovision the VM before capturing.
-        To deprovision, login to VM and execute waagent deprovision command.
-        To do this programmatically, using pysftp to ssh into the VM
-        and executing deprovision command.
-        To SSH into the VM programmatically, need pass private key file path,
-        so we have modified the Cloud Bridge interface to pass
-        the private key file path
+        Create a new image based on this instance. Documentation for create
+        image available at https://docs.microsoft.com/en-us/azure/virtual-ma
+        chines/linux/capture-image. In azure, we need to deprovision the VM
+        before capturing.
+        To deprovision, login to the VM and execute the `waagent deprovision`
+        command. To do this programmatically, use pysftp to ssh into the VM
+        and executing deprovision command. To SSH into the VM programmatically
+        however, we need to pass private key file path, so we have modified the
+        CloudBridge interface to pass the private key file path
         """
 
         self.assert_valid_resource_name(name)
@@ -1428,8 +1403,6 @@ class AzureInstance(BaseInstance):
         if not self._state == 'VM generalized':
             if not self._state == 'VM running':
                 self._provider.azure_client.start_vm(self.id)
-                time.sleep(10)  # Some time is required
-                self._get_network_attributes()
 
             # if private_key_path:
             self._deprovision(private_key_path)
@@ -1466,22 +1439,25 @@ class AzureInstance(BaseInstance):
         """
         Attaches public ip to the instance.
         """
-        nic = self._provider.azure_client.get_nic(self._nic_ids[0])
+        floating_ip_id = floating_ip.id if isinstance(
+            floating_ip, AzureFloatingIP) else floating_ip
+        nic = next(self._nics)
         nic.ip_configurations[0].public_ip_address = {
-            'id': floating_ip
+            'id': floating_ip_id
         }
-        self._provider.azure_client.update_nic(self._nic_ids[0], nic)
+        self._provider.azure_client.update_nic(nic.id, nic)
 
     def remove_floating_ip(self, floating_ip):
         """
         Remove a public IP address from this instance.
         """
-        nic = self._provider.azure_client.get_nic(self._nic_ids[0])
+        floating_ip_id = floating_ip.id if isinstance(
+            floating_ip, AzureFloatingIP) else floating_ip
+        nic = next(self._nics)
         for ip_config in nic.ip_configurations:
-            if ip_config.public_ip_address.id == floating_ip:
+            if ip_config.public_ip_address.id == floating_ip_id:
                 nic.ip_configurations[0].public_ip_address = None
-                self._provider.azure_client.update_nic(self._nic_ids[0],
-                                                       nic)
+                self._provider.azure_client.update_nic(nic.id, nic)
 
     def add_vm_firewall(self, fw):
         '''
@@ -1497,17 +1473,13 @@ class AzureInstance(BaseInstance):
         '''
         fw = (self._provider.security.vm_firewalls.get(fw)
               if isinstance(fw, str) else fw)
-        nic = self._provider.azure_client.get_nic(self._nic_ids[0])
+        nic = next(self._nics)
         if not nic.network_security_group:
             nic.network_security_group = NetworkSecurityGroup()
             nic.network_security_group.id = fw.resource_id
         else:
-            fw_url_params = azure_helpers.\
-                parse_url(VM_FIREWALL_RESOURCE_ID,
-                          nic.network_security_group.id)
             existing_fw = self._provider.security.\
-                vm_firewalls.get(fw_url_params.get(VM_FIREWALL_NAME))
-
+                vm_firewalls.get(nic.network_security_group.id)
             new_fw = self._provider.security.vm_firewalls.\
                 create('{0}-{1}'.format(fw.name, existing_fw.name),
                        'Merged security groups {0} and {1}'.
@@ -1516,7 +1488,7 @@ class AzureInstance(BaseInstance):
             new_fw.add_rule(src_dest_fw=existing_fw)
             nic.network_security_group.id = new_fw.resource_id
 
-        self._provider.azure_client.create_nic(self._nic_ids[0], nic)
+        self._provider.azure_client.update_nic(nic.id, nic)
 
     def remove_vm_firewall(self, fw):
 
@@ -1532,13 +1504,13 @@ class AzureInstance(BaseInstance):
         else we are ignoring.
         '''
 
-        nic = self._provider.azure_client.get_nic(self._nic_ids[0])
+        nic = next(self._nics)
         fw = (self._provider.security.vm_firewalls.get(fw)
               if isinstance(fw, str) else fw)
         if nic.network_security_group and \
                 nic.network_security_group.id == fw.resource_id:
             nic.network_security_group = None
-            self._provider.azure_client.create_nic(self._nic_ids[0], nic)
+            self._provider.azure_client.update_nic(nic.id, nic)
 
     def _update_state(self):
         """
@@ -1573,7 +1545,6 @@ class AzureInstance(BaseInstance):
             if not self._vm.tags:
                 self._vm.tags = {}
             self._update_state()
-            self._get_network_attributes()
         except (CloudError, ValueError) as cloudError:
             log.exception(cloudError.message)
             # The volume no longer exists and cannot be refreshed.

+ 2 - 2
cloudbridge/cloud/providers/azure/services.py

@@ -472,11 +472,11 @@ class AzureInstanceService(BaseInstanceService):
             default_kp = self.provider.security.key_pairs.find(
                 name=default_kp_name)
             if default_kp:
-                return default_kp[0]
+                key_pair = default_kp[0]
             else:
                 key_pair = self.provider.security.key_pairs.create(
                     name=default_kp_name)
-            temp_key_pair = key_pair
+                temp_key_pair = key_pair
 
         image = (self.provider.compute.images.get(image)
                  if isinstance(image, str) else image)