Add zones_export_client's methods and tests to Designate tempest plugin

Partially-Implements: blueprint designate-tempest-plugin

Change-Id: Iff03a53842ac4e44ed720163695a35a74b970768
This commit is contained in:
sonu.kumar 2016-04-29 21:07:16 +09:00
parent 89edc117de
commit c8f7a70b9b
5 changed files with 292 additions and 10 deletions
designate_tempest_plugin

@ -22,6 +22,8 @@ from designate_tempest_plugin.services.dns.v2.json.blacklists_client import \
BlacklistsClient
from designate_tempest_plugin.services.dns.admin.json.quotas_client import \
QuotasClient
from designate_tempest_plugin.services.dns.v2.json.zone_exports_client import \
ZoneExportsClient
CONF = config.CONF
@ -44,3 +46,5 @@ class Manager(clients.Manager):
**params)
self.blacklists_client = BlacklistsClient(self.auth_provider, **params)
self.quotas_client = QuotasClient(self.auth_provider, **params)
self.zone_exports_client = ZoneExportsClient(self.auth_provider,
**params)

@ -0,0 +1,81 @@
"""
Copyright 2015 Rackspace
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.
"""
class ZoneFile(object):
def __init__(self, origin, ttl, records):
self.origin = origin
self.ttl = ttl
self.records = records
def __str__(self):
return str(self.__dict__)
def __repr__(self):
return str(self)
def __eq__(self, other):
return self.__dict__ == other.__dict__
@classmethod
def from_text(cls, text):
"""Return a ZoneFile from a string containing the zone file contents"""
# filter out empty lines and strip all leading/trailing whitespace.
# this assumes no multiline records
lines = [x.strip() for x in text.split('\n') if x.strip()]
assert lines[0].startswith('$ORIGIN')
assert lines[1].startswith('$TTL')
return ZoneFile(
origin=lines[0].split(' ')[1],
ttl=int(lines[1].split(' ')[1]),
records=[ZoneFileRecord.from_text(x) for x in lines[2:]],
)
class ZoneFileRecord(object):
def __init__(self, name, type, data):
self.name = str(name)
self.type = str(type)
self.data = str(data)
def __str__(self):
return str(self.__dict__)
def __repr__(self):
return str(self)
def __eq__(self, other):
return self.__dict__ == other.__dict__
def __hash__(self):
return hash(tuple(sorted(self.__dict__.items())))
@classmethod
def from_text(cls, text):
"""Create a ZoneFileRecord from a line of text of a zone file, like:
mydomain.com. IN NS ns1.example.com.
"""
# assumes records don't have a TTL between the name and the class.
# assumes no parentheses in the record, all on a single line.
parts = [x for x in text.split(' ', 4) if x.strip()]
name, rclass, rtype, data = parts
assert rclass == 'IN'
return cls(name=name, type=rtype, data=data)

