Merge "Adds "list hosts" test case - Cinder"
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
new file mode 100644
index 0000000..e7d8c02
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -0,0 +1,34 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# 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 tempest.api.volume import base
+from tempest.test import attr
+
+
+class VolumeHostsAdminTestsJSON(base.BaseVolumeAdminTest):
+    _interface = "json"
+
+    @attr(type='gate')
+    def test_list_hosts(self):
+        resp, hosts = self.hosts_client.list_hosts()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(hosts) >= 2, "No. of hosts are < 2,"
+                        "response of list hosts is: % s" % hosts)
+
+
+class VolumeHostsAdminTestsXML(VolumeHostsAdminTestsJSON):
+    _interface = 'xml'
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index cdf8638..465f570 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -151,3 +151,4 @@
         else:
             cls.os_adm = clients.AdminManager(interface=cls._interface)
         cls.client = cls.os_adm.volume_types_client
+        cls.hosts_client = cls.os_adm.volume_hosts_client
diff --git a/tempest/clients.py b/tempest/clients.py
index 291b946..86cf2ce 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -136,10 +136,14 @@
     ObjectClientCustomizedHeader
 from tempest.services.orchestration.json.orchestration_client import \
     OrchestrationClient
+from tempest.services.volume.json.admin.volume_hosts_client import \
+    VolumeHostsClientJSON
 from tempest.services.volume.json.admin.volume_types_client import \
     VolumeTypesClientJSON
 from tempest.services.volume.json.snapshots_client import SnapshotsClientJSON
 from tempest.services.volume.json.volumes_client import VolumesClientJSON
+from tempest.services.volume.xml.admin.volume_hosts_client import \
+    VolumeHostsClientXML
 from tempest.services.volume.xml.admin.volume_types_client import \
     VolumeTypesClientXML
 from tempest.services.volume.xml.snapshots_client import SnapshotsClientXML
@@ -239,6 +243,7 @@
             self.credentials_client = CredentialsClientXML(*client_args)
             self.instance_usages_audit_log_client = \
                 InstanceUsagesAuditLogClientXML(*client_args)
+            self.volume_hosts_client = VolumeHostsClientXML(*client_args)
 
             if client_args_v3_auth:
                 self.servers_client_v3_auth = ServersClientXML(
@@ -287,6 +292,7 @@
             self.credentials_client = CredentialsClientJSON(*client_args)
             self.instance_usages_audit_log_client = \
                 InstanceUsagesAuditLogClientJSON(*client_args)
+            self.volume_hosts_client = VolumeHostsClientJSON(*client_args)
 
             if client_args_v3_auth:
                 self.servers_client_v3_auth = ServersClientJSON(
diff --git a/tempest/services/volume/json/admin/volume_hosts_client.py b/tempest/services/volume/json/admin/volume_hosts_client.py
new file mode 100644
index 0000000..fc28ada
--- /dev/null
+++ b/tempest/services/volume/json/admin/volume_hosts_client.py
@@ -0,0 +1,46 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# 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.
+
+import json
+import urllib
+
+from tempest.common.rest_client import RestClient
+
+
+class VolumeHostsClientJSON(RestClient):
+    """
+    Client class to send CRUD Volume Hosts API requests to a Cinder endpoint
+    """
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(VolumeHostsClientJSON, self).__init__(config, username, password,
+                                                    auth_url, tenant_name)
+
+        self.service = self.config.volume.catalog_type
+        self.build_interval = self.config.volume.build_interval
+        self.build_timeout = self.config.volume.build_timeout
+
+    def list_hosts(self, params=None):
+        """Lists all hosts."""
+
+        url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['hosts']
diff --git a/tempest/services/volume/xml/admin/volume_hosts_client.py b/tempest/services/volume/xml/admin/volume_hosts_client.py
new file mode 100644
index 0000000..59ce933
--- /dev/null
+++ b/tempest/services/volume/xml/admin/volume_hosts_client.py
@@ -0,0 +1,72 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 Openstack Foundation.
+# 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.
+
+import urllib
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class VolumeHostsClientXML(RestClientXML):
+    """
+    Client class to send CRUD Volume Hosts API requests to a Cinder endpoint
+    """
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(VolumeHostsClientXML, self).__init__(config, username, password,
+                                                   auth_url, tenant_name)
+        self.service = self.config.volume.catalog_type
+        self.build_interval = self.config.compute.build_interval
+        self.build_timeout = self.config.compute.build_timeout
+
+    def _parse_array(self, node):
+        """
+        This method is to parse the "list" response body
+        Eg:
+
+        <?xml version='1.0' encoding='UTF-8'?>
+        <hosts>
+        <host service-status="available" service="cinder-scheduler"/>
+        <host service-status="available" service="cinder-volume"/>
+        </hosts>
+
+        This method will append the details of specified tag,
+        here it is "host"
+        Return value would be list of hosts as below
+
+        [{'service-status': 'available', 'service': 'cinder-scheduler'},
+         {'service-status': 'available', 'service': 'cinder-volume'}]
+        """
+        array = []
+        for child in node.getchildren():
+            tag_list = child.tag.split('}', 1)
+            if tag_list[0] == "host":
+                array.append(xml_to_json(child))
+        return array
+
+    def list_hosts(self, params=None):
+        """List all the hosts."""
+        url = 'os-hosts'
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body