You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by an...@apache.org on 2016/03/15 23:09:30 UTC

[2/3] libcloud git commit: Adding calls around get images by ids and removing passwords from customer linux images

Adding calls around get images by ids and removing passwords from customer linux images


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

Branch: refs/heads/trunk
Commit: 1e57dcc348ec910143a729d7a6da5b4ca8c2a71b
Parents: af7376c
Author: Jeffrey Dunham <je...@gmail.com>
Authored: Tue Mar 15 16:51:53 2016 -0400
Committer: anthony-shaw <an...@gmail.com>
Committed: Wed Mar 16 09:09:00 2016 +1100

----------------------------------------------------------------------
 libcloud/compute/drivers/dimensiondata.py       |  98 ++++++++++---
 ...age_2ffa36c8_1848_49eb_b4fa_9d908775f68c.xml |  17 +++
 ...age_5234e5c7_01de_4411_8b6e_baeb8d91cf5d.xml |  17 +++
 ...e5a7d0e4_image_customerImage_BAD_REQUEST.xml |   6 +
 ...age_6b4fb0c7_a57b_4f58_b59c_9958f94f971a.xml |  11 ++
 ...c_8dabe5a7d0e4_image_osImage_BAD_REQUEST.xml |   6 +
 ...age_c14b1a46_2428_44c1_9c1a_b20e6418d08c.xml |  12 ++
 libcloud/test/compute/test_dimensiondata.py     | 139 +++++++++++++++++++
 8 files changed, 287 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/1e57dcc3/libcloud/compute/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/dimensiondata.py b/libcloud/compute/drivers/dimensiondata.py
index 61da0da..55db541 100644
--- a/libcloud/compute/drivers/dimensiondata.py
+++ b/libcloud/compute/drivers/dimensiondata.py
@@ -24,6 +24,7 @@ except ImportError:
 from libcloud.compute.base import NodeDriver, Node, NodeAuthPassword
 from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
 from libcloud.common.dimensiondata import dd_object_to_id
+from libcloud.common.dimensiondata import DimensionDataAPIException
 from libcloud.common.dimensiondata import (DimensionDataConnection,
                                            DimensionDataStatus)
 from libcloud.common.dimensiondata import DimensionDataNetwork
