Add Aggregate XML client and tests.
Add Aggregate XML client for create, delete, get, list,
add_host, and remove_host.
This also adds tests using the client.
Partially Implements: blueprint missing-tempest-tests
Change-Id: Id0cb160e5dacc7acbc2ea091dd1160ee999d3887
diff --git a/tempest/clients.py b/tempest/clients.py
index 7b1e5cc..9b2c1f5 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -42,6 +42,7 @@
from tempest.services.compute.json.servers_client import ServersClientJSON
from tempest.services.compute.json.volumes_extensions_client import \
VolumesExtensionsClientJSON
+from tempest.services.compute.xml.aggregates_client import AggregatesClientXML
from tempest.services.compute.xml.availability_zone_client import \
AvailabilityZoneClientXML
from tempest.services.compute.xml.extensions_client import ExtensionsClientXML
@@ -201,6 +202,11 @@
"xml": ServiceClientXML,
}
+AGGREGATES_CLIENT = {
+ "json": AggregatesClientJSON,
+ "xml": AggregatesClientXML,
+}
+
class Manager(object):
@@ -270,6 +276,7 @@
self.availability_zone_client = \
AVAILABILITY_ZONE_CLIENT[interface](*client_args)
self.service_client = SERVICE_CLIENT[interface](*client_args)
+ self.aggregates_client = AGGREGATES_CLIENT[interface](*client_args)
except KeyError:
msg = "Unsupported interface type `%s'" % interface
raise exceptions.InvalidConfiguration(msg)
@@ -285,7 +292,6 @@
self.custom_object_client = ObjectClientCustomizedHeader(*client_args)
self.custom_account_client = \
AccountClientCustomizedHeader(*client_args)
- self.aggregates_client = AggregatesClientJSON(*client_args)
class AltManager(Manager):
diff --git a/tempest/services/compute/xml/aggregates_client.py b/tempest/services/compute/xml/aggregates_client.py
new file mode 100644
index 0000000..0ef8e22
--- /dev/null
+++ b/tempest/services/compute/xml/aggregates_client.py
@@ -0,0 +1,103 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation.
+# 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 import exceptions
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class AggregatesClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(AggregatesClientXML, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def _format_aggregate(self, g):
+ agg = xml_to_json(g)
+ aggregate = {}
+ for key, value in agg.items():
+ if key == 'hosts':
+ aggregate['hosts'] = []
+ for k, v in value.items():
+ aggregate['hosts'].append(v)
+ elif key == 'availability_zone':
+ aggregate[key] = None if value == 'None' else value
+ else:
+ aggregate[key] = value
+ return aggregate
+
+ def _parse_array(self, node):
+ return [self._format_aggregate(x) for x in node]
+
+ def list_aggregates(self):
+ """Get aggregate list."""
+ resp, body = self.get("os-aggregates", self.headers)
+ aggregates = self._parse_array(etree.fromstring(body))
+ return resp, aggregates
+
+ def get_aggregate(self, aggregate_id):
+ """Get details of the given aggregate."""
+ resp, body = self.get("os-aggregates/%s" % str(aggregate_id),
+ self.headers)
+ aggregate = self._format_aggregate(etree.fromstring(body))
+ return resp, aggregate
+
+ def create_aggregate(self, name, availability_zone=None):
+ """Creates a new aggregate."""
+ post_body = Element("aggregate",
+ name=name,
+ availability_zone=availability_zone)
+ resp, body = self.post('os-aggregates',
+ str(Document(post_body)),
+ self.headers)
+ aggregate = self._format_aggregate(etree.fromstring(body))
+ return resp, aggregate
+
+ def delete_aggregate(self, aggregate_id):
+ """Deletes the given aggregate."""
+ return self.delete("os-aggregates/%s" % str(aggregate_id),
+ self.headers)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.get_aggregate(id)
+ except exceptions.NotFound:
+ return True
+ return False
+
+ def add_host(self, aggregate_id, host):
+ """Adds a host to the given aggregate."""
+ post_body = Element("add_host", host=host)
+ resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+ str(Document(post_body)),
+ self.headers)
+ aggregate = self._format_aggregate(etree.fromstring(body))
+ return resp, aggregate
+
+ def remove_host(self, aggregate_id, host):
+ """Removes a host from the given aggregate."""
+ post_body = Element("remove_host", host=host)
+ resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+ str(Document(post_body)),
+ self.headers)
+ aggregate = self._format_aggregate(etree.fromstring(body))
+ return resp, aggregate
diff --git a/tempest/tests/compute/admin/test_aggregates.py b/tempest/tests/compute/admin/test_aggregates.py
index 06acc41..07df77f 100644
--- a/tempest/tests/compute/admin/test_aggregates.py
+++ b/tempest/tests/compute/admin/test_aggregates.py
@@ -27,13 +27,14 @@
Tests Aggregates API that require admin privileges
"""
+ _host_key = 'OS-EXT-SRV-ATTR:host'
_interface = 'json'
@classmethod
def setUpClass(cls):
super(AggregatesAdminTestJSON, cls).setUpClass()
cls.client = cls.os_adm.aggregates_client
- cls.user_client = cls.os.aggregates_client
+ cls.user_client = cls.aggregates_client
cls.aggregate_name_prefix = 'test_aggregate_'
cls.az_name_prefix = 'test_az_'
@@ -212,7 +213,7 @@
availability_zone=az_name)
servers_client.wait_for_server_status(server['id'], 'ACTIVE')
resp, body = admin_servers_client.get_server(server['id'])
- self.assertEqual(self.host, body['OS-EXT-SRV-ATTR:host'])
+ self.assertEqual(self.host, body[self._host_key])
@attr(type='negative')
def test_aggregate_add_non_exist_host(self):
@@ -254,3 +255,9 @@
self.assertRaises(exceptions.Unauthorized,
self.user_client.remove_host,
aggregate['id'], self.host)
+
+
+class AggregatesAdminTestXML(AggregatesAdminTestJSON):
+ _host_key = (
+ '{http://docs.openstack.org/compute/ext/extended_status/api/v1.1}host')
+ _interface = 'xml'
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index 221cfb6..b313e0b 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -62,6 +62,7 @@
cls.interfaces_client = os.interfaces_client
cls.fixed_ips_client = os.fixed_ips_client
cls.availability_zone_client = os.availability_zone_client
+ cls.aggregates_client = os.aggregates_client
cls.build_interval = cls.config.compute.build_interval
cls.build_timeout = cls.config.compute.build_timeout
cls.ssh_user = cls.config.compute.ssh_user