Add zones_import_client's methods and tests to Designate tempest plugin
Partially-Implements: blueprint designate-tempest-plugin
Change-Id: If01461617020f39b4da554b127e7b5e5fd704645
diff --git a/designate_tempest_plugin/clients.py b/designate_tempest_plugin/clients.py
index 79d6cba..805eeda 100644
--- a/designate_tempest_plugin/clients.py
+++ b/designate_tempest_plugin/clients.py
@@ -16,7 +16,8 @@
from designate_tempest_plugin.services.dns.v2.json.zones_client import \
ZonesClient
-
+from designate_tempest_plugin.services.dns.v2.json.zone_imports_client import \
+ ZoneImportsClient
CONF = config.CONF
@@ -35,3 +36,5 @@
params.update(self.default_params)
self.zones_client = ZonesClient(self.auth_provider, **params)
+ self.zone_imports_client = ZoneImportsClient(self.auth_provider,
+ **params)
diff --git a/designate_tempest_plugin/common/waiters.py b/designate_tempest_plugin/common/waiters.py
index a75870b..9563656 100644
--- a/designate_tempest_plugin/common/waiters.py
+++ b/designate_tempest_plugin/common/waiters.py
@@ -81,3 +81,36 @@
message = '(%s) %s' % (caller, message)
raise lib_exc.TimeoutException(message)
+
+
+def wait_for_zone_import_status(client, zone_import_id, status):
+ """Waits for an imported zone to reach the given status."""
+ LOG.info('Waiting for zone import %s to reach %s', zone_import_id, status)
+
+ _, zone_import = client.show_zone_import(zone_import_id)
+ start = int(time.time())
+
+ while zone_import['status'] != status:
+ time.sleep(client.build_interval)
+ _, zone_import = client.show_zone_import(zone_import_id)
+ status_curr = zone_import['status']
+ if status_curr == status:
+ LOG.info('Zone import %s reached %s', zone_import_id, status)
+ return
+
+ if int(time.time()) - start >= client.build_timeout:
+ message = ('Zone import %(zone_import_id)s failed to reach '
+ 'status=%(status)s within the required time '
+ '(%(timeout)s s). Current '
+ 'status: %(status_curr)s' %
+ {'zone_import_id': zone_import_id,
+ 'status': status,
+ 'status_curr': status_curr,
+ 'timeout': client.build_timeout})
+
+ caller = misc_utils.find_test_caller()
+
+ if caller:
+ message = '(%s) %s' % (caller, message)
+
+ raise lib_exc.TimeoutException(message)
diff --git a/designate_tempest_plugin/data_utils.py b/designate_tempest_plugin/data_utils.py
index 73207d5..8b18785 100644
--- a/designate_tempest_plugin/data_utils.py
+++ b/designate_tempest_plugin/data_utils.py
@@ -11,15 +11,20 @@
# 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.lib.common.utils import data_utils
-def rand_zone_name():
+def rand_zone_name(name='', prefix=None, suffix='.com.'):
"""Generate a random zone name
+ :param str name: The name that you want to include
+ :param prefix: the exact text to start the string. Defaults to "rand"
+ :param suffix: the exact text to end the string
:return: a random zone name e.g. example.org.
:rtype: string
"""
- return '%s.com.' % data_utils.rand_name()
+ name = data_utils.rand_name(name=name, prefix=prefix)
+ return name + suffix
def rand_email(domain=None):
@@ -37,3 +42,18 @@
:rtype: string
"""
return data_utils.rand_int_id(start, end)
+
+
+def rand_zonefile_data(name=None, ttl=None):
+ """Generate random zone data, with optional overrides
+
+ :return: A ZoneModel
+ """
+ zone_base = ('$ORIGIN &\n& # IN SOA ns.& nsadmin.& # # # # #\n'
+ '& # IN NS ns.&\n& # IN MX 10 mail.&\nns.& 360 IN A 1.0.0.1')
+ if name is None:
+ name = rand_zone_name()
+ if ttl is None:
+ ttl = str(rand_ttl(1200, 8400))
+
+ return zone_base.replace('&', name).replace('#', ttl)
diff --git a/designate_tempest_plugin/services/dns/json/base.py b/designate_tempest_plugin/services/dns/json/base.py
index 94307b8..e49885a 100644
--- a/designate_tempest_plugin/services/dns/json/base.py
+++ b/designate_tempest_plugin/services/dns/json/base.py
@@ -84,20 +84,28 @@
uuid=uuid,
params=params)
- def _create_request(self, resource, object_dict, params=None):
+ def _create_request(self, resource, object_dict, params=None,
+ headers=None, extra_headers=False):
"""Create an object of the specified type.
:param resource: The name of the REST resource, e.g., 'zones'.
:param object_dict: A Python dict that represents an object of the
specified type.
:param params: A Python dict that represents the query paramaters to
include in the request URI.
+ :param headers (dict): The headers to use for the request.
+ :param extra_headers (bool): Boolean value than indicates if the
+ headers returned by the get_headers()
+ method are to be used but additional
+ headers are needed in the request
+ pass them in as a dict.
:returns: A tuple with the server response and the deserialized created
object.
"""
body = self.serialize(object_dict)
uri = self.get_uri(resource, params=params)
- resp, body = self.post(uri, body=body)
+ resp, body = self.post(uri, body=body, headers=headers,
+ extra_headers=extra_headers)
self.expected_success([201, 202], resp.status)
return resp, self.deserialize(body)
@@ -119,7 +127,7 @@
return resp, self.deserialize(body)
def _list_request(self, resource, params=None):
- """Gets a list of specific objects.
+ """Gets a list of objects.
:param resource: The name of the REST resource, e.g., 'zones'.
:param params: A Python dict that represents the query paramaters to
include in the request URI.
@@ -134,7 +142,7 @@
return resp, self.deserialize(body)
def _update_request(self, resource, uuid, object_dict, params=None):
- """Update a specified object.
+ """Updates the specified object.
:param resource: The name of the REST resource, e.g., 'zones'
:param uuid: Unique identifier of the object in UUID format.
:param object_dict: A Python dict that represents an object of the
@@ -153,7 +161,7 @@
return resp, self.deserialize(body)
def _delete_request(self, resource, uuid, params=None):
- """Delete specified object.
+ """Deletes the specified object.
:param resource: The name of the REST resource, e.g., 'zones'.
:param uuid: The unique identifier of an object in UUID format.
:param params: A Python dict that represents the query paramaters to
@@ -164,4 +172,7 @@
resp, body = self.delete(uri)
self.expected_success([202, 204], resp.status)
- return resp, self.deserialize(body)
+ if resp.status == 202:
+ body = self.deserialize(body)
+
+ return resp, body
diff --git a/designate_tempest_plugin/services/dns/v2/json/zone_imports_client.py b/designate_tempest_plugin/services/dns/v2/json/zone_imports_client.py
new file mode 100644
index 0000000..872fc13
--- /dev/null
+++ b/designate_tempest_plugin/services/dns/v2/json/zone_imports_client.py
@@ -0,0 +1,80 @@
+# Copyright 2016 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 designate_tempest_plugin import data_utils as dns_data_utils
+from designate_tempest_plugin.common import waiters
+from designate_tempest_plugin.services.dns.v2.json import base
+
+
+class ZoneImportsClient(base.DnsClientV2Base):
+
+ @base.handle_errors
+ def create_zone_import(self, zonefile_data=None,
+ params=None, wait_until=None):
+ """Create a zone import.
+ :param zonefile_data: A tuple that represents zone data.
+ :param params: A Python dict that represents the query paramaters to
+ include in the request URI.
+ :return: Serialized imported zone as a dictionary.
+ """
+
+ headers = {'Content-Type': 'text/dns'}
+ zone_data = zonefile_data or dns_data_utils.rand_zonefile_data()
+ resp, body = self._create_request(
+ 'zones/tasks/imports', zone_data, headers=headers)
+
+ # Create Zone should Return a HTTP 202
+ self.expected_success(202, resp.status)
+
+ if wait_until:
+ waiters.wait_for_zone_import_status(self, body['id'], wait_until)
+
+ return resp, body
+
+ @base.handle_errors
+ def show_zone_import(self, uuid, params=None):
+ """Gets a specific zone import.
+ :param uuid: Unique identifier of the imported zone in UUID format.
+ :param params: A Python dict that represents the query paramaters to
+ include in the request URI.
+ :return: Serialized imported zone as a dictionary.
+ """
+ return self._show_request(
+ 'zones/tasks/imports', uuid, params=params)
+
+ @base.handle_errors
+ def list_zone_imports(self, params=None):
+ """Gets all the imported zones.
+ :param params: A Python dict that represents the query paramaters to
+ include in the request URI.
+ :return: Serialized imported zones as a list.
+ """
+ return self._list_request(
+ 'zones/tasks/imports', params=params)
+
+ @base.handle_errors
+ def delete_zone_import(self, uuid, params=None):
+ """Deletes a imported zone having the specified UUID.
+ :param uuid: The unique identifier of the imported zone.
+ :param params: A Python dict that represents the query paramaters to
+ include in the request URI.
+ :return: A tuple with the server response and the response body.
+ """
+ resp, body = self._delete_request(
+ 'zones/tasks/imports', uuid, params=params)
+
+ # Delete Zone should Return a HTTP 204
+ self.expected_success(204, resp.status)
+
+ return resp, body
diff --git a/designate_tempest_plugin/tests/api/v2/test_zones_imports.py b/designate_tempest_plugin/tests/api/v2/test_zones_imports.py
new file mode 100644
index 0000000..c2b5585
--- /dev/null
+++ b/designate_tempest_plugin/tests/api/v2/test_zones_imports.py
@@ -0,0 +1,80 @@
+# Copyright 2016 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 oslo_log import log as logging
+from tempest import test
+from tempest.lib import exceptions as lib_exc
+
+from designate_tempest_plugin.tests import base
+
+LOG = logging.getLogger(__name__)
+
+
+class BaseZonesImportTest(base.BaseDnsTest):
+ excluded_keys = ['created_at', 'updated_at', 'version', 'links',
+ 'status', 'message']
+
+
+class ZonesImportTest(BaseZonesImportTest):
+ @classmethod
+ def setup_clients(cls):
+ super(ZonesImportTest, cls).setup_clients()
+ cls.client = cls.os.zone_imports_client
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('2e2d907d-0609-405b-9c96-3cb2b87e3dce')
+ def test_create_zone_import(self):
+ LOG.info('Create a zone import')
+ _, zone_import = self.client.create_zone_import()
+ self.addCleanup(self.client.delete_zone_import, zone_import['id'])
+
+ LOG.info('Ensure we respond with PENDING')
+ self.assertEqual('PENDING', zone_import['status'])
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('c8909558-0dc6-478a-9e91-eb97b52e59e0')
+ def test_show_zone_import(self):
+ LOG.info('Create a zone import')
+ _, zone_import = self.client.create_zone_import()
+ self.addCleanup(self.client.delete_zone_import, zone_import['id'])
+
+ LOG.info('Re-Fetch the zone import')
+ resp, body = self.client.show_zone_import(zone_import['id'])
+
+ LOG.info('Ensure the fetched response matches the expected one')
+ self.assertExpected(zone_import, body, self.excluded_keys)
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('56a16e68-b241-4e41-bc5c-c40747fa68e3')
+ def test_delete_zone_import(self):
+ LOG.info('Create a zone import')
+ _, zone_import = self.client.create_zone_import()
+
+ LOG.info('Delete the zone')
+ resp, body = self.client.delete_zone_import(zone_import['id'])
+
+ LOG.info('Ensure successful deletion of imported zones')
+ self.assertRaises(lib_exc.NotFound,
+ lambda: self.client.show_zone_import(zone_import['id']))
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('9eab76af-1995-485f-a2ef-8290c1863aba')
+ def test_list_zones_imports(self):
+ LOG.info('Create a zone import')
+ _, zone = self.client.create_zone_import()
+
+ LOG.info('List zones imports')
+ _, body = self.client.list_zone_imports()
+
+ self.assertTrue(len(body['imports']) > 0)