Refactored existing tempest API tests
We must set up Tempest for Watcher (http://docs.openstack.org/developer/tempest/configuration.html) to run integration tests inside devstack environment. This patchset is a refactoring of the stale Tempest tests to now use the latest Tempest coding standards (like using plugins and credentials factory). This commit will have an effect on the doc as we need to integrate Tempest in the Watcher documentation. DocImpact Partially Implements: blueprint tempest-basic-set-up Change-Id: I7600ff8a28d524b56c7dd4903ac4d203634ae412
This commit is contained in:
committed by
Jean-Emile DARTOIS
parent
8832ad78e2
commit
595b13a622
0
watcher_tempest_plugin/services/__init__.py
Normal file
0
watcher_tempest_plugin/services/__init__.py
Normal file
211
watcher_tempest_plugin/services/infra_optim/base.py
Normal file
211
watcher_tempest_plugin/services/infra_optim/base.py
Normal file
@@ -0,0 +1,211 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# 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 abc
|
||||
import functools
|
||||
|
||||
import six
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from tempest.common import service_client
|
||||
|
||||
|
||||
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:
|
||||
# Silently ignore errors
|
||||
pass
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseInfraOptimClient(service_client.ServiceClient):
|
||||
"""Base Tempest REST client for Watcher API."""
|
||||
|
||||
URI_PREFIX = ''
|
||||
|
||||
@abc.abstractmethod
|
||||
def serialize(self, object_dict):
|
||||
"""Serialize an Watcher object."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def deserialize(self, object_str):
|
||||
"""Deserialize an Watcher object."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_uri(self, resource_name, uuid=None, permanent=False):
|
||||
"""Get URI for a specific resource or object.
|
||||
|
||||
:param resource_name: The name of the REST resource, e.g., 'audits'.
|
||||
:param uuid: The unique identifier of an object in UUID format.
|
||||
:return: Relative URI for the resource or object.
|
||||
"""
|
||||
|
||||
prefix = self.URI_PREFIX if not permanent else ''
|
||||
|
||||
return '{pref}/{res}{uuid}'.format(pref=prefix,
|
||||
res=resource_name,
|
||||
uuid='/%s' % uuid if uuid else '')
|
||||
|
||||
def _make_patch(self, allowed_attributes, **kw):
|
||||
"""Create a JSON patch according to RFC 6902.
|
||||
|
||||
:param allowed_attributes: An iterable object that contains a set of
|
||||
allowed attributes for an object.
|
||||
:param **kw: Attributes and new values for them.
|
||||
:return: A JSON path that sets values of the specified attributes to
|
||||
the new ones.
|
||||
"""
|
||||
|
||||
def get_change(kw, path='/'):
|
||||
for name, value in kw.items():
|
||||
if isinstance(value, dict):
|
||||
for ch in get_change(value, path + '%s/' % name):
|
||||
yield ch
|
||||
else:
|
||||
if value is None:
|
||||
yield {'path': path + name,
|
||||
'op': 'remove'}
|
||||
else:
|
||||
yield {'path': path + name,
|
||||
'value': value,
|
||||
'op': 'replace'}
|
||||
|
||||
patch = [ch for ch in get_change(kw)
|
||||
if ch['path'].lstrip('/') in allowed_attributes]
|
||||
|
||||
return patch
|
||||
|
||||
def _list_request(self, resource, permanent=False, **kwargs):
|
||||
"""Get the list of objects of the specified type.
|
||||
|
||||
:param resource: The name of the REST resource, e.g., 'audits'.
|
||||
"param **kw: Parameters for the request.
|
||||
:return: A tuple with the server response and deserialized JSON list
|
||||
of objects
|
||||
"""
|
||||
|
||||
uri = self._get_uri(resource, permanent=permanent)
|
||||
if kwargs:
|
||||
uri += "?%s" % urlparse.urlencode(kwargs)
|
||||
|
||||
resp, body = self.get(uri)
|
||||
self.expected_success(200, resp['status'])
|
||||
|
||||
return resp, self.deserialize(body)
|
||||
|
||||
def _show_request(self, resource, uuid, permanent=False, **kwargs):
|
||||
"""Gets a specific object of the specified type.
|
||||
|
||||
:param uuid: Unique identifier of the object in UUID format.
|
||||
:return: Serialized object as a dictionary.
|
||||
"""
|
||||
|
||||
if 'uri' in kwargs:
|
||||
uri = kwargs['uri']
|
||||
else:
|
||||
uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
|
||||
resp, body = self.get(uri)
|
||||
self.expected_success(200, resp['status'])
|
||||
|
||||
return resp, self.deserialize(body)
|
||||
|
||||
def _create_request(self, resource, object_dict):
|
||||
"""Create an object of the specified type.
|
||||
|
||||
:param resource: The name of the REST resource, e.g., 'audits'.
|
||||
:param object_dict: A Python dict that represents an object of the
|
||||
specified type.
|
||||
:return: A tuple with the server response and the deserialized created
|
||||
object.
|
||||
"""
|
||||
|
||||
body = self.serialize(object_dict)
|
||||
uri = self._get_uri(resource)
|
||||
|
||||
resp, body = self.post(uri, body=body)
|
||||
self.expected_success(201, resp['status'])
|
||||
|
||||
return resp, self.deserialize(body)
|
||||
|
||||
def _delete_request(self, resource, uuid):
|
||||
"""Delete specified object.
|
||||
|
||||
:param resource: The name of the REST resource, e.g., 'audits'.
|
||||
:param uuid: The unique identifier of an object in UUID format.
|
||||
:return: A tuple with the server response and the response body.
|
||||
"""
|
||||
|
||||
uri = self._get_uri(resource, uuid)
|
||||
|
||||
resp, body = self.delete(uri)
|
||||
self.expected_success(204, resp['status'])
|
||||
return resp, body
|
||||
|
||||
def _patch_request(self, resource, uuid, patch_object):
|
||||
"""Update specified object with JSON-patch.
|
||||
|
||||
:param resource: The name of the REST resource, e.g., 'audits'.
|
||||
:param uuid: The unique identifier of an object in UUID format.
|
||||
:return: A tuple with the server response and the serialized patched
|
||||
object.
|
||||
"""
|
||||
|
||||
uri = self._get_uri(resource, uuid)
|
||||
patch_body = self.serialize(patch_object)
|
||||
|
||||
resp, body = self.patch(uri, body=patch_body)
|
||||
self.expected_success(200, resp['status'])
|
||||
return resp, self.deserialize(body)
|
||||
|
||||
@handle_errors
|
||||
def get_api_description(self):
|
||||
"""Retrieves all versions of the Watcher API."""
|
||||
|
||||
return self._list_request('', permanent=True)
|
||||
|
||||
@handle_errors
|
||||
def get_version_description(self, version='v1'):
|
||||
"""Retrieves the description of the API.
|
||||
|
||||
:param version: The version of the API. Default: 'v1'.
|
||||
:return: Serialized description of API resources.
|
||||
"""
|
||||
|
||||
return self._list_request(version, permanent=True)
|
||||
|
||||
def _put_request(self, resource, put_object):
|
||||
"""Update specified object with JSON-patch."""
|
||||
|
||||
uri = self._get_uri(resource)
|
||||
put_body = self.serialize(put_object)
|
||||
|
||||
resp, body = self.put(uri, body=put_body)
|
||||
self.expected_success(202, resp['status'])
|
||||
return resp, body
|
||||
146
watcher_tempest_plugin/services/infra_optim/v1/json/client.py
Normal file
146
watcher_tempest_plugin/services/infra_optim/v1/json/client.py
Normal file
@@ -0,0 +1,146 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# 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 uuid
|
||||
|
||||
from watcher_tempest_plugin.services.infra_optim import base
|
||||
|
||||
|
||||
class InfraOptimClientJSON(base.BaseInfraOptimClient):
|
||||
"""Base Tempest REST client for Watcher API v1."""
|
||||
|
||||
URI_PREFIX = 'v1'
|
||||
|
||||
def serialize(self, object_dict):
|
||||
"""Serialize an Watcher object."""
|
||||
return json.dumps(object_dict)
|
||||
|
||||
def deserialize(self, object_str):
|
||||
"""Deserialize an Watcher object."""
|
||||
return json.loads(object_str.decode('utf-8'))
|
||||
|
||||
# ### AUDIT TEMPLATES ### #
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_templates(self, **kwargs):
|
||||
"""List all existing audit templates."""
|
||||
return self._list_request('audit_templates', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_template_audits(self, audit_template_uuid):
|
||||
"""Lists all audits associated with a audit template."""
|
||||
return self._list_request(
|
||||
'/audit_templates/%s/audits' % audit_template_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_templates_detail(self, **kwargs):
|
||||
"""Lists details of all existing audit templates."""
|
||||
return self._list_request('/audit_templates/detail', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def show_audit_template(self, audit_template_uuid):
|
||||
"""Gets a specific audit template.
|
||||
|
||||
:param audit_template_uuid: Unique identifier of the audit template
|
||||
:return: Serialized audit template as a dictionary.
|
||||
"""
|
||||
return self._show_request('audit_templates', audit_template_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def filter_audit_template_by_host_aggregate(self, host_aggregate):
|
||||
"""Gets an audit template associated with given host agregate ID.
|
||||
|
||||
:param host_aggregate: Unique identifier of the host aggregate
|
||||
:return: Serialized audit template as a dictionary.
|
||||
"""
|
||||
return self._list_request('/audit_templates',
|
||||
host_aggregate=host_aggregate)
|
||||
|
||||
@base.handle_errors
|
||||
def filter_audit_template_by_goal(self, goal):
|
||||
"""Gets an audit template associated with given goal.
|
||||
|
||||
:param goal: goal identifier
|
||||
:return: Serialized audit template as a dictionary.
|
||||
"""
|
||||
return self._list_request('/audit_templates', goal=goal)
|
||||
|
||||
@base.handle_errors
|
||||
def create_audit_template(self, **kwargs):
|
||||
"""Creates an audit template with the specified parameters.
|
||||
|
||||
:param name: The name of the audit template. Default: My Audit Template
|
||||
:param description: The description of the audit template.
|
||||
Default: AT Description
|
||||
:param goal: The goal associated within the audit template.
|
||||
Default: DUMMY
|
||||
:param host_aggregate: ID of the host aggregate targeted by
|
||||
this audit template. Default: 1
|
||||
:param extra: IMetadata associated to this audit template.
|
||||
Default: {}
|
||||
:return: A tuple with the server response and the created audit
|
||||
template.
|
||||
"""
|
||||
|
||||
parameters = {k: v for k, v in kwargs.items() if v is not None}
|
||||
# This name is unique to avoid the DB unique constraint on names
|
||||
unique_name = 'Tempest Audit Template %s' % uuid.uuid4()
|
||||
|
||||
audit_template = {
|
||||
'name': parameters.get('name', unique_name),
|
||||
'description': parameters.get('description', ''),
|
||||
'goal': parameters.get('goal', 'DUMMY'),
|
||||
'host_aggregate': parameters.get('host_aggregate', 1),
|
||||
'extra': parameters.get('extra', {}),
|
||||
}
|
||||
|
||||
return self._create_request('audit_templates', audit_template)
|
||||
|
||||
@base.handle_errors
|
||||
def create_audit(self, audit_template_uuid, **kwargs):
|
||||
"""Create an audit with the specified parameters.
|
||||
|
||||
:param audit_template_uuid: Audit template ID used by the audit
|
||||
:return: A tuple with the server response and the created audit.
|
||||
"""
|
||||
audit = {'audit_template_uuid': audit_template_uuid}
|
||||
audit.update(kwargs)
|
||||
|
||||
return self._create_request('audits', audit)
|
||||
|
||||
@base.handle_errors
|
||||
def delete_audit_template(self, audit_template_uuid):
|
||||
"""Deletes an audit template having the specified UUID.
|
||||
|
||||
:param audit_template_uuid: The unique identifier of the audit template
|
||||
:return: A tuple with the server response and the response body.
|
||||
"""
|
||||
|
||||
return self._delete_request('audit_templates', audit_template_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def update_audit_template(self, audit_template_uuid, patch):
|
||||
"""Update the specified audit template.
|
||||
|
||||
:param audit_template_uuid: The unique identifier of the audit template
|
||||
:param patch: List of dicts representing json patches.
|
||||
:return: A tuple with the server response and the updated audit
|
||||
template.
|
||||
"""
|
||||
|
||||
return self._patch_request('audit_templates',
|
||||
audit_template_uuid, patch)
|
||||
Reference in New Issue
Block a user