802164f2a2
This patch refactors the source and supporting files to use the top level package name of vmware_nsx_tempest_plugin. This better matches the project name as well as the name we plan to publish to PYPI with as per the discussion in [1]. A sample release has been published to the test pypi repo [2] to ensure this works. [1] https://review.openstack.org/#/c/584498/ [2] https://test.pypi.org/project/vmware-nsx-tempest-plugin/ Change-Id: I4cd89f49562c780754ebfb7e93c38b4e6556e314
245 lines
10 KiB
Python
245 lines
10 KiB
Python
# Copyright 2017 VMware, Inc.
|
|
# 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 functools
|
|
|
|
import six
|
|
|
|
from oslo_log import log as logging
|
|
from oslo_serialization import jsonutils as json
|
|
from six.moves.urllib import parse as urllib
|
|
from tempest.lib.common import rest_client
|
|
from tempest.lib import exceptions as lib_exc
|
|
|
|
from vmware_nsx_tempest_plugin.common import models
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def handle_errors(f):
|
|
"""A decorator that allows to ignore certain types of errors."""
|
|
|
|
@functools.wraps(f)
|
|
def wrapper(*args, **kwargs):
|
|
param_name = 'ignore_errors'
|
|
ignored_errors = kwargs.get(param_name, tuple())
|
|
|
|
if param_name in kwargs:
|
|
del kwargs[param_name]
|
|
|
|
try:
|
|
return f(*args, **kwargs)
|
|
except ignored_errors as e:
|
|
# Silently ignore errors as requested
|
|
LOG.debug('Ignoring exception of type %s, as requested', type(e))
|
|
|
|
return wrapper
|
|
|
|
|
|
class DnsClientBase(rest_client.RestClient):
|
|
"""Base Tempest REST client for Designate API"""
|
|
|
|
uri_prefix = ''
|
|
|
|
CREATE_STATUS_CODES = [202]
|
|
SHOW_STATUS_CODES = [200]
|
|
LIST_STATUS_CODES = [200]
|
|
PUT_STATUS_CODES = []
|
|
UPDATE_STATUS_CODES = [202]
|
|
DELETE_STATUS_CODES = [202]
|
|
|
|
def serialize(self, data):
|
|
if isinstance(data, six.string_types):
|
|
return data
|
|
return json.dumps(data)
|
|
|
|
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 models.ZoneFile.from_text(object_str.decode("utf-8"))
|
|
else:
|
|
raise lib_exc.InvalidContentType()
|
|
|
|
@classmethod
|
|
def expected_success(cls, expected_code, read_code):
|
|
# the base class method does not check correctly if read_code is not
|
|
# an int. warn about this and cast to int to avoid silent errors.
|
|
if not isinstance(read_code, int):
|
|
message = ("expected_success(%(expected_code)r, %(read_code)r) "
|
|
"received not-int read_code %(read_code)r" %
|
|
{'expected_code': expected_code,
|
|
'read_code': read_code})
|
|
LOG.warn(message)
|
|
return super(DnsClientBase, cls).expected_success(
|
|
expected_code=expected_code, read_code=int(read_code),
|
|
)
|
|
|
|
def get_uri(self, resource_name, uuid=None, params=None):
|
|
"""Get URI for a specific resource or object.
|
|
:param resource_name: 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
|
|
include in the request URI.
|
|
:returns: Relative URI for the resource or object.
|
|
"""
|
|
uri_pattern = '{pref}/{res}{uuid}{params}'
|
|
|
|
uuid = '/%s' % uuid if uuid else ''
|
|
params = '?%s' % urllib.urlencode(params) if params else ''
|
|
|
|
return uri_pattern.format(pref=self.uri_prefix,
|
|
res=resource_name,
|
|
uuid=uuid,
|
|
params=params)
|
|
|
|
def _create_request(self, resource, data=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'.
|
|
:param data: A Python dict that represents an object of the
|
|
specified type (to be serialized) or a plain string which
|
|
is sent as-is.
|
|
: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(data)
|
|
uri = self.get_uri(resource, params=params)
|
|
|
|
resp, body = self.post(uri, body=body, headers=headers,
|
|
extra_headers=extra_headers)
|
|
self.expected_success(self.CREATE_STATUS_CODES, resp.status)
|
|
|
|
return resp, self.deserialize(resp, body)
|
|
|
|
def _show_request(self, resource, uuid, headers=None, params=None,
|
|
extra_headers=False, ignore_response=False):
|
|
"""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.
|
|
:param params: A Python dict that represents the query paramaters to
|
|
include in the request URI.
|
|
: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: Serialized object as a dictionary.
|
|
"""
|
|
uri = self.get_uri(resource, uuid=uuid, params=params)
|
|
|
|
resp, body = self.get(
|
|
uri, headers=headers, extra_headers=extra_headers)
|
|
|
|
if not ignore_response:
|
|
self.expected_success(self.SHOW_STATUS_CODES, resp.status)
|
|
|
|
return resp, self.deserialize(resp, body)
|
|
|
|
def _list_request(self, resource, params=None):
|
|
"""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.
|
|
:returns: Serialized object as a dictionary.
|
|
"""
|
|
uri = self.get_uri(resource, params=params)
|
|
|
|
resp, body = self.get(uri)
|
|
|
|
self.expected_success(self.LIST_STATUS_CODES, resp.status)
|
|
|
|
return resp, self.deserialize(resp, body)
|
|
|
|
def _put_request(self, resource, uuid, data, params=None):
|
|
"""Updates the specified object using PUT request.
|
|
:param resource: The name of the REST resource, e.g., 'zones'.
|
|
:param uuid: Unique identifier of the object in UUID format.
|
|
:param data: A Python dict that represents an object of the
|
|
specified type (to be serialized) or a plain string which
|
|
is sent as-is.
|
|
:param params: A Python dict that represents the query paramaters to
|
|
include in the request URI.
|
|
:returns: Serialized object as a dictionary.
|
|
"""
|
|
body = self.serialize(data)
|
|
uri = self.get_uri(resource, uuid=uuid, params=params)
|
|
resp, body = self.put(uri, body=body)
|
|
|
|
self.expected_success(self.PUT_STATUS_CODES, resp.status)
|
|
|
|
return resp, self.deserialize(resp, body)
|
|
|
|
def _update_request(self, resource, uuid, data, params=None, headers=None,
|
|
extra_headers=False):
|
|
"""Updates the specified object using PATCH request.
|
|
:param resource: The name of the REST resource, e.g., 'zones'
|
|
:param uuid: Unique identifier of the object in UUID format.
|
|
:param data: A Python dict that represents an object of the
|
|
specified type (to be serialized) or a plain string which
|
|
is sent as-is.
|
|
: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: Serialized object as a dictionary.
|
|
"""
|
|
body = self.serialize(data)
|
|
uri = self.get_uri(resource, uuid=uuid, params=params)
|
|
|
|
resp, body = self.patch(uri, body=body,
|
|
headers=headers, extra_headers=True)
|
|
|
|
self.expected_success(self.UPDATE_STATUS_CODES, resp.status)
|
|
|
|
return resp, self.deserialize(resp, body)
|
|
|
|
def _delete_request(self, resource, uuid, params=None, headers=None,
|
|
extra_headers=False):
|
|
"""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
|
|
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 response body.
|
|
"""
|
|
uri = self.get_uri(resource, uuid=uuid, params=params)
|
|
|
|
resp, body = self.delete(
|
|
uri, headers=headers, extra_headers=extra_headers)
|
|
|
|
self.expected_success(self.DELETE_STATUS_CODES, resp.status)
|
|
if resp.status == 202:
|
|
body = self.deserialize(resp, body)
|
|
|
|
return resp, body
|