Add XML support for test_security_groups.py and test_security_group_rules.py.

Change-Id: I5d521acf116e122c7b5608992c33854caad30bab
diff --git a/tempest/manager.py b/tempest/manager.py
index c4c1e1a..dc4b289 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -45,8 +45,8 @@
 ServersClient = servers_client.ServersClientJSON
 LimitsClient = limits_client.LimitsClientJSON
 ExtensionsClient = extensions_client.ExtensionsClientJSON
-SecurityGroupsClient = security_groups_client.SecurityGroupsClient
 FloatingIPsClient = floating_ips_client.FloatingIPsClientJSON
+SecurityGroupsClient = security_groups_client.SecurityGroupsClientJSON
 KeyPairsClient = keypairs_client.KeyPairsClientJSON
 VolumesExtensionsClient = volumes_extensions_client.VolumesExtensionsClientJSON
 VolumesClient = volumes_client.VolumesClientJSON
diff --git a/tempest/openstack.py b/tempest/openstack.py
index 0e4a0ef..c8bd238 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -33,7 +33,7 @@
 from tempest.services.nova.json.limits_client import LimitsClientJSON
 from tempest.services.nova.json.servers_client import ServersClientJSON
 from tempest.services.nova.json.security_groups_client \
-import SecurityGroupsClient
+import SecurityGroupsClientJSON
 from tempest.services.nova.json.keypairs_client import KeyPairsClientJSON
 from tempest.services.nova.json.volumes_extensions_client \
 import VolumesExtensionsClientJSON
@@ -46,6 +46,8 @@
 from tempest.services.nova.xml.images_client import ImagesClientXML
 from tempest.services.nova.xml.keypairs_client import KeyPairsClientXML
 from tempest.services.nova.xml.limits_client import LimitsClientXML
+from tempest.services.nova.xml.security_groups_client \
+import SecurityGroupsClientXML
 from tempest.services.nova.xml.servers_client import ServersClientXML
 from tempest.services.nova.xml.volumes_extensions_client \
 import VolumesExtensionsClientXML
@@ -100,7 +102,6 @@
     "xml": VolumesClientXML,
 }
 
-
 ADMIN_CLIENT = {
     "json": AdminClientJSON,
     "xml": AdminClientXML,
@@ -111,6 +112,11 @@
     "xml": TokenClientXML,
 }
 
+SECURITY_GROUPS_CLIENT = {
+    "json": SecurityGroupsClientJSON,
+    "xml": SecurityGroupsClientXML,
+}
+
 
 class Manager(object):
 
@@ -166,10 +172,11 @@
             self.volumes_client = VOLUMES_CLIENTS[interface](*client_args)
             self.admin_client = ADMIN_CLIENT[interface](*client_args)
             self.token_client = TOKEN_CLIENT[interface](self.config)
+            self.security_groups_client = \
+                SECURITY_GROUPS_CLIENT[interface](*client_args)
         except KeyError:
             msg = "Unsupported interface type `%s'" % interface
             raise exceptions.InvalidConfiguration(msg)
-        self.security_groups_client = SecurityGroupsClient(*client_args)
         self.console_outputs_client = ConsoleOutputsClient(*client_args)
         self.network_client = NetworkClient(*client_args)
 
diff --git a/tempest/services/nova/json/security_groups_client.py b/tempest/services/nova/json/security_groups_client.py
index 9d482be..7939249 100644
--- a/tempest/services/nova/json/security_groups_client.py
+++ b/tempest/services/nova/json/security_groups_client.py
@@ -2,11 +2,12 @@
 import json
 
 
-class SecurityGroupsClient(RestClient):
+class SecurityGroupsClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(SecurityGroupsClient, self).__init__(config, username, password,
-                                                   auth_url, tenant_name)
+        super(SecurityGroupsClientJSON, self).__init__(config, username,
+                                                       password, auth_url,
+                                                       tenant_name)
         self.service = self.config.compute.catalog_type
 
     def list_security_groups(self, params=None):
@@ -16,9 +17,9 @@
         if params is not None:
             param_list = []
             for param, value in params.iteritems():
-                param_list.append("%s=%s&" % (param, value))
+                param_list.append("%s=%s" % (param, value))
 
-            url += '?' + ' '.join(param_list)
+            url += '?' + ' &'.join(param_list)
 
         resp, body = self.get(url)
         body = json.loads(body)
