list images now lists custom images (added supporting data types and 
serializations). Creating a node now requires a NodeSize and NodeImage created 
by calls to list_sizes and list_images (respectively) rather than strings to be 
more consistent. Creating a node supports creating a vm image if the NodeImage 
passed in represents a VM image (which is indicated in the extra variable and 
is filled out when data is deserialized in the list_images call). Updated tests 
to be compatible with the changes to create_node.
Fixed some styling


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/c9a8587d
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/c9a8587d
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/c9a8587d

Branch: refs/heads/trunk
Commit: c9a8587d24082cfd9dbaee8f000d5b636d7323e2
Parents: c116bf9
Author: Michael Bennett <[email protected]>
Authored: Thu Oct 9 17:57:46 2014 -0400
Committer: Michael Bennett <[email protected]>
Committed: Wed Nov 19 13:03:15 2014 -0500

----------------------------------------------------------------------
 libcloud/compute/drivers/azure.py   | 258 ++++++++++++++++++++++---------
 libcloud/test/compute/test_azure.py |  23 +--
 2 files changed, 197 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/c9a8587d/libcloud/compute/drivers/azure.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/azure.py 
b/libcloud/compute/drivers/azure.py
index 4f9d20e..5534eb1 100644
--- a/libcloud/compute/drivers/azure.py
+++ b/libcloud/compute/drivers/azure.py
@@ -16,6 +16,7 @@
 
 """
 import httplib
+import itertools
 import re
 import time
 import collections
@@ -63,7 +64,7 @@ From 
http://msdn.microsoft.com/en-us/library/windowsazure/dn197896.aspx
 """
 AZURE_COMPUTE_INSTANCE_TYPES = {
     'A0': {
-        'id': 'A0',
+        'id': 'ExtraSmall',
         'name': 'ExtraSmall Instance',
         'ram': 768,
         'disk': 127,
@@ -73,7 +74,7 @@ AZURE_COMPUTE_INSTANCE_TYPES = {
         'cores': 'Shared'
     },
     'A1': {
-        'id': 'A1',
+        'id': 'Small',
         'name': 'Small Instance',
         'ram': 1792,
         'disk': 127,
@@ -83,7 +84,7 @@ AZURE_COMPUTE_INSTANCE_TYPES = {
         'cores': 1
     },
     'A2': {
-        'id': 'A2',
+        'id': 'Medium',
         'name': 'Medium Instance',
         'ram': 3584,
         'disk': 127,
@@ -93,7 +94,7 @@ AZURE_COMPUTE_INSTANCE_TYPES = {
         'cores': 2
     },
     'A3': {
-        'id': 'A3',
+        'id': 'Large',
         'name': 'Large Instance',
         'ram': 7168,
         'disk': 127,
@@ -103,7 +104,7 @@ AZURE_COMPUTE_INSTANCE_TYPES = {
         'cores': 4
     },
     'A4': {
-        'id': 'A4',
+        'id': 'ExtraLarge',
         'name': 'ExtraLarge Instance',
         'ram': 14336,
         'disk': 127,
@@ -161,8 +162,8 @@ _KNOWN_SERIALIZATION_XFORMS = {
     'os': 'OS',
     'persistent_vm_downtime_info': 'PersistentVMDowntimeInfo',
     'copy_id': 'CopyId',
-    }
-
+    'os_disk_configuration': 'OSDiskConfiguration'
+}
 
 
 class AzureNodeDriver(NodeDriver):
@@ -174,7 +175,7 @@ class AzureNodeDriver(NodeDriver):
     _instance_types = AZURE_COMPUTE_INSTANCE_TYPES
     _blob_url = ".blob.core.windows.net"
     features = {'create_node': ['password']}
-    service_location = 
collections.namedtuple('service_location',['is_affinity_group', 
'service_location'])
+    service_location = collections.namedtuple('service_location', 
['is_affinity_group', 'service_location'])
 
     NODE_STATE_MAP = {
         'RoleStateUnknown': NodeState.UNKNOWN,
@@ -203,8 +204,12 @@ class AzureNodeDriver(NodeDriver):
         """
         self.subscription_id = subscription_id
         self.key_file = key_file
-        super(AzureNodeDriver, self).__init__(self.subscription_id, 
self.key_file,
-                                           secure=True, **kwargs)
+        super(AzureNodeDriver, self).__init__(
+            self.subscription_id,
+            self.key_file,
+            secure=True,
+            **kwargs
+        )
 
     def list_sizes(self):
         """
@@ -228,9 +233,12 @@ class AzureNodeDriver(NodeDriver):
         """        
         data = self._perform_get(self._get_image_path(), Images)
 
+        custom_image_data = self._perform_get(self._get_vmimage_path(), 
VMImages)
+
         images = [self._to_image(i) for i in data]
+        images.extend(self._vm_to_image(j) for j in custom_image_data)
 
-        if location != None:
+        if location is not None:
             images = [image for image in images if location in 
image.extra["location"]]
 
         return images
@@ -265,13 +273,17 @@ class AzureNodeDriver(NodeDriver):
         response = self._perform_get(
             self._get_hosted_service_path(ex_cloud_service_name) +
             '?embed-detail=True',
-            None)
-        if response.status != 200 :
-            raise LibcloudError('Message: %s, Body: %s, Status code: %d' %
-                                (response.error, response.body, 
response.status)
-                                , driver=self)
+            None
+        )
+
+        if response.status != 200:
+            raise LibcloudError(
+                'Message: %s, Body: %s, Status code: %d' %
+                (response.error, response.body, response.status),
+                driver=self
+            )
 
-        data =  self._parse_response(response, HostedService)
+        data = self._parse_response(response, HostedService)
 
         try:
             return [self._to_node(n) for n in
@@ -392,10 +404,6 @@ class AzureNodeDriver(NodeDriver):
            :type        ex_admin_user_id:  ``str``
 
         """
-        name = kwargs['name']
-        size = kwargs['size']
-        image = kwargs['image']
-
         password = None
         auth = self._get_and_check_auth(kwargs["auth"])        
         password = auth.password
@@ -418,12 +426,22 @@ class AzureNodeDriver(NodeDriver):
         if "size" not in kwargs:
             raise ValueError("size is required. ")
 
+        if not isinstance(kwargs['size'], NodeSize):
+            raise ValueError('Size must be an instance of NodeSize')
+
         if "image" not in kwargs:
             raise ValueError("image is required.")
 
         if "name" not in kwargs:
             raise ValueError("name is required.")
 
+        name = kwargs['name']
+        size = kwargs['size']
+        image = kwargs['image']
+
+        if not isinstance(image, NodeImage):
+            raise ValueError("Image must be an instance of NodeImage, produced 
by list_images()")
+
         node_list = 
self.list_nodes(ex_cloud_service_name=ex_cloud_service_name)
         network_config = ConfigurationSet()
         network_config.configuration_set_type = 'NetworkConfiguration'
@@ -431,7 +449,7 @@ class AzureNodeDriver(NodeDriver):
         # We do this because we need to pass a Configuration to the 
         # method. This will be either Linux or Windows. 
         if re.search("Win|SQL|SharePoint|Visual|Dynamics|DynGP|BizTalk",
-                     image, re.I):
+                     image.name, re.I):
             machine_config = WindowsConfigurationSet(
                 computer_name=name, admin_password=password,
                 admin_user_name=ex_admin_user_id)
@@ -494,13 +512,14 @@ class AzureNodeDriver(NodeDriver):
 
         _storage_location = self._get_cloud_service_location(
             service_name=ex_cloud_service_name)
-        
+
         # OK, bit annoying here. You must create a deployment before
         # you can create an instance; however, the deployment function
         # creates the first instance, but all subsequent instances
-        # must be created using the add_role function. 
+        # must be created using the add_role function.
         #
         # So, yeah, annoying.
+
         if node_list is None:
             # This is the first node in this cloud service.
             if "ex_storage_service_name" in kwargs:
@@ -523,30 +542,50 @@ class AzureNodeDriver(NodeDriver):
             else:
                 ex_deployment_name = ex_cloud_service_name
 
-            blob_url = "http://"; + ex_storage_service_name \
-                       + ".blob.core.windows.net"
+            if image.extra['vm_image']:
+                response = self._perform_post(
+                    
self._get_deployment_path_using_name(ex_cloud_service_name),
+                    AzureXmlSerializer.virtual_machine_deployment_to_xml(
+                        ex_deployment_name,
+                        ex_deployment_slot,
+                        name,
+                        name,
+                        machine_config,
+                        None,
+                        'PersistentVMRole',
+                        None,
+                        None,
+                        None,
+                        size.id,
+                        None,
+                        image.id))
+            else:
+                blob_url = "http://"; + ex_storage_service_name \
+                           + ".blob.core.windows.net"
 
-            # Azure's pattern in the UI.
-            disk_name = "{0}-{1}-{2}.vhd".format(
-                ex_cloud_service_name,name,time.strftime("%Y-%m-%d"))
-            media_link = blob_url + "/vhds/" + disk_name
-            disk_config = OSVirtualHardDisk(image, media_link)
+                # Azure's pattern in the UI.
+                disk_name = "{0}-{1}-{2}.vhd".format(
+                    ex_cloud_service_name,name,time.strftime("%Y-%m-%d"))
+                media_link = blob_url + "/vhds/" + disk_name
 
-            response = self._perform_post(
-                self._get_deployment_path_using_name(ex_cloud_service_name),
-                AzureXmlSerializer.virtual_machine_deployment_to_xml(
-                    ex_deployment_name,
-                    ex_deployment_slot,
-                    name,
-                    name,
-                    machine_config,
-                    disk_config,
-                    'PersistentVMRole',
-                    network_config,
-                    None,
-                    None,
-                    size,
-                    None))
+                disk_config = OSVirtualHardDisk(image.id, media_link)
+
+                response = self._perform_post(
+                    
self._get_deployment_path_using_name(ex_cloud_service_name),
+                    AzureXmlSerializer.virtual_machine_deployment_to_xml(
+                        ex_deployment_name,
+                        ex_deployment_slot,
+                        name,
+                        name,
+                        machine_config,
+                        disk_config,
+                        'PersistentVMRole',
+                        network_config,
+                        None,
+                        None,
+                        size.id,
+                        None,
+                        None))
 
             if response.status != 202:
                 raise LibcloudError('Message: %s, Body: %s, Status code: %d' %
@@ -574,25 +613,41 @@ class AzureNodeDriver(NodeDriver):
                         is_affinity_group=_storage_location.is_affinity_group
                         )
 
-            blob_url = "http://"; + ex_storage_service_name + \
-                       ".blob.core.windows.net"
-            disk_name = "{0}-{1}-{2}.vhd".format(
-                ex_cloud_service_name,name,time.strftime("%Y-%m-%d"))
-            media_link = blob_url + "/vhds/" + disk_name
-            disk_config = OSVirtualHardDisk(image, media_link)
-
-            response = self._perform_post(
-                self._get_role_path(ex_cloud_service_name, 
-                    _deployment_name),
-                AzureXmlSerializer.add_role_to_xml(
-                    name, # role_name
-                    machine_config, # system_config 
-                    disk_config, # os_virtual_hard_disk
-                    'PersistentVMRole', # role_type
-                    network_config, # network_config
-                    None, # availability_set_name
-                    None, # data_virtual_hard_disks
-                    size)) # role_size)
+            if image.extra['vm_image']:
+                response = self._perform_post(
+                    self._get_role_path(ex_cloud_service_name,
+                        image.extra['deployment_name']),
+                    AzureXmlSerializer.add_role_to_xml(
+                        name, # role_name
+                        machine_config, # system_config
+                        None, # os_virtual_hard_disk
+                        'PersistentVMRole', # role_type
+                        None, # network_config
+                        None, # availability_set_name
+                        None, # data_virtual_hard_disks
+                        image.id, #vm_image
+                        size.id)) # role_size
+            else:
+                blob_url = "http://"; + ex_storage_service_name + \
+                           ".blob.core.windows.net"
+                disk_name = "{0}-{1}-{2}.vhd".format(
+                    ex_cloud_service_name,name,time.strftime("%Y-%m-%d"))
+                media_link = blob_url + "/vhds/" + disk_name
+                disk_config = OSVirtualHardDisk(image.id, media_link)
+
+                response = self._perform_post(
+                    self._get_role_path(ex_cloud_service_name,
+                        _deployment_name),
+                    AzureXmlSerializer.add_role_to_xml(
+                        name, # role_name
+                        machine_config, # system_config
+                        disk_config, # os_virtual_hard_disk
+                        'PersistentVMRole', # role_type
+                        network_config, # network_config
+                        None, # availability_set_name
+                        None, # data_virtual_hard_disks
+                        size.id, # role_size
+                        None)) #vm_image)
 
             if response.status != 202:
                 raise LibcloudError('Message: %s, Body: %s, Status code: %d' %
@@ -871,12 +926,29 @@ class AzureNodeDriver(NodeDriver):
             name=data.label,
             driver=self.connection.driver,
             extra={
-                'os' : data.os,
-                'category' : data.category,
-                'description' : data.description,
-                'location' : data.location,
-                'affinity_group' : data.affinity_group,
-                'media_link' : data.media_link
+                'os': data.os,
+                'category': data.category,
+                'description': data.description,
+                'location': data.location,
+                'affinity_group': data.affinity_group,
+                'media_link': data.media_link,
+                'vm_image': False
+            })
+
+    def _vm_to_image(self, data):
+
+        return NodeImage(
+            id=data.name,
+            name=data.label,
+            driver=self.connection.driver,
+            extra={
+                'os': data.os_disk_configuration.os,
+                'category': data.category,
+                'location': data.location,
+                'media_link': data.os_disk_configuration.media_link,
+                'affinity_group': data.affinity_group,
+                'deployment_name': data.deployment_name,
+                'vm_image': True
             })
 
     def _to_volume(self, volume, node):
@@ -1412,6 +1484,9 @@ class AzureNodeDriver(NodeDriver):
     def _get_image_path(self, image_name=None):
         return self._get_path('services/images', image_name)
 
+    def _get_vmimage_path(self, image_name=None):
+        return self._get_path('services/vmimages', image_name)
+
     def _get_hosted_service_path(self, service_name=None):
         return self._get_path('services/hostedservices', service_name)
 
@@ -1786,7 +1861,7 @@ class AzureXmlSerializer():
 
     @staticmethod
     def role_to_xml(availability_set_name, data_virtual_hard_disks,
-                    network_configuration_set, os_virtual_hard_disk, role_name,
+                    network_configuration_set, os_virtual_hard_disk, 
vm_image_name, role_name,
                     role_size, role_type, system_configuration_set):
         xml = AzureXmlSerializer.data_to_xml([('RoleName', role_name),
                                           ('RoleType', role_type)])
@@ -1839,6 +1914,9 @@ class AzureXmlSerializer():
                  ('SourceImageName', os_virtual_hard_disk.source_image_name)])
             xml += '</OSVirtualHardDisk>'
 
+        if vm_image_name is not None:
+            xml += AzureXmlSerializer.data_to_xml([('VMImageName', 
vm_image_name)])
+
         if role_size is not None:
             xml += AzureXmlSerializer.data_to_xml([('RoleSize', role_size)])
 
@@ -1848,12 +1926,13 @@ class AzureXmlSerializer():
     def add_role_to_xml(role_name, system_configuration_set,
                         os_virtual_hard_disk, role_type,
                         network_configuration_set, availability_set_name,
-                        data_virtual_hard_disks, role_size):
+                        data_virtual_hard_disks, vm_image_name, role_size):
         xml = AzureXmlSerializer.role_to_xml(
             availability_set_name,
             data_virtual_hard_disks,
             network_configuration_set,
             os_virtual_hard_disk,
+            vm_image_name,
             role_name,
             role_size,
             role_type,
@@ -1906,7 +1985,7 @@ class AzureXmlSerializer():
                                           network_configuration_set,
                                           availability_set_name,
                                           data_virtual_hard_disks, role_size,
-                                          virtual_network_name):
+                                          virtual_network_name, vm_image_name):
         xml = AzureXmlSerializer.data_to_xml([('Name', deployment_name),
                                           ('DeploymentSlot', deployment_slot),
                                           ('Label', label)])
@@ -1917,6 +1996,7 @@ class AzureXmlSerializer():
             data_virtual_hard_disks,
             network_configuration_set,
             os_virtual_hard_disk,
+            vm_image_name,
             role_name,
             role_size,
             role_type,
@@ -2243,6 +2323,13 @@ class Images(WindowsAzureData):
     def __getitem__(self, index):
         return self.images[index]
 
+
+class VMImages(Images):
+
+    def __init__(self):
+        self.images = _list_of(VMImage)
+
+
 class OSImage(WindowsAzureData):
 
     def __init__(self):
@@ -2257,6 +2344,19 @@ class OSImage(WindowsAzureData):
         self.eula = u''
         self.description = u''
 
+class VMImage(WindowsAzureData):
+
+    def __init__(self):
+        self.name = u''
+        self.label = u''
+        self.category = u''
+        self.os_disk_configuration = OSDiskConfiguration()
+        self.service_name = u''
+        self.deployment_name = u''
+        self.role_name = u''
+        self.location = u''
+        self.affinity_group = u''
+
 class HostedServices(WindowsAzureData):
 
     def __init__(self):
@@ -2504,6 +2604,16 @@ class OperatingSystem(WindowsAzureData):
         self.family = 0
         self.family_label = _Base64String()
 
+class OSDiskConfiguration(WindowsAzureData):
+
+    def __init__(self):
+        self.name = u''
+        self.host_caching = u''
+        self.os_state = u''
+        self.os = u''
+        self.media_link = u''
+        self.logical_disk_size_in_gb = 0
+
 class OperatingSystems(WindowsAzureData):
 
     def __init__(self):

http://git-wip-us.apache.org/repos/asf/libcloud/blob/c9a8587d/libcloud/test/compute/test_azure.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_azure.py 
b/libcloud/test/compute/test_azure.py
index 4f76e85..7ac9622 100644
--- a/libcloud/test/compute/test_azure.py
+++ b/libcloud/test/compute/test_azure.py
@@ -1,6 +1,6 @@
 import libcloud
 from libcloud.common.types import LibcloudError
-from libcloud.compute.base import NodeAuthPassword
+from libcloud.compute.base import NodeAuthPassword, NodeImage, NodeSize
 from libcloud.common.azure import AzureServiceManagementConnection
 
 __author__ = 'david'
@@ -196,9 +196,10 @@ class AzureNodeDriverTests(unittest.TestCase) :
         auth = NodeAuthPassword("Pa55w0rd", False)
         kwargs["auth"]= auth
 
-        kwargs["size"]= "ExtraSmall"
-        kwargs["image"] = \
-            "5112500ae3b842c8b9c604889f8753c3__OpenLogic-CentOS-65-20140415"
+        kwargs["size"] = NodeSize(id="ExtraSmall")
+        kwargs["image"] = NodeImage(
+            id="5112500ae3b842c8b9c604889f8753c3__OpenLogic-CentOS-65-20140415"
+        )
         kwargs["name"] = "dcoddkinztest03"
 
         result = self.driver.create_node(
@@ -216,9 +217,10 @@ class AzureNodeDriverTests(unittest.TestCase) :
         auth = NodeAuthPassword("Pa55w0rd", False)
         kwargs["auth"]= auth
 
-        kwargs["size"]= "ExtraSmall"
-        kwargs["image"] = \
-            "5112500ae3b842c8b9c604889f8753c3__OpenLogic-CentOS-65-20140415"
+        kwargs["size"] = NodeSize(id="ExtraSmall")
+        kwargs["image"] = NodeImage(
+            id="5112500ae3b842c8b9c604889f8753c3__OpenLogic-CentOS-65-20140415"
+        )
         kwargs["name"] = "dcoddkinztest03"
 
         node = type('Node', (object,), dict(id="dc14"))
@@ -237,9 +239,10 @@ class AzureNodeDriverTests(unittest.TestCase) :
         auth = NodeAuthPassword("Pa55w0rd", False)
         kwargs["auth"]= auth
 
-        kwargs["size"]= "ExtraSmall"
-        kwargs["image"] = \
-            "5112500ae3b842c8b9c604889f8753c3__OpenLogic-CentOS-65-20140415"
+        kwargs["size"] = NodeSize(id="ExtraSmall")
+        kwargs["image"] = NodeImage(
+            id="5112500ae3b842c8b9c604889f8753c3__OpenLogic-CentOS-65-20140415"
+        )
         kwargs["name"] = "dcoddkinztest04"
 
         with self.assertRaises(LibcloudError):

Reply via email to