@ -16,8 +16,10 @@ import functools
from oslo_log import log as logging
from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
from six.moves.urllib import parse as urllib
from designate_tempest_plugin.common.models import ZoneFile
LOG = logging.getLogger(__name__)
@ -50,8 +52,13 @@ class DnsClientBase(rest_client.RestClient):
def serialize(self, object_dict):
return json.dumps(object_dict)
def deserialize(self, object_str):
return json.loads(object_str)
def deserialize(self, resp, object_str):
if 'application/json' in resp['content-type']:
return json.loads(object_str)
elif 'text/dns' in resp['content-type']:
return ZoneFile.from_text(object_str)
else:
raise lib_exc.InvalidContentType()
def expected_success(self, expected_code, read_code):
# the base class method does not check correctly if read_code is not
@ -84,7 +91,7 @@ class DnsClientBase(rest_client.RestClient):
uuid=uuid,
params=params)
def _create_request(self, resource, object_dict, params=None,
def _create_request(self, resource, object_dict=None, 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'.
@ -108,9 +115,9 @@ class DnsClientBase(rest_client.RestClient):
extra_headers=extra_headers)
self.expected_success([201, 202], resp.status)
return resp, self.deserialize(body)
return resp, self.deserialize(resp, body)
def _show_request(self, resource, uuid, params=None):
def _show_request(self, resource, uuid, headers=None, params=None):
"""Gets a specific object of the specified type.
:param resource: The name of the REST resource, e.g., 'zones'.
:param uuid: Unique identifier of the object in UUID format.
@ -120,11 +127,11 @@ class DnsClientBase(rest_client.RestClient):
"""
uri = self.get_uri(resource, uuid=uuid, params=params)
resp, body = self.get(uri)
resp, body = self.get(uri, headers=headers)
self.expected_success(200, resp.status)
return resp, self.deserialize(body)
return resp, self.deserialize(resp, body)
def _list_request(self, resource, params=None):
"""Gets a list of objects.
@ -139,7 +146,7 @@ class DnsClientBase(rest_client.RestClient):
self.expected_success(200, resp.status)
return resp, self.deserialize(body)
return resp, self.deserialize(resp, body)
def _update_request(self, resource, uuid, object_dict, params=None):
"""Updates the specified object.
@ -158,7 +165,7 @@ class DnsClientBase(rest_client.RestClient):
self.expected_success([200, 202], resp.status)
return resp, self.deserialize(body)
return resp, self.deserialize(resp, body)
def _delete_request(self, resource, uuid, params=None):
"""Deletes the specified object.
@ -173,6 +180,6 @@ class DnsClientBase(rest_client.RestClient):
resp, body = self.delete(uri)
self.expected_success([202, 204], resp.status)
if resp.status == 202:
body = self.deserialize(body)
body = self.deserialize(resp, body)
return resp, body

@ -0,0 +1,93 @@
# 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.common import waiters
from designate_tempest_plugin.services.dns.v2.json import base
class ZoneExportsClient(base.DnsClientV2Base):
@base.handle_errors
def create_zone_export(self, uuid, params=None, wait_until=False):
"""Create a zone export.
:param uuid: Unique identifier of the zone in UUID format.
:param params: A Python dict that represents the query paramaters to
include in the request URI.
:param wait_until: Block until the exported zone reaches the
desired status
:return: Serialized imported zone as a dictionary.
"""
export_uri = 'zones/{0}/tasks/export'.format(uuid)
resp, body = self._create_request(
export_uri, params=params)
# Create Zone Export should Return a HTTP 202
self.expected_success(202, resp.status)
if wait_until:
waiters.wait_for_zone_export_status(self, body['id'], wait_until)
return resp, body
@base.handle_errors
def show_zone_export_records(self, uuid, params=None):
"""Gets a specific exported zone.
:param uuid: Unique identifier of the exported zone in UUID format.
:param params: A Python dict that represents the query paramaters to
include in the request URI.
:return: Serialized exported zone as a dictionary.
"""
return self._show_request(
'zones/tasks/exports', uuid, params=params)
@base.handle_errors
def show_zone_exported(self, uuid, params=None):
"""Gets a specific exported zone.
:param uuid: Unique identifier of the exported zone in UUID format.
:param params: A Python dict that represents the query paramaters to
include in the request URI.
:return: Serialized exported zone as a dictionary.
"""
headers = {'Accept': 'text/dns'}
return self._show_request(
'zones/tasks/exports/{0}/export'.format(uuid),
headers=headers, params=params)
@base.handle_errors
def list_zones_exports(self, params=None):
"""Gets all the exported zones
:param params: A Python dict that represents the query paramaters to
include in the request URI.
:return: Serialized exported zone as a list.
"""
return self._list_request(
'zones/tasks/exports', params=params)
@base.handle_errors
def delete_zone_export(self, uuid, params=None):
"""Deletes an exported zone having the specified UUID.
:param uuid: The unique identifier of the exported 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/exports', uuid, params=params)
# Delete Zone export should Return a HTTP 204
self.expected_success(204, resp.status)
return resp, body

@ -0,0 +1,97 @@
# 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 BaseZoneExportsTest(base.BaseDnsTest):
excluded_keys = ['created_at', 'updated_at', 'version', 'links',
'status', 'location']
class ZonesExportTest(BaseZoneExportsTest):
@classmethod
def setup_clients(cls):
super(ZonesExportTest, cls).setup_clients()
cls.zone_client = cls.os.zones_client
cls.client = cls.os.zone_exports_client
@test.attr(type='smoke')
@test.idempotent_id('2dd8a9a0-98a2-4bf6-bb51-286583b30f40')
def test_create_zone_export(self):
LOG.info('Create a zone')
_, zone = self.zone_client.create_zone()
self.addCleanup(self.zone_client.delete_zone, zone['id'])
LOG.info('Create a zone export')
_, zone_export = self.client.create_zone_export(zone['id'])
LOG.info('Ensure we respond with PENDING')
self.assertEqual('PENDING', zone_export['status'])
@test.attr(type='smoke')
@test.idempotent_id('2d29a2a9-1941-4b7e-9d8a-ad6c2140ea68')
def test_show_zone_export(self):
LOG.info('Create a zone')
_, zone = self.zone_client.create_zone()
self.addCleanup(self.zone_client.delete_zone, zone['id'])
LOG.info('Create a zone export')
resp, zone_export = self.client.create_zone_export(zone['id'])
LOG.info('Re-Fetch the zone export records')
_, body = self.client.show_zone_export_records(str(zone_export['id']))
LOG.info('Ensure the fetched response matches the records of '
'exported zone')
self.assertExpected(zone_export, body, self.excluded_keys)
@test.attr(type='smoke')
@test.idempotent_id('97234f00-8bcb-43f8-84dd-874f8bc4a80e')
def test_delete_zone_export(self):
LOG.info('Create a zone')
_, zone = self.zone_client.create_zone()
self.addCleanup(self.zone_client.delete_zone, zone['id'],
ignore_errors=lib_exc.NotFound)
LOG.info('Create a zone export')
_, zone_export = self.client.create_zone_export(zone['id'])
LOG.info('Delete the exported zone')
_, body = self.client.delete_zone_export(zone_export['id'])
LOG.info('Ensure the exported zone has been successfully deleted')
self.assertRaises(lib_exc.NotFound,
lambda: self.client.show_zone_export_records(zone_export['id']))
@test.attr(type='smoke')
@test.idempotent_id('476bfdfe-58c8-46e2-b376-8403c0fff440')
def test_list_zones_exports(self):
LOG.info('Create a zone')
_, zone = self.zone_client.create_zone()
self.addCleanup(self.zone_client.delete_zone, zone['id'])
_, export = self.client.create_zone_export(zone['id'])
LOG.info('List zones exports')
_, body = self.client.list_zones_exports()
self.assertTrue(len(body['exports']) > 0)