Merge "Xml Support for Image Test scripts"
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index 3b01efb..27bb979 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -77,6 +77,19 @@
             data['images'].append(self._parse_image(image))
         return data
 
+    def _parse_key_value(self, node):
+        """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
+        data = {}
+        for node in node.getchildren():
+            data[node.get('key')] = node.text
+        return data
+
+    def _parse_metadata(self, node):
+        """Parse the response body without children."""
+        data = {}
+        data[node.get('key')] = node.text
+        return data
+
     def create_image(self, server_id, name, meta=None):
         """Creates an image of the original server."""
         post_body = Element('createImage', name=name)
@@ -153,51 +166,53 @@
             if int(time.time()) - start >= self.build_timeout:
                 raise exceptions.TimeoutException
 
+    def _metadata_body(self, meta):
+        post_body = Element('metadata')
+        for k, v in meta.items():
+            data = Element('meta', key=k)
+            data.append(Text(v))
+            post_body.append(data)
+        return post_body
+
     def list_image_metadata(self, image_id):
         """Lists all metadata items for an image."""
         resp, body = self.get("images/%s/metadata" % str(image_id),
                               self.headers)
-        body = xml_to_json(etree.fromstring(body))
-        return resp, body['metadata']
-
-    def _metadata_body(image_id, meta):
-        post_body = Document('metadata')
-        for k, v in meta:
-            text = Text(v)
-            metadata = Element('meta', text, key=k)
-            post_body.append(metadata)
-        return post_body
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
 
     def set_image_metadata(self, image_id, meta):
         """Sets the metadata for an image."""
-        post_body = self._metadata_body(image_id, meta)
-        resp, body = self.put('images/%s/metadata' % str(image_id),
-                              post_body, self.headers)
-        body = xml_to_json(etree.fromstring(body))
-        return resp, body['metadata']
+        post_body = self._metadata_body(meta)
+        resp, body = self.put('images/%s/metadata' % image_id,
+                              str(Document(post_body)), self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
 
     def update_image_metadata(self, image_id, meta):
         """Updates the metadata for an image."""
-        post_body = self._metadata_body(image_id, meta)
+        post_body = self._metadata_body(meta)
         resp, body = self.post('images/%s/metadata' % str(image_id),
-                               post_body, self.headers)
-        body = xml_to_json(etree.fromstring(body))
-        return resp, body['metadata']
+                               str(Document(post_body)), self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
 
     def get_image_metadata_item(self, image_id, key):
         """Returns the value for a specific image metadata key."""
         resp, body = self.get("images/%s/metadata/%s.xml" %
                               (str(image_id), key), self.headers)
-        body = xml_to_json(etree.fromstring(body))
-        return resp, body['meta']
+        body = self._parse_metadata(etree.fromstring(body))
+        return resp, body
 
     def set_image_metadata_item(self, image_id, key, meta):
         """Sets the value for a specific image metadata key."""
-        post_body = Document('meta', Text(meta), key=key)
-        resp, body = self.post('images/%s/metadata/%s' % (str(image_id), key),
-                               post_body, self.headers)
+        for k, v in meta.items():
+            post_body = Element('meta', key=key)
+            post_body.append(Text(v))
+        resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
+                              str(Document(post_body)), self.headers)
         body = xml_to_json(etree.fromstring(body))
-        return resp, body['meta']
+        return resp, body
 
     def update_image_metadata_item(self, image_id, key, meta):
         """Sets the value for a specific image metadata key."""
@@ -209,6 +224,5 @@
 
     def delete_image_metadata_item(self, image_id, key):
         """Deletes a single image metadata key/value pair."""
-        resp, body = self.delete("images/%s/metadata/%s" % (str(image_id), key,
-                                 self.headers))
-        return resp, body
+        return self.delete("images/%s/metadata/%s" % (str(image_id), key),
+                           self.headers)
diff --git a/tempest/tests/compute/images/test_image_metadata.py b/tempest/tests/compute/images/test_image_metadata.py
index 918075c..5d6439b 100644
--- a/tempest/tests/compute/images/test_image_metadata.py
+++ b/tempest/tests/compute/images/test_image_metadata.py
@@ -21,12 +21,12 @@
 from tempest.tests.compute import base
 
 
-class ImagesMetadataTest(base.BaseComputeTest):
+class ImagesMetadataTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ImagesMetadataTest, cls).setUpClass()
+        super(ImagesMetadataTestJSON, cls).setUpClass()
         cls.servers_client = cls.servers_client
         cls.client = cls.images_client
 
@@ -44,10 +44,10 @@
     @classmethod
     def tearDownClass(cls):
         cls.client.delete_image(cls.image_id)
-        super(ImagesMetadataTest, cls).tearDownClass()
+        super(ImagesMetadataTestJSON, cls).tearDownClass()
 
     def setUp(self):
-        super(ImagesMetadataTest, self).setUp()
+        super(ImagesMetadataTestJSON, self).setUp()
         meta = {'key1': 'value1', 'key2': 'value2'}
         resp, _ = self.client.set_image_metadata(self.image_id, meta)
         self.assertEqual(resp.status, 200)
@@ -143,3 +143,7 @@
         # item from nonexistant image
         self.assertRaises(exceptions.NotFound,
                           self.client.delete_image_metadata_item, 999, 'key1')
+
+
+class ImagesMetadataTestXML(ImagesMetadataTestJSON):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/images/test_list_image_filters.py b/tempest/tests/compute/images/test_list_image_filters.py
index 472f7fb..e668aca 100644
--- a/tempest/tests/compute/images/test_list_image_filters.py
+++ b/tempest/tests/compute/images/test_list_image_filters.py
@@ -22,12 +22,12 @@
 from tempest.tests.compute import base
 
 
-class ListImageFiltersTest(base.BaseComputeTest):
+class ListImageFiltersTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ListImageFiltersTest, cls).setUpClass()
+        super(ListImageFiltersTestJSON, cls).setUpClass()
         cls.client = cls.images_client
 
         resp, cls.server1 = cls.create_server()
@@ -65,7 +65,7 @@
         cls.client.delete_image(cls.image1_id)
         cls.client.delete_image(cls.image2_id)
         cls.client.delete_image(cls.image3_id)
-        super(ListImageFiltersTest, cls).tearDownClass()
+        super(ListImageFiltersTestJSON, cls).tearDownClass()
 
     @attr(type='negative')
     def test_get_image_not_existing(self):
@@ -140,7 +140,9 @@
         # Verify only the expected number of results are returned
         params = {'limit': '1'}
         resp, images = self.client.list_images(params)
-        self.assertEqual(1, len(images))
+        #when _interface='xml', one element for images_links in images
+        #ref: Question #224349
+        self.assertEqual(1, len([x for x in images if 'id' in x]))
 
     @attr(type='positive')
     def test_list_images_filter_by_changes_since(self):
@@ -226,3 +228,7 @@
     def test_get_nonexistant_image(self):
         # Negative test: GET on non existant image should fail
         self.assertRaises(exceptions.NotFound, self.client.get_image, 999)
+
+
+class ListImageFiltersTestXML(ListImageFiltersTestJSON):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/images/test_list_images.py b/tempest/tests/compute/images/test_list_images.py
index d583a95..ec9e7bc 100644
--- a/tempest/tests/compute/images/test_list_images.py
+++ b/tempest/tests/compute/images/test_list_images.py
@@ -19,17 +19,17 @@
 from tempest.tests.compute import base
 
 
-class ListImagesTest(base.BaseComputeTest):
+class ListImagesTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ListImagesTest, cls).setUpClass()
+        super(ListImagesTestJSON, cls).setUpClass()
         cls.client = cls.images_client
 
     @classmethod
     def tearDownClass(cls):
-        super(ListImagesTest, cls).tearDownClass()
+        super(ListImagesTestJSON, cls).tearDownClass()
 
     @attr(type='smoke')
     def test_get_image(self):
@@ -50,3 +50,7 @@
         resp, images = self.client.list_images_with_detail()
         found = any([i for i in images if i['id'] == self.image_ref])
         self.assertTrue(found)
+
+
+class ListImagesTestXML(ListImagesTestJSON):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/test_authorization.py b/tempest/tests/compute/test_authorization.py
index 02ea4d4..91cf39f 100644
--- a/tempest/tests/compute/test_authorization.py
+++ b/tempest/tests/compute/test_authorization.py
@@ -23,7 +23,7 @@
 from tempest.tests.compute import base
 
 
-class AuthorizationTest(base.BaseComputeTest):
+class AuthorizationTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
     @classmethod
@@ -32,7 +32,7 @@
             msg = "Need >1 user"
             raise cls.skipException(msg)
 
-        super(AuthorizationTest, cls).setUpClass()
+        super(AuthorizationTestJSON, cls).setUpClass()
 
         cls.client = cls.os.servers_client
         cls.images_client = cls.os.images_client
@@ -71,18 +71,15 @@
 
         name = rand_name('security')
         description = rand_name('description')
-        resp, cls.security_group = \
-        cls.security_client.create_security_group(name, description)
+        resp, cls.security_group = cls.security_client.create_security_group(
+            name, description)
 
         parent_group_id = cls.security_group['id']
         ip_protocol = 'tcp'
         from_port = 22
         to_port = 22
-        resp, cls.rule =\
-        cls.security_client.create_security_group_rule(
-                                        parent_group_id,
-                                        ip_protocol, from_port,
-                                        to_port)
+        resp, cls.rule = cls.security_client.create_security_group_rule(
+            parent_group_id, ip_protocol, from_port, to_port)
 
     @classmethod
     def tearDownClass(cls):
@@ -90,7 +87,7 @@
             cls.images_client.delete_image(cls.image['id'])
             cls.keypairs_client.delete_keypair(cls.keypairname)
             cls.security_client.delete_security_group(cls.security_group['id'])
-        super(AuthorizationTest, cls).tearDownClass()
+        super(AuthorizationTestJSON, cls).tearDownClass()
 
     def test_get_server_for_alt_account_fails(self):
         # A GET request for a server on another user's account should fail
@@ -278,7 +275,7 @@
             self.alt_security_client.base_url = self.saved_base_url
             if resp['status'] is not None:
                 self.alt_security_client.delete_security_group_rule(
-                                        body['id'])  # BUG
+                    body['id'])  # BUG
                 self.fail("Create security group rule request should not "
                           "happen if the tenant id does not match the"
                           " current user")
@@ -352,3 +349,7 @@
         self.assertRaises(exceptions.NotFound,
                           self.alt_client.get_console_output,
                           self.server['id'], 10)
+
+
+class AuthorizationTestXML(AuthorizationTestJSON):
+    _interface = 'xml'