diff --git a/tempest/services/nova/xml/security_groups_client.py b/tempest/services/nova/xml/security_groups_client.py
new file mode 100644
index 0000000..8edd1af
--- /dev/null
+++ b/tempest/services/nova/xml/security_groups_client.py
@@ -0,0 +1,133 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 IBM
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.nova.xml.common import Document
+from tempest.services.nova.xml.common import Element
+from tempest.services.nova.xml.common import Text
+from tempest.services.nova.xml.common import xml_to_json
+
+
+class SecurityGroupsClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(SecurityGroupsClientXML, self).__init__(
+                                        config, username, password,
+                                        auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def _parse_array(self, node):
+        array = []
+        for child in node.getchildren():
+            array.append(xml_to_json(child))
+        return array
+
+    def _parse_body(self, body):
+        json = xml_to_json(body)
+        return json
+
+    def list_security_groups(self, params=None):
+        """List all security groups for a user"""
+
+        url = 'os-security-groups'
+        if params is not None:
+            param_list = []
+            for param, value in params.iteritems():
+                param_list.append("%s=%s" % (param, value))
+
+            url += '?' + ' &'.join(param_list)
+
+        resp, body = self.get(url, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def get_security_group(self, security_group_id):
+        """Get the details of a Security Group"""
+        url = "os-security-groups/%s" % str(security_group_id)
+        resp, body = self.get(url, self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def create_security_group(self, name, description):
+        """
+        Creates a new security group.
+        name (Required): Name of security group.
+        description (Required): Description of security group.
+        """
+        security_group = Element("security_group", name=name)
+        des = Element("description")
+        des.append(Text(content=description))
+        security_group.append(des)
+        resp, body = self.post('os-security-groups',
+                               str(Document(security_group)),
+                               self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def delete_security_group(self, security_group_id):
+        """Deletes the provided Security Group"""
+        return self.delete('os-security-groups/%s' %
+                           str(security_group_id), self.headers)
+
+    def create_security_group_rule(self, parent_group_id, ip_proto, from_port,
+                                   to_port, **kwargs):
+        """
+        Creating a new security group rules.
+        parent_group_id :ID of Security group
+        ip_protocol : ip_proto (icmp, tcp, udp).
+        from_port: Port at start of range.
+        to_port  : Port at end of range.
+        Following optional keyword arguments are accepted:
+        cidr     : CIDR for address range.
+        group_id : ID of the Source group
+        """
+        group_rule = Element("security_group_rule")
+        parent_group = Element("parent_group_id")
+        parent_group.append(Text(content=parent_group_id))
+        ip_protocol = Element("ip_protocol")
+        ip_protocol.append(Text(content=ip_proto))
+        from_port_num = Element("from_port")
+        from_port_num.append(Text(content=str(from_port)))
+        to_port_num = Element("to_port")
+        to_port_num.append(Text(content=str(to_port)))
+
+        cidr = kwargs.get('cidr')
+        if cidr is not None:
+            cidr_num = Element("cidr")
+            cidr_num.append(Text(content=cidr))
+
+        group_id = kwargs.get('group_id')
+        if group_id is not None:
+            group_id_num = Element("group_id")
+            group_id_num.append(Text(content=group_id))
+
+        group_rule.append(parent_group)
+        group_rule.append(ip_protocol)
+        group_rule.append(from_port_num)
+        group_rule.append(to_port_num)
+
+        url = 'os-security-group-rules'
+        resp, body = self.post(url, str(Document(group_rule)), self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def delete_security_group_rule(self, group_rule_id):
+        """Deletes the provided Security Group rule"""
+        return self.delete('os-security-group-rules/%s' %
+                           str(group_rule_id), self.headers)
diff --git a/tempest/services/nova/xml/servers_client.py b/tempest/services/nova/xml/servers_client.py
index 9df53da..353f1c3 100644
--- a/tempest/services/nova/xml/servers_client.py
+++ b/tempest/services/nova/xml/servers_client.py
@@ -307,8 +307,8 @@
                          str(Document(revert)), self.headers)
 
     def create_image(self, server_id, image_name):
-        metadata = element('metadata')
-        image = element('createImage',
+        metadata = Element('metadata')
+        image = Element('createImage',
                         metadata,
                         xmlns=XMLNS_11,
                         name=image_name)
diff --git a/tempest/tests/compute/test_security_group_rules.py b/tempest/tests/compute/test_security_group_rules.py
index cde276a..fd56dc3 100644
--- a/tempest/tests/compute/test_security_group_rules.py
+++ b/tempest/tests/compute/test_security_group_rules.py
@@ -19,14 +19,13 @@
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.tests.compute import base
 
 
-class SecurityGroupRulesTest(BaseComputeTest):
+class SecurityGroupRulesTest(object):
 
-    @classmethod
+    @staticmethod
     def setUpClass(cls):
-        super(SecurityGroupRulesTest, cls).setUpClass()
         cls.client = cls.security_groups_client
 
     @attr(type='positive')
@@ -252,3 +251,19 @@
         else:
             self.fail('Security Group Rule should not be deleted '
                       'with nonexistant rule id')
+
+
+class SecurityGroupRulesTestJSON(base.BaseComputeTestJSON,
+                                 SecurityGroupRulesTest):
+    @classmethod
+    def setUpClass(cls):
+        super(SecurityGroupRulesTestJSON, cls).setUpClass()
+        SecurityGroupRulesTest.setUpClass(cls)
+
+
+class SecurityGroupRulesTestXML(base.BaseComputeTestXML,
+                                SecurityGroupRulesTest):
+    @classmethod
+    def setUpClass(cls):
+        super(SecurityGroupRulesTestXML, cls).setUpClass()
+        SecurityGroupRulesTest.setUpClass(cls)