@@ -177,12 +178,14 @@ class DimensionDataNodeDriver(NodeDriver):
         :rtype: :class:`Node`
         """
         password = None
-        if isinstance(auth, basestring):
-            auth_obj = NodeAuthPassword(password=auth)
-            password = auth
-        else:
-            auth_obj = self._get_and_check_auth(auth)
-            password = auth_obj.password
+        image_needs_auth = self._image_needs_auth(image)
+        if image_needs_auth:
+            if isinstance(auth, basestring):
+                auth_obj = NodeAuthPassword(password=auth)
+                password = auth
+            else:
+                auth_obj = self._get_and_check_auth(auth)
+                password = auth_obj.password
 
         if (ex_network_domain is None and
                 ex_network is None and
@@ -262,8 +265,9 @@ class DimensionDataNodeDriver(NodeDriver):
 
         node = self.ex_get_node_by_id(node_id)
 
-        if getattr(auth_obj, "generated", False):
-            node.extra['password'] = auth_obj.password
+        if image_needs_auth:
+            if getattr(auth_obj, "generated", False):
+                node.extra['password'] = auth_obj.password
 
         return node
 
@@ -389,7 +393,7 @@ class DimensionDataNodeDriver(NodeDriver):
         if location is not None:
             params['datacenterId'] = self._location_to_location_id(location)
 
-        return self._to_base_images(
+        return self._to_images(
             self.connection.request_with_orgId_api_2(
                 'image/osImage',
                 params=params)
@@ -1665,44 +1669,94 @@ class DimensionDataNodeDriver(NodeDriver):
         if location is not None:
             params['datacenterId'] = self._location_to_location_id(location)
 
-        return self._to_base_images(
+        return self._to_images(
             self.connection.request_with_orgId_api_2(
                 'image/customerImage',
                 params=params)
             .object, 'customerImage')
 
+    def ex_get_base_image_by_id(self, id):
+        """
+        Gets a Base image in the Dimension Data Cloud given the id
+
+        :param id: The id of the image
+        :type  id: ``str``
+
+        :rtype: :class:`NodeImage`
+        """
+        image = self.connection.request_with_orgId_api_2(
+            'image/osImage/%s' % id).object
+        return self._to_image(image)
+
+    def ex_get_customer_image_by_id(self, id):
+        """
+        Gets a Customer image in the Dimension Data Cloud given the id
+
+        :param id: The id of the image
+        :type  id: ``str``
+
+        :rtype: :class:`NodeImage`
+        """
+        image = self.connection.request_with_orgId_api_2(
+            'image/customerImage/%s' % id).object
+        return self._to_image(image)
+
+    def ex_get_image_by_id(self, id):
+        """
+        Gets a Base/Customer image in the Dimension Data Cloud given the id
+
+        Note: This first checks the base image
+              If it is not a base image we check if it is a customer image
+              If it is not in either of these a DimensionDataAPIException
+              is thrown
+
+        :param id: The id of the image
+        :type  id: ``str``
+
+        :rtype: :class:`NodeImage`
+        """
+        try:
+            return self.ex_get_base_image_by_id(id)
+        except DimensionDataAPIException as e:
+            if e.code != 'RESOURCE_NOT_FOUND':
+                raise e
+        return self.ex_get_customer_image_by_id(id)
+
     def _list_nodes_single_page(self, params={}):
         return self.connection.request_with_orgId_api_2(
             'server/server', params=params).object
 
-    def _to_base_images(self, object, el_name='osImage'):
+    def _to_images(self, object, el_name='osImage'):
         images = []
         locations = self.list_locations()
 
         for element in object.findall(fixxpath(el_name, TYPES_URN)):
-            images.append(self._to_base_image(element, locations))
+            images.append(self._to_image(element, locations))
 
         return images
 
-    def _to_base_image(self, element, locations):
-        # Eventually we will probably need multiple _to_image() functions
-        # that parse <ServerImage> differently than <DeployedImage>.
-        # DeployedImages are customer snapshot images, and ServerImages are
-        # 'base' images provided by DimensionData
+    def _to_image(self, element, locations=None):
         location_id = element.get('datacenterId')
+        if locations is None:
+            locations = self.list_locations(location_id)
         location = list(filter(lambda x: x.id == location_id,
                                locations))[0]
         cpu_spec = self._to_cpu_spec(element.find(fixxpath('cpu', TYPES_URN)))
         os_el = element.find(fixxpath('operatingSystem', TYPES_URN))
+        if element.tag.endswith('customerImage'):
+            is_customer_image = True
+        else:
+            is_customer_image = False
         extra = {
             'description': findtext(element, 'description', TYPES_URN),
-            'OS_type': os_el.get('type'),
+            'OS_type': os_el.get('family'),
             'OS_displayName': os_el.get('displayName'),
             'cpu': cpu_spec,
             'memoryGb': findtext(element, 'memoryGb', TYPES_URN),
             'osImageKey': findtext(element, 'osImageKey', TYPES_URN),
             'created': findtext(element, 'createTime', TYPES_URN),
             'location': location,
+            'isCustomerImage': is_customer_image
         }
 
         return NodeImage(id=element.get('id'),
@@ -1929,7 +1983,6 @@ class DimensionDataNodeDriver(NodeDriver):
 
         has_network_info \
             = element.find(fixxpath('networkInfo', TYPES_URN)) is not None
-
         cpu_spec = self._to_cpu_spec(element.find(fixxpath('cpu', TYPES_URN)))
         disks = self._to_disks(element)
         extra = {
@@ -2017,6 +2070,13 @@ class DimensionDataNodeDriver(NodeDriver):
                                     TYPES_URN))
         return s
 
+    def _image_needs_auth(self, image):
+        if not isinstance(image, NodeImage):
+            image = self.ex_get_image_by_id(image)
+        if image.extra['isCustomerImage'] and image.extra['OS_type'] == 'UNIX':
+            return False
+        return True
+
     @staticmethod
     def _get_node_state(state, started, action):
         try:

http://git-wip-us.apache.org/repos/asf/libcloud/blob/1e57dcc3/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_2ffa36c8_1848_49eb_b4fa_9d908775f68c.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_2ffa36c8_1848_49eb_b4fa_9d908775f68c.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_2ffa36c8_1848_49eb_b4fa_9d908775f68c.xml
new file mode 100644
index 0000000..bff6183
--- /dev/null
+++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_2ffa36c8_1848_49eb_b4fa_9d908775f68c.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+    <customerImage xmlns="urn:didata.com:api:cloud:types" id="2ffa36c8-1848-49eb-b4fa-9d908775f68c" datacenterId="NA9">
+        <name>CustomerImageWithPricedSoftwareLabels</name>
+        <description />
+        <operatingSystem id="WIN2008S32" displayName="WIN2008S/32" family="WINDOWS" />
+        <cpu count="1" speed="STANDARD" coresPerSocket="1" />
+        <memoryGb>1</memoryGb>
+        <disk id="29455efc-51af-4b4d-91b3-d81ca0dff7d8" scsiId="0" sizeGb="50" speed="STANDARD" />
+        <softwareLabel>MSSQL2008R2S</softwareLabel>
+        <createTime>2015-11-03T15:25:34.000Z</createTime>
+        <source type="CLONE">
+            <artifact type="SERVER_ID" value="7c9c2551-269d-4274-a247126ba7c6215c" />
+        </source>
+        <state>NORMAL</state>
+        <vmwareTools versionStatus="CURRENT" runningStatus="NOT_RUNNING" />
+        <virtualHardware version="vmx-08" upToDate="false" />
+    </customerImage>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/1e57dcc3/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_5234e5c7_01de_4411_8b6e_baeb8d91cf5d.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_5234e5c7_01de_4411_8b6e_baeb8d91cf5d.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_5234e5c7_01de_4411_8b6e_baeb8d91cf5d.xml
new file mode 100644
index 0000000..db5302d
--- /dev/null
+++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_5234e5c7_01de_4411_8b6e_baeb8d91cf5d.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+    <customerImage xmlns="urn:didata.com:api:cloud:types" id="5234e5c7-01de-4411-8b6e-baeb8d91cf5d" datacenterId="NA9">
+        <name>ImportedCustomerImage</name>
+        <description />
+        <operatingSystem id="REDHAT664" displayName="REDHAT6/64" family="UNIX" />
+        <cpu count="4" speed="STANDARD" coresPerSocket="1" />
+        <memoryGb>2</memoryGb>
+        <disk id="1a82316f-23ed-4fe9-b6d8-6b92ac467423" scsiId="0" sizeGb="12" speed="STANDARD" />
+        <createTime>2015-11-19T14:29:02.000Z</createTime>
+        <source type="IMPORT">
+        <artifact type="MF" value="ImportedCustomerImage.mf" date="2015-1119T14:28:54.000Z" />
+        <artifact type="OVF" value="ImportedCustomerImage.ovf" date="2015-1119T14:28:05.000Z" />
+        <artifact type="VMDK" value="ImportedCustomerImage-disk1.vmdk" date="2015-11-19T12:22:31.000Z" /></source>
+        <state>NORMAL</state>
+        <vmwareTools versionStatus="NEED_UPGRADE" runningStatus="NOT_RUNNING" apiVersion="8389" />
+        <virtualHardware version="vmx-10" upToDate="true" />
+    </customerImage>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/1e57dcc3/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_BAD_REQUEST.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_BAD_REQUEST.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_BAD_REQUEST.xml
new file mode 100644
index 0000000..2579b63
--- /dev/null
+++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_BAD_REQUEST.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<response xmlns="urn:didata.com:api:cloud:types" requestId="na/2016-03-15T12:38:27.999-04:00/e9e8370d-d4d4-4725-afae-77e7832bacbd">
+  <operation>GET_CUSTOMER_IMAGE</operation>
+  <responseCode>RESOURCE_NOT_FOUND</responseCode>
+  <message>Server Image 2ffa36c8-1848-49eb-b4fa-9d908775f68c not found.</message>
+</response>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/1e57dcc3/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_6b4fb0c7_a57b_4f58_b59c_9958f94f971a.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_6b4fb0c7_a57b_4f58_b59c_9958f94f971a.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_6b4fb0c7_a57b_4f58_b59c_9958f94f971a.xml
new file mode 100644
index 0000000..1631137
--- /dev/null
+++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_6b4fb0c7_a57b_4f58_b59c_9958f94f971a.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+  <osImage xmlns="urn:didata.com:api:cloud:types" id="6b4fb0c7-a57b-4f58-b59c-9958f94f971a" datacenterId="NA9">
+    <name>Win2012 DC 2 CPU</name>
+    <description>Windows 2012 Datacenter</description>
+    <operatingSystem id="WIN2012DC64" displayName="WIN2012DC/64" family="WINDOWS" />
+    <cpu count="2" speed="STANDARD" coresPerSocket="1" />
+    <memoryGb>4</memoryGb>
+    <disk id="f5e01854-a211-4ec6-96d6-2753b6d47877" scsiId="0" sizeGb="50" speed="STANDARD" />
+    <createTime>2015-09-17T11:44:43.000Z</createTime>
+    <osImageKey>T-WIN-2012-DATACTR-64-2-4-50</osImageKey>
+  </osImage>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/1e57dcc3/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_BAD_REQUEST.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_BAD_REQUEST.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_BAD_REQUEST.xml
new file mode 100644
index 0000000..0091648
--- /dev/null
+++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_BAD_REQUEST.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<response xmlns="urn:didata.com:api:cloud:types" requestId="na/2016-03-15T12:38:27.999-04:00/e9e8370d-d4d4-4725-afae-77e7832bacbd">
+  <operation>GET_OS_IMAGE</operation>
+  <responseCode>RESOURCE_NOT_FOUND</responseCode>
+  <message>Server Image 2ffa36c8-1848-49eb-b4fa-9d908775f68c not found.</message>
+</response>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/1e57dcc3/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_c14b1a46_2428_44c1_9c1a_b20e6418d08c.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_c14b1a46_2428_44c1_9c1a_b20e6418d08c.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_c14b1a46_2428_44c1_9c1a_b20e6418d08c.xml
new file mode 100644
index 0000000..27d6c25
--- /dev/null
+++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_c14b1a46_2428_44c1_9c1a_b20e6418d08c.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+  <osImage xmlns="urn:didata.com:api:cloud:types" id="c14b1a46-2428-44c1-9c1a-b20e6418d08c" datacenterId="NA9">
+    <name>RedHat 6 64-bit 2 CPU</name>
+    <description>RedHat 6.6 Enterprise (Santiago) 64-bit</description>
+    <operatingSystem id="REDHAT664" displayName="REDHAT6/64" family="UNIX" />
+    <cpu count="2" speed="STANDARD" coresPerSocket="1" />
+    <memoryGb>4</memoryGb>
+    <disk id="a02b7244-99d8-4889-84a5-5e4373c1bb26" scsiId="0" sizeGb="10" speed="STANDARD" />
+    <createTime>2015-09-17T11:23:48.000Z</createTime>
+    <osImageKey>T-RHEL-6-64-2-4-10</osImageKey>
+  </osImage>
+

http://git-wip-us.apache.org/repos/asf/libcloud/blob/1e57dcc3/libcloud/test/compute/test_dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_dimensiondata.py b/libcloud/test/compute/test_dimensiondata.py
index 24d9378..8ec9d27 100644
--- a/libcloud/test/compute/test_dimensiondata.py
+++ b/libcloud/test/compute/test_dimensiondata.py
@@ -177,6 +177,56 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin):
         self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
         self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER')
 
+    def test_create_node_response_no_pass_random_gen(self):
+        image = self.driver.list_images()[0]
+        network = self.driver.ex_list_networks()[0]
+        node = self.driver.create_node(name='test2', image=image, auth=None,
+                                       ex_description='test2 node', ex_network=network,
+                                       ex_is_started=False)
+        self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+        self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER')
+        self.assertTrue('password' in node.extra)
+
+    def test_create_node_response_no_pass_customer_windows(self):
+        image = self.driver.ex_list_customer_images()[1]
+        network = self.driver.ex_list_networks()[0]
+        node = self.driver.create_node(name='test2', image=image, auth=None,
+                                       ex_description='test2 node', ex_network=network,
+                                       ex_is_started=False)
+        self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+        self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER')
+        self.assertTrue('password' in node.extra)
+
+    def test_create_node_response_no_pass_customer_windows_STR(self):
+        image = self.driver.ex_list_customer_images()[1].id
+        network = self.driver.ex_list_networks()[0]
+        node = self.driver.create_node(name='test2', image=image, auth=None,
+                                       ex_description='test2 node', ex_network=network,
+                                       ex_is_started=False)
+        self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+        self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER')
+        self.assertTrue('password' in node.extra)
+
+    def test_create_node_response_no_pass_customer_linux(self):
+        image = self.driver.ex_list_customer_images()[0]
+        network = self.driver.ex_list_networks()[0]
+        node = self.driver.create_node(name='test2', image=image, auth=None,
+                                       ex_description='test2 node', ex_network=network,
+                                       ex_is_started=False)
+        self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+        self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER')
+        self.assertTrue('password' not in node.extra)
+
+    def test_create_node_response_no_pass_customer_linux_STR(self):
+        image = self.driver.ex_list_customer_images()[0].id
+        network = self.driver.ex_list_networks()[0]
+        node = self.driver.create_node(name='test2', image=image, auth=None,
+                                       ex_description='test2 node', ex_network=network,
+                                       ex_is_started=False)
+        self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+        self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER')
+        self.assertTrue('password' not in node.extra)
+
     def test_create_node_response_STR(self):
         rootPw = 'pass123'
         image = self.driver.list_images()[0].id
@@ -715,6 +765,31 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin):
         location = self.driver.ex_get_location_by_id(None)
         self.assertIsNone(location)
 
+    def test_ex_get_base_image_by_id(self):
+        image_id = self.driver.list_images()[0].id
+        image = self.driver.ex_get_base_image_by_id(image_id)
+        self.assertEqual(image.extra['OS_type'], 'UNIX')
+
+    def test_ex_get_customer_image_by_id(self):
+        image_id = self.driver.ex_list_customer_images()[1].id
+        image = self.driver.ex_get_customer_image_by_id(image_id)
+        self.assertEqual(image.extra['OS_type'], 'WINDOWS')
+
+    def test_ex_get_image_by_id_base_img(self):
+        image_id = self.driver.list_images()[1].id
+        image = self.driver.ex_get_base_image_by_id(image_id)
+        self.assertEqual(image.extra['OS_type'], 'WINDOWS')
+
+    def test_ex_get_image_by_id_customer_img(self):
+        image_id = self.driver.ex_list_customer_images()[0].id
+        image = self.driver.ex_get_customer_image_by_id(image_id)
+        self.assertEqual(image.extra['OS_type'], 'UNIX')
+
+    def test_ex_get_image_by_id_customer_FAIL(self):
+        image_id = 'FAKE_IMAGE_ID'
+        with self.assertRaises(DimensionDataAPIException):
+            self.driver.ex_get_base_image_by_id(image_id)
+
     def test_priv_location_to_location_id(self):
         location = self.driver.ex_get_location_by_id('NA9')
         self.assertEqual(
@@ -732,6 +807,30 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin):
         with self.assertRaises(TypeError):
             self.driver._location_to_location_id([1, 2, 3])
 
+    def test_priv_image_needs_auth_os_img(self):
+        image = self.driver.list_images()[0]
+        self.assertTrue(self.driver._image_needs_auth(image))
+
+    def test_priv_image_needs_auth_os_img_STR(self):
+        image = self.driver.list_images()[0].id
+        self.assertTrue(self.driver._image_needs_auth(image))
+
+    def test_priv_image_needs_auth_cust_img_windows(self):
+        image = self.driver.ex_list_customer_images()[1]
+        self.assertTrue(self.driver._image_needs_auth(image))
+
+    def test_priv_image_needs_auth_cust_img_windows_STR(self):
+        image = self.driver.ex_list_customer_images()[1].id
+        self.assertTrue(self.driver._image_needs_auth(image))
+
+    def test_priv_image_needs_auth_cust_img_linux(self):
+        image = self.driver.ex_list_customer_images()[0]
+        self.assertTrue(not self.driver._image_needs_auth(image))
+
+    def test_priv_image_needs_auth_cust_img_linux_STR(self):
+        image = self.driver.ex_list_customer_images()[0].id
+        self.assertTrue(not self.driver._image_needs_auth(image))
+
 
 class InvalidRequestError(Exception):
     def __init__(self, tag):
@@ -1349,11 +1448,51 @@ class DimensionDataMockHttp(MockHttp):
             'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage.xml')
         return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+    def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_c14b1a46_2428_44c1_9c1a_b20e6418d08c(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_c14b1a46_2428_44c1_9c1a_b20e6418d08c.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_6b4fb0c7_a57b_4f58_b59c_9958f94f971a(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_6b4fb0c7_a57b_4f58_b59c_9958f94f971a.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_5234e5c7_01de_4411_8b6e_baeb8d91cf5d(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_BAD_REQUEST.xml')
+        return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_2ffa36c8_1848_49eb_b4fa_9d908775f68c(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_BAD_REQUEST.xml')
+        return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_FAKE_IMAGE_ID(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_osImage_BAD_REQUEST.xml')
+        return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
+
     def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage(self, method, url, body, headers):
         body = self.fixtures.load(
             'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage.xml')
         return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+    def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_5234e5c7_01de_4411_8b6e_baeb8d91cf5d(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_5234e5c7_01de_4411_8b6e_baeb8d91cf5d.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_2ffa36c8_1848_49eb_b4fa_9d908775f68c(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_2ffa36c8_1848_49eb_b4fa_9d908775f68c.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_FAKE_IMAGE_ID(self, method, url, body, headers):
+        body = self.fixtures.load(
+            'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_image_customerImage_BAD_REQUEST.xml')
+        return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
+
     def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_reconfigureServer(self, method, url, body, headers):
         request = ET.fromstring(body)
         if request.tag != "{urn:didata.com:api:cloud:types}reconfigureServer":