diff --git a/doc/source/contributor/testing.rst b/doc/source/contributor/testing.rst index ab0675f09..afb0e72ec 100644 --- a/doc/source/contributor/testing.rst +++ b/doc/source/contributor/testing.rst @@ -47,4 +47,12 @@ When you're done, deactivate the virtualenv:: $ deactivate -.. include:: ../../../watcher_tempest_plugin/README.rst +.. _tempest_tests: + +Tempest tests +============= + +Tempest tests for Watcher has been migrated to the external repo +`watcher-tempest-plugin`_. + +.. _watcher-tempest-plugin: https://github.com/openstack/watcher-tempest-plugin diff --git a/setup.cfg b/setup.cfg index 374a6e466..df2bee1b1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,7 +21,6 @@ classifier = [files] packages = watcher - watcher_tempest_plugin data_files = etc/ = etc/* @@ -40,9 +39,6 @@ console_scripts = watcher-applier = watcher.cmd.applier:main watcher-sync = watcher.cmd.sync:main -tempest.test_plugins = - watcher_tests = watcher_tempest_plugin.plugin:WatcherTempestPlugin - watcher.database.migration_backend = sqlalchemy = watcher.db.sqlalchemy.migration @@ -103,7 +99,6 @@ autodoc_exclude_modules = watcher.db.sqlalchemy.alembic.env watcher.db.sqlalchemy.alembic.versions.* watcher.tests.* - watcher_tempest_plugin.* watcher.doc diff --git a/watcher_tempest_plugin/README.rst b/watcher_tempest_plugin/README.rst deleted file mode 100644 index 1fd805fec..000000000 --- a/watcher_tempest_plugin/README.rst +++ /dev/null @@ -1,158 +0,0 @@ -.. - Except where otherwise noted, this document is licensed under Creative - Commons Attribution 3.0 License. You can view the license at: - - https://creativecommons.org/licenses/by/3.0/ - -.. _tempest_tests: - -Tempest tests -============= - -The following procedure gets you started with Tempest testing but you can also -refer to the `Tempest documentation`_ for more details. - -.. _Tempest documentation: https://docs.openstack.org/tempest/latest - - -Tempest installation --------------------- - -To install Tempest you can issue the following commands:: - - $ git clone https://github.com/openstack/tempest/ - $ cd tempest/ - $ pip install . - -The folder you are into now will be called ```` from now onwards. - -Please note that although it is fully working outside a virtual environment, it -is recommended to install within a `venv`. - - -Watcher Tempest testing setup ------------------------------ - -You can now install Watcher alongside it in development mode by issuing the -following command:: - - $ pip install -e - -Then setup a local working environment (here ``watcher-cloud``) for running -Tempest for Watcher which shall contain the configuration for your OpenStack -integration platform. - -In a virtual environment, you can do so by issuing the following command:: - - $ cd - $ tempest init watcher-cloud - -Otherwise, if you are not using a virtualenv:: - - $ cd - $ tempest init --config-dir ./etc watcher-cloud - -By default the configuration file is empty so before starting, you need to -issue the following commands:: - - $ cd /watcher-cloud/etc - $ cp tempest.conf.sample tempest.conf - -At this point you need to edit the ``watcher-cloud/etc/tempest.conf`` -file as described in the `Tempest configuration guide`_. -Shown below is a minimal configuration you need to set within your -``tempest.conf`` configuration file which can get you started. - -For Keystone V3:: - - [identity] - uri_v3 = http://:/v3 - auth_version = v3 - - [auth] - admin_username = - admin_password = - admin_tenant_name = - admin_domain_name = - - [identity-feature-enabled] - api_v2 = false - api_v3 = true - -For Keystone V2:: - - [identity] - uri = http://:/v2.0 - auth_version = v2 - - [auth] - admin_tenant_name = - admin_username = - admin_password = - -In both cases:: - - [network] - public_network_id = - -You now have the minimum configuration for running Watcher Tempest tests on a -single node. - -Since deploying Watcher with only a single compute node is not very useful, a -few more configuration have to be set in your ``tempest.conf`` file in order to -enable the execution of multi-node scenarios:: - - [compute] - # To indicate Tempest test that you have provided enough compute nodes - min_compute_nodes = 2 - - # Image UUID you can get using the "glance image-list" command - image_ref = - - -For more information, please refer to: - -- Keystone connection: https://docs.openstack.org/tempest/latest/configuration.html#keystone-connection-info -- Dynamic Keystone Credentials: https://docs.openstack.org/tempest/latest/configuration.html#dynamic-credentials - -.. _virtual environment: http://docs.python-guide.org/en/latest/dev/virtualenvs/ -.. _Tempest configuration guide: http://docs.openstack.org/tempest/latest/configuration.html - - -Watcher Tempest tests execution -------------------------------- - -To list all Watcher Tempest cases, you can issue the following commands:: - - $ cd - $ testr list-tests watcher - -To run only these tests in Tempest, you can then issue these commands:: - - $ ./run_tempest.sh --config watcher-cloud/etc/tempest.conf -N -- watcher - -Or alternatively the following commands if you are:: - - $ cd /watcher-cloud - $ ../run_tempest.sh -N -- watcher - -To run a single test case, go to Tempest directory, then run with test case -name, e.g.:: - - $ cd - $ ./run_tempest.sh --config watcher-cloud/etc/tempest.conf -N \ - -- watcher_tempest_plugin.tests.api.admin.test_audit_template.TestCreateDeleteAuditTemplate.test_create_audit_template - -Alternatively, you can also run the Watcher Tempest plugin tests using tox. But -before you can do so, you need to follow the Tempest explanation on running -`tox with plugins`_. Then, run:: - - $ export TEMPEST_CONFIG_DIR=/watcher-cloud/etc/ - $ tox -eall-plugin watcher - -.. _tox with plugins: https://docs.openstack.org/tempest/latest/plugin.html#notes-for-using-plugins-with-virtualenvs - -And, to run a specific test:: - - $ export TEMPEST_CONFIG_DIR=/watcher-cloud/etc/ - $ tox -eall-plugin watcher_tempest_plugin.tests.api.admin.test_audit_template.TestCreateDeleteAuditTemplate.test_create_audit_template diff --git a/watcher_tempest_plugin/__init__.py b/watcher_tempest_plugin/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/config.py b/watcher_tempest_plugin/config.py deleted file mode 100644 index 426399d26..000000000 --- a/watcher_tempest_plugin/config.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- 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. - -from oslo_config import cfg - - -service_option = cfg.BoolOpt("watcher", - default=True, - help="Whether or not watcher is expected to be " - "available") diff --git a/watcher_tempest_plugin/infra_optim_clients.py b/watcher_tempest_plugin/infra_optim_clients.py deleted file mode 100644 index edf20913b..000000000 --- a/watcher_tempest_plugin/infra_optim_clients.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- 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 six -from tempest import clients -from tempest.common import credentials_factory as creds_factory -from tempest import config - -from watcher_tempest_plugin.services.infra_optim.v1.json import client as ioc - -CONF = config.CONF - - -@six.add_metaclass(abc.ABCMeta) -class BaseManager(clients.Manager): - - def __init__(self, credentials): - super(BaseManager, self).__init__(credentials) - self.io_client = ioc.InfraOptimClientJSON( - self.auth_provider, 'infra-optim', CONF.identity.region) - - -class AdminManager(BaseManager): - def __init__(self): - super(AdminManager, self).__init__( - creds_factory.get_configured_admin_credentials(), - ) diff --git a/watcher_tempest_plugin/plugin.py b/watcher_tempest_plugin/plugin.py deleted file mode 100644 index 560c544ce..000000000 --- a/watcher_tempest_plugin/plugin.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 os - -from tempest.test_discover import plugins - -from watcher_tempest_plugin import config as watcher_config - - -class WatcherTempestPlugin(plugins.TempestPlugin): - def load_tests(self): - base_path = os.path.split(os.path.dirname( - os.path.abspath(__file__)))[0] - test_dir = "watcher_tempest_plugin/tests" - full_test_dir = os.path.join(base_path, test_dir) - return full_test_dir, base_path - - def register_opts(self, conf): - conf.register_opt(watcher_config.service_option, - group='service_available') - - def get_opt_lists(self): - return [('service_available', [watcher_config.service_option])] diff --git a/watcher_tempest_plugin/services/__init__.py b/watcher_tempest_plugin/services/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/services/infra_optim/__init__.py b/watcher_tempest_plugin/services/infra_optim/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/services/infra_optim/base.py b/watcher_tempest_plugin/services/infra_optim/base.py deleted file mode 100644 index d2487748c..000000000 --- a/watcher_tempest_plugin/services/infra_optim/base.py +++ /dev/null @@ -1,211 +0,0 @@ -# -*- 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.lib.common import rest_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(rest_client.RestClient): - """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, int(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, int(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, int(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, int(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, int(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, int(resp['status'])) - return resp, body diff --git a/watcher_tempest_plugin/services/infra_optim/v1/__init__.py b/watcher_tempest_plugin/services/infra_optim/v1/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/services/infra_optim/v1/json/__init__.py b/watcher_tempest_plugin/services/infra_optim/v1/json/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/services/infra_optim/v1/json/client.py b/watcher_tempest_plugin/services/infra_optim/v1/json/client.py deleted file mode 100644 index 2ee27f5d4..000000000 --- a/watcher_tempest_plugin/services/infra_optim/v1/json/client.py +++ /dev/null @@ -1,331 +0,0 @@ -# -*- 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. - -from oslo_serialization import jsonutils -from watcher.common import utils -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 jsonutils.dumps(object_dict) - - def deserialize(self, object_str): - """Deserialize an Watcher object.""" - return jsonutils.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_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 create_audit_template(self, **kwargs): - """Creates an audit template with the specified parameters. - - :param name: The name of the audit template. - :param description: The description of the audit template. - :param goal_uuid: The related Goal UUID associated. - :param strategy_uuid: The related Strategy UUID associated. - :param audit_scope: Scope the audit should apply to. - :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' % utils.generate_uuid() - - audit_template = { - 'name': parameters.get('name', unique_name), - 'description': parameters.get('description'), - 'goal': parameters.get('goal'), - 'strategy': parameters.get('strategy'), - 'scope': parameters.get('scope', []), - } - - return self._create_request('audit_templates', audit_template) - - @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) - - # ### AUDITS ### # - - @base.handle_errors - def list_audits(self, **kwargs): - """List all existing audit templates.""" - return self._list_request('audits', **kwargs) - - @base.handle_errors - def list_audits_detail(self, **kwargs): - """Lists details of all existing audit templates.""" - return self._list_request('/audits/detail', **kwargs) - - @base.handle_errors - def show_audit(self, audit_uuid): - """Gets a specific audit template. - - :param audit_uuid: Unique identifier of the audit template - :return: Serialized audit template as a dictionary - """ - return self._show_request('audits', audit_uuid) - - @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) - if not audit['state']: - del audit['state'] - - return self._create_request('audits', audit) - - @base.handle_errors - def delete_audit(self, audit_uuid): - """Deletes an audit having the specified UUID - - :param audit_uuid: The unique identifier of the audit - :return: A tuple with the server response and the response body - """ - - return self._delete_request('audits', audit_uuid) - - @base.handle_errors - def update_audit(self, audit_uuid, patch): - """Update the specified audit. - - :param audit_uuid: The unique identifier of the audit - :param patch: List of dicts representing json patches. - :return: Tuple with the server response and the updated audit - """ - - return self._patch_request('audits', audit_uuid, patch) - - # ### ACTION PLANS ### # - - @base.handle_errors - def list_action_plans(self, **kwargs): - """List all existing action plan""" - return self._list_request('action_plans', **kwargs) - - @base.handle_errors - def list_action_plans_detail(self, **kwargs): - """Lists details of all existing action plan""" - return self._list_request('/action_plans/detail', **kwargs) - - @base.handle_errors - def show_action_plan(self, action_plan_uuid): - """Gets a specific action plan - - :param action_plan_uuid: Unique identifier of the action plan - :return: Serialized action plan as a dictionary - """ - return self._show_request('/action_plans', action_plan_uuid) - - @base.handle_errors - def delete_action_plan(self, action_plan_uuid): - """Deletes an action plan having the specified UUID - - :param action_plan_uuid: The unique identifier of the action_plan - :return: A tuple with the server response and the response body - """ - - return self._delete_request('/action_plans', action_plan_uuid) - - @base.handle_errors - def delete_action_plans_by_audit(self, audit_uuid): - """Deletes an action plan having the specified UUID - - :param audit_uuid: The unique identifier of the related Audit - """ - - action_plans = self.list_action_plans(audit_uuid=audit_uuid)[1] - - for action_plan in action_plans: - self.delete_action_plan(action_plan['uuid']) - - @base.handle_errors - def update_action_plan(self, action_plan_uuid, patch): - """Update the specified action plan - - :param action_plan_uuid: The unique identifier of the action_plan - :param patch: List of dicts representing json patches. - :return: Tuple with the server response and the updated action_plan - """ - - return self._patch_request('/action_plans', action_plan_uuid, patch) - - @base.handle_errors - def start_action_plan(self, action_plan_uuid): - """Start the specified action plan - - :param action_plan_uuid: The unique identifier of the action_plan - :return: Tuple with the server response and the updated action_plan - """ - - return self._patch_request( - '/action_plans', action_plan_uuid, - [{'path': '/state', 'op': 'replace', 'value': 'PENDING'}]) - - # ### GOALS ### # - - @base.handle_errors - def list_goals(self, **kwargs): - """List all existing goals""" - return self._list_request('/goals', **kwargs) - - @base.handle_errors - def list_goals_detail(self, **kwargs): - """Lists details of all existing goals""" - return self._list_request('/goals/detail', **kwargs) - - @base.handle_errors - def show_goal(self, goal): - """Gets a specific goal - - :param goal: UUID or Name of the goal - :return: Serialized goal as a dictionary - """ - return self._show_request('/goals', goal) - - # ### ACTIONS ### # - - @base.handle_errors - def list_actions(self, **kwargs): - """List all existing actions""" - return self._list_request('/actions', **kwargs) - - @base.handle_errors - def list_actions_detail(self, **kwargs): - """Lists details of all existing actions""" - return self._list_request('/actions/detail', **kwargs) - - @base.handle_errors - def show_action(self, action_uuid): - """Gets a specific action - - :param action_uuid: Unique identifier of the action - :return: Serialized action as a dictionary - """ - return self._show_request('/actions', action_uuid) - - # ### STRATEGIES ### # - - @base.handle_errors - def list_strategies(self, **kwargs): - """List all existing strategies""" - return self._list_request('/strategies', **kwargs) - - @base.handle_errors - def list_strategies_detail(self, **kwargs): - """Lists details of all existing strategies""" - return self._list_request('/strategies/detail', **kwargs) - - @base.handle_errors - def show_strategy(self, strategy): - """Gets a specific strategy - - :param strategy_id: Name of the strategy - :return: Serialized strategy as a dictionary - """ - return self._show_request('/strategies', strategy) - - # ### SCORING ENGINE ### # - - @base.handle_errors - def list_scoring_engines(self, **kwargs): - """List all existing scoring_engines""" - return self._list_request('/scoring_engines', **kwargs) - - @base.handle_errors - def list_scoring_engines_detail(self, **kwargs): - """Lists details of all existing scoring_engines""" - return self._list_request('/scoring_engines/detail', **kwargs) - - @base.handle_errors - def show_scoring_engine(self, scoring_engine): - """Gets a specific scoring_engine - - :param scoring_engine: UUID or Name of the scoring_engine - :return: Serialized scoring_engine as a dictionary - """ - return self._show_request('/scoring_engines', scoring_engine) - - # ### SERVICES ### # - - @base.handle_errors - def list_services(self, **kwargs): - """List all existing services""" - return self._list_request('/services', **kwargs) - - @base.handle_errors - def list_services_detail(self, **kwargs): - """Lists details of all existing services""" - return self._list_request('/services/detail', **kwargs) - - @base.handle_errors - def show_service(self, service): - """Gets a specific service - - :param service: Name of the strategy - :return: Serialized strategy as a dictionary - """ - return self._show_request('/services', service) diff --git a/watcher_tempest_plugin/tests/__init__.py b/watcher_tempest_plugin/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/tests/api/__init__.py b/watcher_tempest_plugin/tests/api/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/tests/api/admin/__init__.py b/watcher_tempest_plugin/tests/api/admin/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/tests/api/admin/base.py b/watcher_tempest_plugin/tests/api/admin/base.py deleted file mode 100644 index 63cdb8398..000000000 --- a/watcher_tempest_plugin/tests/api/admin/base.py +++ /dev/null @@ -1,259 +0,0 @@ -# -*- 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 functools - -from tempest.lib.common.utils import data_utils -from tempest.lib.common.utils import test_utils -from tempest import test - -from watcher_tempest_plugin import infra_optim_clients as clients - - -class BaseInfraOptimTest(test.BaseTestCase): - """Base class for Infrastructure Optimization API tests.""" - - # States where the object is waiting for some event to perform a transition - IDLE_STATES = ('RECOMMENDED', - 'FAILED', - 'SUCCEEDED', - 'CANCELLED', - 'SUSPENDED') - # States where the object can only be DELETED (end of its life-cycle) - FINISHED_STATES = ('FAILED', - 'SUCCEEDED', - 'CANCELLED', - 'SUPERSEDED') - - @classmethod - def setup_credentials(cls): - super(BaseInfraOptimTest, cls).setup_credentials() - cls.mgr = clients.AdminManager() - - @classmethod - def setup_clients(cls): - super(BaseInfraOptimTest, cls).setup_clients() - cls.client = cls.mgr.io_client - - @classmethod - def resource_setup(cls): - super(BaseInfraOptimTest, cls).resource_setup() - - # Set of all created audit templates UUIDs - cls.created_audit_templates = set() - # Set of all created audit UUIDs - cls.created_audits = set() - # Set of all created audit UUIDs. We use it to build the list of - # action plans to delete (including potential orphan one(s)) - cls.created_action_plans_audit_uuids = set() - - @classmethod - def resource_cleanup(cls): - """Ensure that all created objects get destroyed.""" - try: - action_plans_to_be_deleted = set() - # Phase 1: Make sure all objects are in an idle state - for audit_uuid in cls.created_audits: - test_utils.call_until_true( - func=functools.partial( - cls.is_audit_idle, audit_uuid), - duration=30, - sleep_for=.5 - ) - - for audit_uuid in cls.created_action_plans_audit_uuids: - _, action_plans = cls.client.list_action_plans( - audit_uuid=audit_uuid) - action_plans_to_be_deleted.update( - ap['uuid'] for ap in action_plans['action_plans']) - - for action_plan in action_plans['action_plans']: - try: - test_utils.call_until_true( - func=functools.partial( - cls.is_action_plan_idle, action_plan['uuid']), - duration=30, - sleep_for=.5 - ) - except Exception: - action_plans_to_be_deleted.remove( - action_plan['uuid']) - - # Phase 2: Delete them all - for action_plan_uuid in action_plans_to_be_deleted: - cls.delete_action_plan(action_plan_uuid) - - for audit_uuid in cls.created_audits.copy(): - cls.delete_audit(audit_uuid) - - for audit_template_uuid in cls.created_audit_templates.copy(): - cls.delete_audit_template(audit_template_uuid) - - finally: - super(BaseInfraOptimTest, cls).resource_cleanup() - - def validate_self_link(self, resource, uuid, link): - """Check whether the given self link formatted correctly.""" - expected_link = "{base}/{pref}/{res}/{uuid}".format( - base=self.client.base_url, - pref=self.client.URI_PREFIX, - res=resource, - uuid=uuid - ) - self.assertEqual(expected_link, link) - - def assert_expected(self, expected, actual, - keys=('created_at', 'updated_at', 'deleted_at')): - # Check if not expected keys/values exists in actual response body - for key, value in expected.items(): - if key not in keys: - self.assertIn(key, actual) - self.assertEqual(value, actual[key]) - - # ### AUDIT TEMPLATES ### # - - @classmethod - def create_audit_template(cls, goal, name=None, description=None, - strategy=None, scope=None): - """Wrapper utility for creating a test audit template - - :param goal: Goal UUID or name related to the audit template. - :param name: The name of the audit template. Default: My Audit Template - :param description: The description of the audit template. - :param strategy: Strategy UUID or name related to the audit template. - :param scope: Scope that will be applied on all derived audits. - :return: A tuple with The HTTP response and its body - """ - description = description or data_utils.rand_name( - 'test-audit_template') - resp, body = cls.client.create_audit_template( - name=name, description=description, - goal=goal, strategy=strategy, scope=scope) - - cls.created_audit_templates.add(body['uuid']) - - return resp, body - - @classmethod - def delete_audit_template(cls, uuid): - """Deletes a audit_template having the specified UUID - - :param uuid: The unique identifier of the audit template - :return: Server response - """ - resp, _ = cls.client.delete_audit_template(uuid) - - if uuid in cls.created_audit_templates: - cls.created_audit_templates.remove(uuid) - - return resp - - # ### AUDITS ### # - - @classmethod - def create_audit(cls, audit_template_uuid, audit_type='ONESHOT', - state=None, interval=None, parameters=None): - """Wrapper utility for creating a test audit - - :param audit_template_uuid: Audit Template UUID this audit will use - :param audit_type: Audit type (either ONESHOT or CONTINUOUS) - :param state: Audit state (str) - :param interval: Audit interval in seconds or cron syntax (str) - :param parameters: list of execution parameters - :return: A tuple with The HTTP response and its body - """ - resp, body = cls.client.create_audit( - audit_template_uuid=audit_template_uuid, audit_type=audit_type, - state=state, interval=interval, parameters=parameters) - - cls.created_audits.add(body['uuid']) - cls.created_action_plans_audit_uuids.add(body['uuid']) - - return resp, body - - @classmethod - def delete_audit(cls, audit_uuid): - """Deletes an audit having the specified UUID - - :param audit_uuid: The unique identifier of the audit. - :return: the HTTP response - """ - resp, _ = cls.client.delete_audit(audit_uuid) - - if audit_uuid in cls.created_audits: - cls.created_audits.remove(audit_uuid) - - return resp - - @classmethod - def has_audit_succeeded(cls, audit_uuid): - _, audit = cls.client.show_audit(audit_uuid) - return audit.get('state') == 'SUCCEEDED' - - @classmethod - def has_audit_finished(cls, audit_uuid): - _, audit = cls.client.show_audit(audit_uuid) - return audit.get('state') in cls.FINISHED_STATES - - @classmethod - def is_audit_idle(cls, audit_uuid): - _, audit = cls.client.show_audit(audit_uuid) - return audit.get('state') in cls.IDLE_STATES - - # ### ACTION PLANS ### # - - @classmethod - def create_action_plan(cls, audit_template_uuid, **audit_kwargs): - """Wrapper utility for creating a test action plan - - :param audit_template_uuid: Audit template UUID to use - :param audit_kwargs: Dict of audit properties to set - :return: The action plan as dict - """ - _, audit = cls.create_audit(audit_template_uuid, **audit_kwargs) - audit_uuid = audit['uuid'] - - assert test_utils.call_until_true( - func=functools.partial(cls.has_audit_finished, audit_uuid), - duration=30, - sleep_for=.5 - ) - - _, action_plans = cls.client.list_action_plans(audit_uuid=audit_uuid) - if len(action_plans['action_plans']) == 0: - return - - return action_plans['action_plans'][0] - - @classmethod - def delete_action_plan(cls, action_plan_uuid): - """Deletes an action plan having the specified UUID - - :param action_plan_uuid: The unique identifier of the action plan. - :return: the HTTP response - """ - resp, _ = cls.client.delete_action_plan(action_plan_uuid) - - if action_plan_uuid in cls.created_action_plans_audit_uuids: - cls.created_action_plans_audit_uuids.remove(action_plan_uuid) - - return resp - - @classmethod - def is_action_plan_idle(cls, action_plan_uuid): - """This guard makes sure your action plan is not running""" - _, action_plan = cls.client.show_action_plan(action_plan_uuid) - return action_plan.get('state') in cls.IDLE_STATES diff --git a/watcher_tempest_plugin/tests/api/admin/test_action.py b/watcher_tempest_plugin/tests/api/admin/test_action.py deleted file mode 100644 index 3fa2d9441..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_action.py +++ /dev/null @@ -1,110 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -import collections -import functools - -from tempest.lib.common.utils import test_utils -from tempest.lib import decorators - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestShowListAction(base.BaseInfraOptimTest): - """Tests for actions""" - - @classmethod - def resource_setup(cls): - super(TestShowListAction, cls).resource_setup() - _, cls.goal = cls.client.show_goal("DUMMY") - _, cls.audit_template = cls.create_audit_template(cls.goal['uuid']) - _, cls.audit = cls.create_audit(cls.audit_template['uuid']) - - assert test_utils.call_until_true( - func=functools.partial(cls.has_audit_finished, cls.audit['uuid']), - duration=30, - sleep_for=.5 - ) - _, action_plans = cls.client.list_action_plans( - audit_uuid=cls.audit['uuid']) - cls.action_plan = action_plans['action_plans'][0] - - @decorators.attr(type='smoke') - def test_show_one_action(self): - _, body = self.client.list_actions( - action_plan_uuid=self.action_plan["uuid"]) - actions = body['actions'] - - _, action = self.client.show_action(actions[0]["uuid"]) - - self.assertEqual(self.action_plan["uuid"], action['action_plan_uuid']) - self.assertEqual("PENDING", action['state']) - - @decorators.attr(type='smoke') - def test_show_action_with_links(self): - _, body = self.client.list_actions( - action_plan_uuid=self.action_plan["uuid"]) - actions = body['actions'] - - _, action = self.client.show_action(actions[0]["uuid"]) - - self.assertIn('links', action.keys()) - self.assertEqual(2, len(action['links'])) - self.assertIn(action['uuid'], action['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_actions(self): - _, body = self.client.list_actions() - - # Verify self links. - for action in body['actions']: - self.validate_self_link('actions', action['uuid'], - action['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_actions_by_action_plan(self): - _, body = self.client.list_actions( - action_plan_uuid=self.action_plan["uuid"]) - - for item in body['actions']: - self.assertEqual(self.action_plan["uuid"], - item['action_plan_uuid']) - - action_counter = collections.Counter( - act['action_type'] for act in body['actions']) - - # A dummy strategy generates 2 "nop" actions and 1 "sleep" action - self.assertEqual(3, len(body['actions'])) - self.assertEqual(2, action_counter.get("nop")) - self.assertEqual(1, action_counter.get("sleep")) - - @decorators.attr(type="smoke") - def test_list_actions_by_audit(self): - _, body = self.client.list_actions(audit_uuid=self.audit["uuid"]) - - for item in body['actions']: - self.assertEqual(self.action_plan["uuid"], - item['action_plan_uuid']) - - action_counter = collections.Counter( - act['action_type'] for act in body['actions']) - - # A dummy strategy generates 2 "nop" actions and 1 "sleep" action - self.assertEqual(3, len(body['actions'])) - self.assertEqual(2, action_counter.get("nop")) - self.assertEqual(1, action_counter.get("sleep")) diff --git a/watcher_tempest_plugin/tests/api/admin/test_action_plan.py b/watcher_tempest_plugin/tests/api/admin/test_action_plan.py deleted file mode 100644 index 442ac420c..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_action_plan.py +++ /dev/null @@ -1,140 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -import functools - -from tempest.lib.common.utils import test_utils -from tempest.lib import decorators -from tempest.lib import exceptions - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest): - """Tests for action plans""" - - @decorators.attr(type='smoke') - def test_create_action_plan(self): - _, goal = self.client.show_goal("dummy") - _, audit_template = self.create_audit_template(goal['uuid']) - _, audit = self.create_audit(audit_template['uuid']) - - self.assertTrue(test_utils.call_until_true( - func=functools.partial(self.has_audit_finished, audit['uuid']), - duration=30, - sleep_for=.5 - )) - _, action_plans = self.client.list_action_plans( - audit_uuid=audit['uuid']) - action_plan = action_plans['action_plans'][0] - - _, action_plan = self.client.show_action_plan(action_plan['uuid']) - - self.assertEqual(audit['uuid'], action_plan['audit_uuid']) - self.assertEqual('RECOMMENDED', action_plan['state']) - - @decorators.attr(type='smoke') - def test_delete_action_plan(self): - _, goal = self.client.show_goal("dummy") - _, audit_template = self.create_audit_template(goal['uuid']) - _, audit = self.create_audit(audit_template['uuid']) - - self.assertTrue(test_utils.call_until_true( - func=functools.partial(self.has_audit_finished, audit['uuid']), - duration=30, - sleep_for=.5 - )) - _, action_plans = self.client.list_action_plans( - audit_uuid=audit['uuid']) - action_plan = action_plans['action_plans'][0] - - _, action_plan = self.client.show_action_plan(action_plan['uuid']) - - self.client.delete_action_plan(action_plan['uuid']) - - self.assertRaises(exceptions.NotFound, self.client.show_action_plan, - action_plan['uuid']) - - -class TestShowListActionPlan(base.BaseInfraOptimTest): - """Tests for action_plan.""" - - @classmethod - def resource_setup(cls): - super(TestShowListActionPlan, cls).resource_setup() - _, cls.goal = cls.client.show_goal("dummy") - _, cls.audit_template = cls.create_audit_template(cls.goal['uuid']) - _, cls.audit = cls.create_audit(cls.audit_template['uuid']) - - assert test_utils.call_until_true( - func=functools.partial(cls.has_audit_finished, cls.audit['uuid']), - duration=30, - sleep_for=.5 - ) - _, action_plans = cls.client.list_action_plans( - audit_uuid=cls.audit['uuid']) - if len(action_plans['action_plans']) > 0: - cls.action_plan = action_plans['action_plans'][0] - - @decorators.attr(type='smoke') - def test_show_action_plan(self): - _, action_plan = self.client.show_action_plan( - self.action_plan['uuid']) - - self.assert_expected(self.action_plan, action_plan) - - @decorators.attr(type='smoke') - def test_show_action_plan_detail(self): - _, action_plans = self.client.list_action_plans_detail( - audit_uuid=self.audit['uuid']) - - action_plan = action_plans['action_plans'][0] - - self.assert_expected(self.action_plan, action_plan) - - @decorators.attr(type='smoke') - def test_show_action_plan_with_links(self): - _, action_plan = self.client.show_action_plan( - self.action_plan['uuid']) - self.assertIn('links', action_plan.keys()) - self.assertEqual(2, len(action_plan['links'])) - self.assertIn(action_plan['uuid'], - action_plan['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_action_plans(self): - _, body = self.client.list_action_plans() - self.assertIn(self.action_plan['uuid'], - [i['uuid'] for i in body['action_plans']]) - # Verify self links. - for action_plan in body['action_plans']: - self.validate_self_link('action_plans', action_plan['uuid'], - action_plan['links'][0]['href']) - - @decorators.attr(type='smoke') - def test_list_with_limit(self): - # We create 3 extra audits to exceed the limit we fix - for _ in range(3): - self.create_action_plan(self.audit_template['uuid']) - - _, body = self.client.list_action_plans(limit=3) - - next_marker = body['action_plans'][-1]['uuid'] - - self.assertEqual(3, len(body['action_plans'])) - self.assertIn(next_marker, body['next']) diff --git a/watcher_tempest_plugin/tests/api/admin/test_api_discovery.py b/watcher_tempest_plugin/tests/api/admin/test_api_discovery.py deleted file mode 100644 index f30cb4b6e..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_api_discovery.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- 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. - -from tempest.lib import decorators - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestApiDiscovery(base.BaseInfraOptimTest): - """Tests for API discovery features.""" - - @decorators.attr(type='smoke') - def test_api_versions(self): - _, descr = self.client.get_api_description() - expected_versions = ('v1',) - versions = [version['id'] for version in descr['versions']] - - for v in expected_versions: - self.assertIn(v, versions) - - @decorators.attr(type='smoke') - def test_default_version(self): - _, descr = self.client.get_api_description() - default_version = descr['default_version'] - self.assertEqual('v1', default_version['id']) - - @decorators.attr(type='smoke') - def test_version_1_resources(self): - _, descr = self.client.get_version_description(version='v1') - expected_resources = ('audit_templates', 'audits', 'action_plans', - 'actions', 'links', 'media_types') - - for res in expected_resources: - self.assertIn(res, descr) diff --git a/watcher_tempest_plugin/tests/api/admin/test_audit.py b/watcher_tempest_plugin/tests/api/admin/test_audit.py deleted file mode 100644 index 13a187ea3..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_audit.py +++ /dev/null @@ -1,221 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -import functools - -from tempest.lib.common.utils import test_utils -from tempest.lib import decorators -from tempest.lib import exceptions - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): - """Tests for audit.""" - - audit_states = ['ONGOING', 'SUCCEEDED', 'FAILED', - 'CANCELLED', 'DELETED', 'PENDING', 'SUSPENDED'] - - def assert_expected(self, expected, actual, - keys=('created_at', 'updated_at', - 'deleted_at', 'state')): - super(TestCreateUpdateDeleteAudit, self).assert_expected( - expected, actual, keys) - - @decorators.attr(type='smoke') - def test_create_audit_oneshot(self): - _, goal = self.client.show_goal("dummy") - _, audit_template = self.create_audit_template(goal['uuid']) - - audit_params = dict( - audit_template_uuid=audit_template['uuid'], - audit_type='ONESHOT', - ) - - _, body = self.create_audit(**audit_params) - audit_params.pop('audit_template_uuid') - audit_params['goal_uuid'] = goal['uuid'] - self.assert_expected(audit_params, body) - - _, audit = self.client.show_audit(body['uuid']) - self.assert_expected(audit, body) - - @decorators.attr(type='smoke') - def test_create_audit_continuous(self): - _, goal = self.client.show_goal("dummy") - _, audit_template = self.create_audit_template(goal['uuid']) - - audit_params = dict( - audit_template_uuid=audit_template['uuid'], - audit_type='CONTINUOUS', - interval='7200', - ) - - _, body = self.create_audit(**audit_params) - audit_params.pop('audit_template_uuid') - audit_params['goal_uuid'] = goal['uuid'] - self.assert_expected(audit_params, body) - - _, audit = self.client.show_audit(body['uuid']) - self.assert_expected(audit, body) - - @decorators.attr(type='smoke') - def test_create_audit_with_wrong_audit_template(self): - audit_params = dict( - audit_template_uuid='INVALID', - audit_type='ONESHOT', - ) - - self.assertRaises( - exceptions.BadRequest, self.create_audit, **audit_params) - - @decorators.attr(type='smoke') - def test_create_audit_with_invalid_state(self): - _, goal = self.client.show_goal("dummy") - _, audit_template = self.create_audit_template(goal['uuid']) - - audit_params = dict( - audit_template_uuid=audit_template['uuid'], - state='INVALID', - ) - - self.assertRaises( - exceptions.BadRequest, self.create_audit, **audit_params) - - @decorators.attr(type='smoke') - def test_create_audit_with_no_state(self): - _, goal = self.client.show_goal("dummy") - _, audit_template = self.create_audit_template(goal['uuid']) - - audit_params = dict( - audit_template_uuid=audit_template['uuid'], - state='', - ) - - _, body = self.create_audit(**audit_params) - audit_params.pop('audit_template_uuid') - audit_params['goal_uuid'] = goal['uuid'] - self.assert_expected(audit_params, body) - - _, audit = self.client.show_audit(body['uuid']) - - initial_audit_state = audit.pop('state') - self.assertIn(initial_audit_state, self.audit_states) - - self.assert_expected(audit, body) - - @decorators.attr(type='smoke') - def test_delete_audit(self): - _, goal = self.client.show_goal("dummy") - _, audit_template = self.create_audit_template(goal['uuid']) - _, body = self.create_audit(audit_template['uuid']) - audit_uuid = body['uuid'] - - test_utils.call_until_true( - func=functools.partial( - self.is_audit_idle, audit_uuid), - duration=10, - sleep_for=.5 - ) - - def is_audit_deleted(uuid): - try: - return not bool(self.client.show_audit(uuid)) - except exceptions.NotFound: - return True - - self.delete_audit(audit_uuid) - - test_utils.call_until_true( - func=functools.partial(is_audit_deleted, audit_uuid), - duration=5, - sleep_for=1 - ) - - self.assertTrue(is_audit_deleted(audit_uuid)) - - -class TestShowListAudit(base.BaseInfraOptimTest): - """Tests for audit.""" - - audit_states = ['ONGOING', 'SUCCEEDED', 'FAILED', - 'CANCELLED', 'DELETED', 'PENDING', 'SUSPENDED'] - - @classmethod - def resource_setup(cls): - super(TestShowListAudit, cls).resource_setup() - _, cls.goal = cls.client.show_goal("dummy") - _, cls.audit_template = cls.create_audit_template(cls.goal['uuid']) - _, cls.audit = cls.create_audit(cls.audit_template['uuid']) - - def assert_expected(self, expected, actual, - keys=('created_at', 'updated_at', - 'deleted_at', 'state')): - super(TestShowListAudit, self).assert_expected( - expected, actual, keys) - - @decorators.attr(type='smoke') - def test_show_audit(self): - _, audit = self.client.show_audit( - self.audit['uuid']) - - initial_audit = self.audit.copy() - del initial_audit['state'] - audit_state = audit['state'] - actual_audit = audit.copy() - del actual_audit['state'] - - self.assertIn(audit_state, self.audit_states) - self.assert_expected(initial_audit, actual_audit) - - @decorators.attr(type='smoke') - def test_show_audit_with_links(self): - _, audit = self.client.show_audit( - self.audit['uuid']) - self.assertIn('links', audit.keys()) - self.assertEqual(2, len(audit['links'])) - self.assertIn(audit['uuid'], - audit['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_audits(self): - _, body = self.client.list_audits() - self.assertIn(self.audit['uuid'], - [i['uuid'] for i in body['audits']]) - # Verify self links. - for audit in body['audits']: - self.validate_self_link('audits', audit['uuid'], - audit['links'][0]['href']) - - @decorators.attr(type='smoke') - def test_list_with_limit(self): - # We create 3 extra audits to exceed the limit we fix - for _ in range(3): - self.create_audit(self.audit_template['uuid']) - - _, body = self.client.list_audits(limit=3) - - next_marker = body['audits'][-1]['uuid'] - self.assertEqual(3, len(body['audits'])) - self.assertIn(next_marker, body['next']) - - @decorators.attr(type='smoke') - def test_list_audits_related_to_given_audit_template(self): - _, body = self.client.list_audits( - goal=self.goal['uuid']) - self.assertIn(self.audit['uuid'], [n['uuid'] for n in body['audits']]) diff --git a/watcher_tempest_plugin/tests/api/admin/test_audit_template.py b/watcher_tempest_plugin/tests/api/admin/test_audit_template.py deleted file mode 100644 index 75ac80a09..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_audit_template.py +++ /dev/null @@ -1,226 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -from oslo_utils import uuidutils - -from tempest.lib import decorators -from tempest.lib import exceptions - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestCreateDeleteAuditTemplate(base.BaseInfraOptimTest): - """Tests on audit templates""" - - @decorators.attr(type='smoke') - def test_create_audit_template(self): - goal_name = "dummy" - _, goal = self.client.show_goal(goal_name) - - params = { - 'name': 'my at name %s' % uuidutils.generate_uuid(), - 'description': 'my at description', - 'goal': goal['uuid']} - expected_data = { - 'name': params['name'], - 'description': params['description'], - 'goal_uuid': params['goal'], - 'goal_name': goal_name, - 'strategy_uuid': None, - 'strategy_name': None} - - _, body = self.create_audit_template(**params) - self.assert_expected(expected_data, body) - - _, audit_template = self.client.show_audit_template(body['uuid']) - self.assert_expected(audit_template, body) - - @decorators.attr(type='smoke') - def test_create_audit_template_unicode_description(self): - goal_name = "dummy" - _, goal = self.client.show_goal(goal_name) - # Use a unicode string for testing: - params = { - 'name': 'my at name %s' % uuidutils.generate_uuid(), - 'description': 'my àt déscrïptïôn', - 'goal': goal['uuid']} - - expected_data = { - 'name': params['name'], - 'description': params['description'], - 'goal_uuid': params['goal'], - 'goal_name': goal_name, - 'strategy_uuid': None, - 'strategy_name': None} - - _, body = self.create_audit_template(**params) - self.assert_expected(expected_data, body) - - _, audit_template = self.client.show_audit_template(body['uuid']) - self.assert_expected(audit_template, body) - - @decorators.attr(type='smoke') - def test_delete_audit_template(self): - _, goal = self.client.show_goal("dummy") - _, body = self.create_audit_template(goal=goal['uuid']) - audit_uuid = body['uuid'] - - self.delete_audit_template(audit_uuid) - - self.assertRaises(exceptions.NotFound, self.client.show_audit_template, - audit_uuid) - - -class TestAuditTemplate(base.BaseInfraOptimTest): - """Tests for audit_template.""" - - @classmethod - def resource_setup(cls): - super(TestAuditTemplate, cls).resource_setup() - _, cls.goal = cls.client.show_goal("dummy") - _, cls.strategy = cls.client.show_strategy("dummy") - _, cls.audit_template = cls.create_audit_template( - goal=cls.goal['uuid'], strategy=cls.strategy['uuid']) - - @decorators.attr(type='smoke') - def test_show_audit_template(self): - _, audit_template = self.client.show_audit_template( - self.audit_template['uuid']) - - self.assert_expected(self.audit_template, audit_template) - - @decorators.attr(type='smoke') - def test_filter_audit_template_by_goal_uuid(self): - _, audit_templates = self.client.list_audit_templates( - goal=self.audit_template['goal_uuid']) - - audit_template_uuids = [ - at["uuid"] for at in audit_templates['audit_templates']] - self.assertIn(self.audit_template['uuid'], audit_template_uuids) - - @decorators.attr(type='smoke') - def test_filter_audit_template_by_strategy_uuid(self): - _, audit_templates = self.client.list_audit_templates( - strategy=self.audit_template['strategy_uuid']) - - audit_template_uuids = [ - at["uuid"] for at in audit_templates['audit_templates']] - self.assertIn(self.audit_template['uuid'], audit_template_uuids) - - @decorators.attr(type='smoke') - def test_show_audit_template_with_links(self): - _, audit_template = self.client.show_audit_template( - self.audit_template['uuid']) - self.assertIn('links', audit_template.keys()) - self.assertEqual(2, len(audit_template['links'])) - self.assertIn(audit_template['uuid'], - audit_template['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_audit_templates(self): - _, body = self.client.list_audit_templates() - self.assertIn(self.audit_template['uuid'], - [i['uuid'] for i in body['audit_templates']]) - # Verify self links. - for audit_template in body['audit_templates']: - self.validate_self_link('audit_templates', audit_template['uuid'], - audit_template['links'][0]['href']) - - @decorators.attr(type='smoke') - def test_list_with_limit(self): - # We create 3 extra audit templates to exceed the limit we fix - for _ in range(3): - self.create_audit_template(self.goal['uuid']) - - _, body = self.client.list_audit_templates(limit=3) - - next_marker = body['audit_templates'][-1]['uuid'] - self.assertEqual(3, len(body['audit_templates'])) - self.assertIn(next_marker, body['next']) - - @decorators.attr(type='smoke') - def test_update_audit_template_replace(self): - _, new_goal = self.client.show_goal("server_consolidation") - _, new_strategy = self.client.show_strategy("basic") - - params = {'name': 'my at name %s' % uuidutils.generate_uuid(), - 'description': 'my at description', - 'goal': self.goal['uuid']} - - _, body = self.create_audit_template(**params) - - new_name = 'my at new name %s' % uuidutils.generate_uuid() - new_description = 'my new at description' - - patch = [{'path': '/name', - 'op': 'replace', - 'value': new_name}, - {'path': '/description', - 'op': 'replace', - 'value': new_description}, - {'path': '/goal', - 'op': 'replace', - 'value': new_goal['uuid']}, - {'path': '/strategy', - 'op': 'replace', - 'value': new_strategy['uuid']}] - - self.client.update_audit_template(body['uuid'], patch) - - _, body = self.client.show_audit_template(body['uuid']) - self.assertEqual(new_name, body['name']) - self.assertEqual(new_description, body['description']) - self.assertEqual(new_goal['uuid'], body['goal_uuid']) - self.assertEqual(new_strategy['uuid'], body['strategy_uuid']) - - @decorators.attr(type='smoke') - def test_update_audit_template_remove(self): - description = 'my at description' - name = 'my at name %s' % uuidutils.generate_uuid() - params = {'name': name, - 'description': description, - 'goal': self.goal['uuid']} - - _, audit_template = self.create_audit_template(**params) - - # Removing the description - self.client.update_audit_template( - audit_template['uuid'], - [{'path': '/description', 'op': 'remove'}]) - - _, body = self.client.show_audit_template(audit_template['uuid']) - self.assertIsNone(body.get('description')) - - # Assert nothing else was changed - self.assertEqual(name, body['name']) - self.assertIsNone(body['description']) - self.assertEqual(self.goal['uuid'], body['goal_uuid']) - - @decorators.attr(type='smoke') - def test_update_audit_template_add(self): - params = {'name': 'my at name %s' % uuidutils.generate_uuid(), - 'goal': self.goal['uuid']} - - _, body = self.create_audit_template(**params) - - patch = [{'path': '/description', 'op': 'add', 'value': 'description'}] - - self.client.update_audit_template(body['uuid'], patch) - - _, body = self.client.show_audit_template(body['uuid']) - self.assertEqual('description', body['description']) diff --git a/watcher_tempest_plugin/tests/api/admin/test_goal.py b/watcher_tempest_plugin/tests/api/admin/test_goal.py deleted file mode 100644 index 2cf228ed9..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_goal.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -from tempest.lib import decorators - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestShowListGoal(base.BaseInfraOptimTest): - """Tests for goals""" - - DUMMY_GOAL = "dummy" - - @classmethod - def resource_setup(cls): - super(TestShowListGoal, cls).resource_setup() - - def assert_expected(self, expected, actual, - keys=('created_at', 'updated_at', 'deleted_at')): - super(TestShowListGoal, self).assert_expected( - expected, actual, keys) - - @decorators.attr(type='smoke') - def test_show_goal(self): - _, goal = self.client.show_goal(self.DUMMY_GOAL) - - self.assertEqual(self.DUMMY_GOAL, goal['name']) - expected_fields = { - 'created_at', 'deleted_at', 'display_name', - 'efficacy_specification', 'links', 'name', - 'updated_at', 'uuid'} - self.assertEqual(expected_fields, set(goal.keys())) - - @decorators.attr(type='smoke') - def test_show_goal_with_links(self): - _, goal = self.client.show_goal(self.DUMMY_GOAL) - self.assertIn('links', goal.keys()) - self.assertEqual(2, len(goal['links'])) - self.assertIn(goal['uuid'], - goal['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_goals(self): - _, body = self.client.list_goals() - self.assertIn(self.DUMMY_GOAL, - [i['name'] for i in body['goals']]) - - # Verify self links. - for goal in body['goals']: - self.validate_self_link('goals', goal['uuid'], - goal['links'][0]['href']) diff --git a/watcher_tempest_plugin/tests/api/admin/test_scoring_engine.py b/watcher_tempest_plugin/tests/api/admin/test_scoring_engine.py deleted file mode 100644 index 466fe4129..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_scoring_engine.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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. - -from __future__ import unicode_literals - -from tempest.lib import decorators - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestShowListScoringEngine(base.BaseInfraOptimTest): - """Tests for scoring engines""" - - DUMMY_SCORING_ENGINE = "dummy_scorer" - - @classmethod - def resource_setup(cls): - super(TestShowListScoringEngine, cls).resource_setup() - - def assert_expected(self, expected, actual, - keys=('created_at', 'updated_at', 'deleted_at')): - super(TestShowListScoringEngine, self).assert_expected( - expected, actual, keys) - - @decorators.attr(type='smoke') - def test_show_scoring_engine(self): - _, scoring_engine = self.client.show_scoring_engine( - self.DUMMY_SCORING_ENGINE) - - self.assertEqual(self.DUMMY_SCORING_ENGINE, scoring_engine['name']) - - expected_fields = {'metainfo', 'description', 'name', 'uuid', 'links'} - self.assertEqual(expected_fields, set(scoring_engine.keys())) - - @decorators.attr(type='smoke') - def test_show_scoring_engine_with_links(self): - _, scoring_engine = self.client.show_scoring_engine( - self.DUMMY_SCORING_ENGINE) - self.assertIn('links', scoring_engine.keys()) - self.assertEqual(2, len(scoring_engine['links'])) - self.assertIn(scoring_engine['uuid'], - scoring_engine['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_scoring_engines(self): - _, body = self.client.list_scoring_engines() - self.assertIn(self.DUMMY_SCORING_ENGINE, - [i['name'] for i in body['scoring_engines']]) - - # Verify self links. - for scoring_engine in body['scoring_engines']: - self.validate_self_link('scoring_engines', scoring_engine['uuid'], - scoring_engine['links'][0]['href']) diff --git a/watcher_tempest_plugin/tests/api/admin/test_service.py b/watcher_tempest_plugin/tests/api/admin/test_service.py deleted file mode 100644 index 948d8b1a2..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_service.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- encoding: utf-8 -*- -# Copyright (c) 2016 Servionica -# -# 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 __future__ import unicode_literals - -from tempest.lib import decorators - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestShowListService(base.BaseInfraOptimTest): - """Tests for services""" - - DECISION_ENGINE = "watcher-decision-engine" - APPLIER = "watcher-applier" - - @classmethod - def resource_setup(cls): - super(TestShowListService, cls).resource_setup() - - def assert_expected(self, expected, actual, - keys=('created_at', 'updated_at', 'deleted_at')): - super(TestShowListService, self).assert_expected( - expected, actual, keys) - - @decorators.attr(type='smoke') - def test_show_service(self): - _, body = self.client.list_services() - self.assertIn('services', body) - services = body['services'] - self.assertIn(self.DECISION_ENGINE, - [i['name'] for i in body['services']]) - - service_id = filter(lambda x: self.DECISION_ENGINE == x['name'], - services)[0]['id'] - _, service = self.client.show_service(service_id) - - self.assertEqual(self.DECISION_ENGINE, service['name']) - self.assertIn("host", service.keys()) - self.assertIn("last_seen_up", service.keys()) - self.assertIn("status", service.keys()) - - @decorators.attr(type='smoke') - def test_show_service_with_links(self): - _, body = self.client.list_services() - self.assertIn('services', body) - services = body['services'] - self.assertIn(self.DECISION_ENGINE, - [i['name'] for i in body['services']]) - - service_id = filter(lambda x: self.DECISION_ENGINE == x['name'], - services)[0]['id'] - _, service = self.client.show_service(service_id) - - self.assertIn('links', service.keys()) - self.assertEqual(2, len(service['links'])) - self.assertIn(str(service['id']), - service['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_services(self): - _, body = self.client.list_services() - self.assertIn('services', body) - services = body['services'] - self.assertIn(self.DECISION_ENGINE, - [i['name'] for i in body['services']]) - - for service in services: - self.assertTrue( - all(val is not None for key, val in service.items() - if key in ['id', 'name', 'host', 'status', - 'last_seen_up'])) - - # Verify self links. - for service in body['services']: - self.validate_self_link('services', service['id'], - service['links'][0]['href']) diff --git a/watcher_tempest_plugin/tests/api/admin/test_strategy.py b/watcher_tempest_plugin/tests/api/admin/test_strategy.py deleted file mode 100644 index 73eefd7b6..000000000 --- a/watcher_tempest_plugin/tests/api/admin/test_strategy.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -from tempest.lib import decorators - -from watcher_tempest_plugin.tests.api.admin import base - - -class TestShowListStrategy(base.BaseInfraOptimTest): - """Tests for strategies""" - - DUMMY_STRATEGY = "dummy" - - @classmethod - def resource_setup(cls): - super(TestShowListStrategy, cls).resource_setup() - - def assert_expected(self, expected, actual, - keys=('created_at', 'updated_at', 'deleted_at')): - super(TestShowListStrategy, self).assert_expected( - expected, actual, keys) - - @decorators.attr(type='smoke') - def test_show_strategy(self): - _, strategy = self.client.show_strategy(self.DUMMY_STRATEGY) - - self.assertEqual(self.DUMMY_STRATEGY, strategy['name']) - self.assertIn("display_name", strategy.keys()) - - @decorators.attr(type='smoke') - def test_show_strategy_with_links(self): - _, strategy = self.client.show_strategy(self.DUMMY_STRATEGY) - self.assertIn('links', strategy.keys()) - self.assertEqual(2, len(strategy['links'])) - self.assertIn(strategy['uuid'], - strategy['links'][0]['href']) - - @decorators.attr(type="smoke") - def test_list_strategies(self): - _, body = self.client.list_strategies() - self.assertIn('strategies', body) - strategies = body['strategies'] - self.assertIn(self.DUMMY_STRATEGY, - [i['name'] for i in body['strategies']]) - - for strategy in strategies: - self.assertTrue( - all(val is not None for key, val in strategy.items() - if key in ['uuid', 'name', 'display_name', 'goal_uuid'])) - - # Verify self links. - for strategy in body['strategies']: - self.validate_self_link('strategies', strategy['uuid'], - strategy['links'][0]['href']) diff --git a/watcher_tempest_plugin/tests/scenario/__init__.py b/watcher_tempest_plugin/tests/scenario/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher_tempest_plugin/tests/scenario/base.py b/watcher_tempest_plugin/tests/scenario/base.py deleted file mode 100644 index 18688b031..000000000 --- a/watcher_tempest_plugin/tests/scenario/base.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*- 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. -# - -from __future__ import unicode_literals - -import time - -from oslo_log import log -from tempest import config -from tempest import exceptions -from tempest.lib.common.utils import data_utils -from tempest.lib.common.utils import test_utils - -from watcher_tempest_plugin import infra_optim_clients as clients -from watcher_tempest_plugin.tests.scenario import manager - -LOG = log.getLogger(__name__) -CONF = config.CONF - - -class BaseInfraOptimScenarioTest(manager.ScenarioTest): - """Base class for Infrastructure Optimization API tests.""" - - # States where the object is waiting for some event to perform a transition - IDLE_STATES = ('RECOMMENDED', 'FAILED', 'SUCCEEDED', 'CANCELLED') - # States where the object can only be DELETED (end of its life-cycle) - FINISHED_STATES = ('FAILED', 'SUCCEEDED', 'CANCELLED', 'SUPERSEDED') - - @classmethod - def setup_credentials(cls): - cls._check_network_config() - super(BaseInfraOptimScenarioTest, cls).setup_credentials() - cls.mgr = clients.AdminManager() - - @classmethod - def setup_clients(cls): - super(BaseInfraOptimScenarioTest, cls).setup_clients() - cls.client = cls.mgr.io_client - - @classmethod - def resource_setup(cls): - super(BaseInfraOptimScenarioTest, cls).resource_setup() - - @classmethod - def resource_cleanup(cls): - """Ensure that all created objects get destroyed.""" - super(BaseInfraOptimScenarioTest, cls).resource_cleanup() - - @classmethod - def wait_for(cls, condition, timeout=30): - start_time = time.time() - while time.time() - start_time < timeout: - if condition(): - break - time.sleep(.5) - - @classmethod - def _check_network_config(cls): - if not CONF.network.public_network_id: - msg = 'public network not defined.' - LOG.error(msg) - raise exceptions.InvalidConfiguration(msg) - - @classmethod - def _are_all_action_plans_finished(cls): - _, action_plans = cls.client.list_action_plans() - return all([ap['state'] in cls.FINISHED_STATES - for ap in action_plans['action_plans']]) - - def wait_for_all_action_plans_to_finish(self): - assert test_utils.call_until_true( - func=self._are_all_action_plans_finished, - duration=300, - sleep_for=5 - ) - - # ### AUDIT TEMPLATES ### # - - def create_audit_template(self, goal, name=None, description=None, - strategy=None): - """Wrapper utility for creating a test audit template - - :param goal: Goal UUID or name related to the audit template. - :param name: The name of the audit template. Default: My Audit Template - :param description: The description of the audit template. - :param strategy: Strategy UUID or name related to the audit template. - :return: A tuple with The HTTP response and its body - """ - description = description or data_utils.rand_name( - 'test-audit_template') - resp, body = self.client.create_audit_template( - name=name, description=description, goal=goal, strategy=strategy) - - self.addCleanup( - self.delete_audit_template, - audit_template_uuid=body["uuid"] - ) - - return resp, body - - def delete_audit_template(self, audit_template_uuid): - """Deletes a audit_template having the specified UUID - - :param audit_template_uuid: The unique identifier of the audit template - :return: Server response - """ - resp, _ = self.client.delete_audit_template(audit_template_uuid) - return resp - - # ### AUDITS ### # - - def create_audit(self, audit_template_uuid, audit_type='ONESHOT', - state=None, interval=None, parameters=None): - """Wrapper utility for creating a test audit - - :param audit_template_uuid: Audit Template UUID this audit will use - :param type: Audit type (either ONESHOT or CONTINUOUS) - :param state: Audit state (str) - :param interval: Audit interval in seconds (int) - :param parameters: list of execution parameters - :return: A tuple with The HTTP response and its body - """ - resp, body = self.client.create_audit( - audit_template_uuid=audit_template_uuid, audit_type=audit_type, - state=state, interval=interval, parameters=parameters) - - self.addCleanup(self.delete_audit, audit_uuid=body["uuid"]) - return resp, body - - def delete_audit(self, audit_uuid): - """Deletes an audit having the specified UUID - - :param audit_uuid: The unique identifier of the audit. - :return: the HTTP response - """ - - _, action_plans = self.client.list_action_plans(audit_uuid=audit_uuid) - for action_plan in action_plans.get("action_plans", []): - self.delete_action_plan(action_plan_uuid=action_plan["uuid"]) - - resp, _ = self.client.delete_audit(audit_uuid) - return resp - - def has_audit_succeeded(self, audit_uuid): - _, audit = self.client.show_audit(audit_uuid) - if audit.get('state') in ('FAILED', 'CANCELLED'): - raise ValueError() - - return audit.get('state') == 'SUCCEEDED' - - @classmethod - def has_audit_finished(cls, audit_uuid): - _, audit = cls.client.show_audit(audit_uuid) - return audit.get('state') in cls.FINISHED_STATES - - # ### ACTION PLANS ### # - - def delete_action_plan(self, action_plan_uuid): - """Deletes an action plan having the specified UUID - - :param action_plan_uuid: The unique identifier of the action plan. - :return: the HTTP response - """ - resp, _ = self.client.delete_action_plan(action_plan_uuid) - return resp - - def has_action_plan_finished(self, action_plan_uuid): - _, action_plan = self.client.show_action_plan(action_plan_uuid) - return action_plan.get('state') in ('FAILED', 'SUCCEEDED', 'CANCELLED', - 'SUPERSEDED') diff --git a/watcher_tempest_plugin/tests/scenario/manager.py b/watcher_tempest_plugin/tests/scenario/manager.py deleted file mode 100644 index 53645257f..000000000 --- a/watcher_tempest_plugin/tests/scenario/manager.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# Copyright 2013 IBM Corp. -# 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 - -from tempest.common import compute -from tempest.common import waiters -from tempest import config -from tempest.lib.common.utils import data_utils -from tempest.lib.common.utils import test_utils -from tempest.lib import exceptions as lib_exc -import tempest.test - -CONF = config.CONF - -LOG = log.getLogger(__name__) - - -class ScenarioTest(tempest.test.BaseTestCase): - """Base class for scenario tests. Uses tempest own clients. """ - - credentials = ['primary'] - - @classmethod - def setup_clients(cls): - super(ScenarioTest, cls).setup_clients() - # Clients (in alphabetical order) - cls.flavors_client = cls.os_primary.flavors_client - cls.compute_floating_ips_client = ( - cls.os_primary.compute_floating_ips_client) - if CONF.service_available.glance: - # Check if glance v1 is available to determine which client to use. - if CONF.image_feature_enabled.api_v1: - cls.image_client = cls.os_primary.image_client - elif CONF.image_feature_enabled.api_v2: - cls.image_client = cls.os_primary.image_client_v2 - else: - raise lib_exc.InvalidConfiguration( - 'Either api_v1 or api_v2 must be True in ' - '[image-feature-enabled].') - # Compute image client - cls.compute_images_client = cls.os_primary.compute_images_client - cls.keypairs_client = cls.os_primary.keypairs_client - # Nova security groups client - cls.compute_security_groups_client = ( - cls.os_primary.compute_security_groups_client) - cls.compute_security_group_rules_client = ( - cls.os_primary.compute_security_group_rules_client) - cls.servers_client = cls.os_primary.servers_client - cls.interface_client = cls.os_primary.interfaces_client - # Neutron network client - cls.networks_client = cls.os_primary.networks_client - cls.ports_client = cls.os_primary.ports_client - cls.routers_client = cls.os_primary.routers_client - cls.subnets_client = cls.os_primary.subnets_client - cls.floating_ips_client = cls.os_primary.floating_ips_client - cls.security_groups_client = cls.os_primary.security_groups_client - cls.security_group_rules_client = ( - cls.os_primary.security_group_rules_client) - - if CONF.volume_feature_enabled.api_v2: - cls.volumes_client = cls.os_primary.volumes_v2_client - cls.snapshots_client = cls.os_primary.snapshots_v2_client - else: - cls.volumes_client = cls.os_primary.volumes_client - cls.snapshots_client = cls.os_primary.snapshots_client - - # ## Test functions library - # - # The create_[resource] functions only return body and discard the - # resp part which is not used in scenario tests - - def _create_port(self, network_id, client=None, namestart='port-quotatest', - **kwargs): - if not client: - client = self.ports_client - name = data_utils.rand_name(namestart) - result = client.create_port( - name=name, - network_id=network_id, - **kwargs) - self.assertIsNotNone(result, 'Unable to allocate port') - port = result['port'] - self.addCleanup(test_utils.call_and_ignore_notfound_exc, - client.delete_port, port['id']) - return port - - def create_keypair(self, client=None): - if not client: - client = self.keypairs_client - name = data_utils.rand_name(self.__class__.__name__) - # We don't need to create a keypair by pubkey in scenario - body = client.create_keypair(name=name) - self.addCleanup(client.delete_keypair, name) - return body['keypair'] - - def create_server(self, name=None, image_id=None, flavor=None, - validatable=False, wait_until='ACTIVE', - clients=None, **kwargs): - """Wrapper utility that returns a test server. - - This wrapper utility calls the common create test server and - returns a test server. The purpose of this wrapper is to minimize - the impact on the code of the tests already using this - function. - """ - - # NOTE(jlanoux): As a first step, ssh checks in the scenario - # tests need to be run regardless of the run_validation and - # validatable parameters and thus until the ssh validation job - # becomes voting in CI. The test resources management and IP - # association are taken care of in the scenario tests. - # Therefore, the validatable parameter is set to false in all - # those tests. In this way create_server just return a standard - # server and the scenario tests always perform ssh checks. - - # Needed for the cross_tenant_traffic test: - if clients is None: - clients = self.os_primary - - if name is None: - name = data_utils.rand_name(self.__class__.__name__ + "-server") - - vnic_type = CONF.network.port_vnic_type - - # If vnic_type is configured create port for - # every network - if vnic_type: - ports = [] - - create_port_body = {'binding:vnic_type': vnic_type, - 'namestart': 'port-smoke'} - if kwargs: - # Convert security group names to security group ids - # to pass to create_port - if 'security_groups' in kwargs: - security_groups = \ - clients.security_groups_client.list_security_groups( - ).get('security_groups') - sec_dict = dict([(s['name'], s['id']) - for s in security_groups]) - - sec_groups_names = [s['name'] for s in kwargs.pop( - 'security_groups')] - security_groups_ids = [sec_dict[s] - for s in sec_groups_names] - - if security_groups_ids: - create_port_body[ - 'security_groups'] = security_groups_ids - networks = kwargs.pop('networks', []) - else: - networks = [] - - # If there are no networks passed to us we look up - # for the project's private networks and create a port. - # The same behaviour as we would expect when passing - # the call to the clients with no networks - if not networks: - networks = clients.networks_client.list_networks( - **{'router:external': False, 'fields': 'id'})['networks'] - - # It's net['uuid'] if networks come from kwargs - # and net['id'] if they come from - # clients.networks_client.list_networks - for net in networks: - net_id = net.get('uuid', net.get('id')) - if 'port' not in net: - port = self._create_port(network_id=net_id, - client=clients.ports_client, - **create_port_body) - ports.append({'port': port['id']}) - else: - ports.append({'port': net['port']}) - if ports: - kwargs['networks'] = ports - self.ports = ports - - tenant_network = self.get_tenant_network() - - body, servers = compute.create_test_server( - clients, - tenant_network=tenant_network, - wait_until=wait_until, - name=name, flavor=flavor, - image_id=image_id, **kwargs) - - self.addCleanup(waiters.wait_for_server_termination, - clients.servers_client, body['id']) - self.addCleanup(test_utils.call_and_ignore_notfound_exc, - clients.servers_client.delete_server, body['id']) - server = clients.servers_client.show_server(body['id'])['server'] - return server diff --git a/watcher_tempest_plugin/tests/scenario/test_execute_actuator.py b/watcher_tempest_plugin/tests/scenario/test_execute_actuator.py deleted file mode 100644 index fd4a18dc6..000000000 --- a/watcher_tempest_plugin/tests/scenario/test_execute_actuator.py +++ /dev/null @@ -1,340 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -import collections -import functools - -from tempest import config -from tempest.lib.common.utils import test_utils - -from watcher_tempest_plugin.tests.scenario import base - -CONF = config.CONF - - -class TestExecuteActionsViaActuator(base.BaseInfraOptimScenarioTest): - - scenarios = [ - ("nop", {"actions": [ - {"action_type": "nop", - "input_parameters": { - "message": "hello World"}}]}), - ("sleep", {"actions": [ - {"action_type": "sleep", - "input_parameters": { - "duration": 1.0}}]}), - ("change_nova_service_state", {"actions": [ - {"action_type": "change_nova_service_state", - "input_parameters": { - "state": "enabled"}, - "filling_function": - "_prerequisite_param_for_" - "change_nova_service_state_action"}]}), - ("resize", {"actions": [ - {"action_type": "resize", - "filling_function": "_prerequisite_param_for_resize_action"}]}), - ("migrate", {"actions": [ - {"action_type": "migrate", - "input_parameters": { - "migration_type": "live"}, - "filling_function": "_prerequisite_param_for_migrate_action"}, - {"action_type": "migrate", - "filling_function": "_prerequisite_param_for_migrate_action"}]}) - ] - - @classmethod - def resource_setup(cls): - super(TestExecuteActionsViaActuator, cls).resource_setup() - if CONF.compute.min_compute_nodes < 2: - raise cls.skipException( - "Less than 2 compute nodes, skipping multinode tests.") - if not CONF.compute_feature_enabled.live_migration: - raise cls.skipException("Live migration is not enabled") - - cls.initial_compute_nodes_setup = cls.get_compute_nodes_setup() - enabled_compute_nodes = [cn for cn in cls.initial_compute_nodes_setup - if cn.get('status') == 'enabled'] - - cls.wait_for_compute_node_setup() - - if len(enabled_compute_nodes) < 2: - raise cls.skipException( - "Less than 2 compute nodes are enabled, " - "skipping multinode tests.") - - @classmethod - def get_compute_nodes_setup(cls): - services_client = cls.mgr.services_client - available_services = services_client.list_services()['services'] - - return [srv for srv in available_services - if srv.get('binary') == 'nova-compute'] - - @classmethod - def wait_for_compute_node_setup(cls): - - def _are_compute_nodes_setup(): - try: - hypervisors_client = cls.mgr.hypervisor_client - hypervisors = hypervisors_client.list_hypervisors( - detail=True)['hypervisors'] - available_hypervisors = set( - hyp['hypervisor_hostname'] for hyp in hypervisors) - available_services = set( - service['host'] - for service in cls.get_compute_nodes_setup()) - - return ( - available_hypervisors == available_services and - len(hypervisors) >= 2) - except Exception: - return False - - assert test_utils.call_until_true( - func=_are_compute_nodes_setup, - duration=600, - sleep_for=2 - ) - - @classmethod - def rollback_compute_nodes_status(cls): - current_compute_nodes_setup = cls.get_compute_nodes_setup() - for cn_setup in current_compute_nodes_setup: - cn_hostname = cn_setup.get('host') - matching_cns = [ - cns for cns in cls.initial_compute_nodes_setup - if cns.get('host') == cn_hostname - ] - initial_cn_setup = matching_cns[0] # Should return a single result - if cn_setup.get('status') != initial_cn_setup.get('status'): - if initial_cn_setup.get('status') == 'enabled': - rollback_func = cls.mgr.services_client.enable_service - else: - rollback_func = cls.mgr.services_client.disable_service - rollback_func(binary='nova-compute', host=cn_hostname) - - def _create_one_instance_per_host(self): - """Create 1 instance per compute node - - This goes up to the min_compute_nodes threshold so that things don't - get crazy if you have 1000 compute nodes but set min to 3. - """ - host_client = self.mgr.hosts_client - all_hosts = host_client.list_hosts()['hosts'] - compute_nodes = [x for x in all_hosts if x['service'] == 'compute'] - - created_servers = [] - for _ in compute_nodes[:CONF.compute.min_compute_nodes]: - # by getting to active state here, this means this has - # landed on the host in question. - created_servers.append( - self.create_server(image_id=CONF.compute.image_ref, - wait_until='ACTIVE', - clients=self.mgr)) - - return created_servers - - def _get_flavors(self): - return self.mgr.flavors_client.list_flavors()['flavors'] - - def _prerequisite_param_for_migrate_action(self): - created_instances = self._create_one_instance_per_host() - instance = created_instances[0] - source_node = created_instances[0]["OS-EXT-SRV-ATTR:host"] - destination_node = created_instances[-1]["OS-EXT-SRV-ATTR:host"] - - parameters = { - "resource_id": instance['id'], - "migration_type": "live", - "source_node": source_node, - "destination_node": destination_node - } - - return parameters - - def _prerequisite_param_for_resize_action(self): - created_instances = self._create_one_instance_per_host() - instance = created_instances[0] - current_flavor_id = instance['flavor']['id'] - - flavors = self._get_flavors() - new_flavors = [f for f in flavors if f['id'] != current_flavor_id] - new_flavor = new_flavors[0] - - parameters = { - "resource_id": instance['id'], - "flavor": new_flavor['name'] - } - - return parameters - - def _prerequisite_param_for_change_nova_service_state_action(self): - enabled_compute_nodes = [cn for cn in - self.initial_compute_nodes_setup - if cn.get('status') == 'enabled'] - enabled_compute_node = enabled_compute_nodes[0] - - parameters = { - "resource_id": enabled_compute_node['host'], - "state": "enabled" - } - - return parameters - - def _fill_actions(self, actions): - for action in actions: - filling_function_name = action.pop('filling_function', None) - - if filling_function_name is not None: - filling_function = getattr(self, filling_function_name, None) - - if filling_function is not None: - parameters = filling_function() - - resource_id = parameters.pop('resource_id', None) - - if resource_id is not None: - action['resource_id'] = resource_id - - input_parameters = action.get('input_parameters', None) - - if input_parameters is not None: - parameters.update(input_parameters) - input_parameters.update(parameters) - else: - action['input_parameters'] = parameters - - def _execute_actions(self, actions): - self.wait_for_all_action_plans_to_finish() - - _, goal = self.client.show_goal("unclassified") - _, strategy = self.client.show_strategy("actuator") - _, audit_template = self.create_audit_template( - goal['uuid'], strategy=strategy['uuid']) - _, audit = self.create_audit( - audit_template['uuid'], parameters={"actions": actions}) - - self.assertTrue(test_utils.call_until_true( - func=functools.partial(self.has_audit_succeeded, audit['uuid']), - duration=30, - sleep_for=.5 - )) - _, action_plans = self.client.list_action_plans( - audit_uuid=audit['uuid']) - action_plan = action_plans['action_plans'][0] - - _, action_plan = self.client.show_action_plan(action_plan['uuid']) - - # Execute the action plan - _, updated_ap = self.client.start_action_plan(action_plan['uuid']) - - self.assertTrue(test_utils.call_until_true( - func=functools.partial( - self.has_action_plan_finished, action_plan['uuid']), - duration=300, - sleep_for=1 - )) - _, finished_ap = self.client.show_action_plan(action_plan['uuid']) - _, action_list = self.client.list_actions( - action_plan_uuid=finished_ap["uuid"]) - - self.assertIn(updated_ap['state'], ('PENDING', 'ONGOING')) - self.assertIn(finished_ap['state'], ('SUCCEEDED', 'SUPERSEDED')) - - expected_action_counter = collections.Counter( - act['action_type'] for act in actions) - action_counter = collections.Counter( - act['action_type'] for act in action_list['actions']) - - self.assertEqual(expected_action_counter, action_counter) - - def test_execute_nop(self): - self.addCleanup(self.rollback_compute_nodes_status) - - actions = [{ - "action_type": "nop", - "input_parameters": {"message": "hello World"}}] - self._execute_actions(actions) - - def test_execute_sleep(self): - self.addCleanup(self.rollback_compute_nodes_status) - - actions = [ - {"action_type": "sleep", - "input_parameters": {"duration": 1.0}} - ] - self._execute_actions(actions) - - def test_execute_change_nova_service_state(self): - self.addCleanup(self.rollback_compute_nodes_status) - - enabled_compute_nodes = [ - cn for cn in self.initial_compute_nodes_setup - if cn.get('status') == 'enabled'] - - enabled_compute_node = enabled_compute_nodes[0] - actions = [ - {"action_type": "change_nova_service_state", - "resource_id": enabled_compute_node['host'], - "input_parameters": {"state": "enabled"}} - ] - self._execute_actions(actions) - - def test_execute_resize(self): - self.addCleanup(self.rollback_compute_nodes_status) - - created_instances = self._create_one_instance_per_host() - instance = created_instances[0] - current_flavor_id = instance['flavor']['id'] - - flavors = self._get_flavors() - new_flavors = [f for f in flavors if f['id'] != current_flavor_id] - new_flavor = new_flavors[0] - - actions = [ - {"action_type": "resize", - "resource_id": instance['id'], - "input_parameters": {"flavor": new_flavor['name']}} - ] - self._execute_actions(actions) - - def test_execute_migrate(self): - self.addCleanup(self.rollback_compute_nodes_status) - - created_instances = self._create_one_instance_per_host() - instance = created_instances[0] - source_node = created_instances[0]["OS-EXT-SRV-ATTR:host"] - destination_node = created_instances[-1]["OS-EXT-SRV-ATTR:host"] - actions = [ - {"action_type": "migrate", - "resource_id": instance['id'], - "input_parameters": { - "migration_type": "live", - "source_node": source_node, - "destination_node": destination_node}} - ] - self._execute_actions(actions) - - def test_execute_scenarios(self): - self.addCleanup(self.rollback_compute_nodes_status) - - for _, scenario in self.scenarios: - actions = scenario['actions'] - self._fill_actions(actions) - self._execute_actions(actions) diff --git a/watcher_tempest_plugin/tests/scenario/test_execute_basic_optim.py b/watcher_tempest_plugin/tests/scenario/test_execute_basic_optim.py deleted file mode 100644 index b4b5e7611..000000000 --- a/watcher_tempest_plugin/tests/scenario/test_execute_basic_optim.py +++ /dev/null @@ -1,191 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -import functools - -from tempest import config -from tempest.lib.common.utils import test_utils - -from watcher_tempest_plugin.tests.scenario import base - -CONF = config.CONF - - -class TestExecuteBasicStrategy(base.BaseInfraOptimScenarioTest): - """Tests for action plans""" - - GOAL_NAME = "server_consolidation" - - @classmethod - def skip_checks(cls): - super(TestExecuteBasicStrategy, cls).skip_checks() - - @classmethod - def resource_setup(cls): - super(TestExecuteBasicStrategy, cls).resource_setup() - if CONF.compute.min_compute_nodes < 2: - raise cls.skipException( - "Less than 2 compute nodes, skipping multinode tests.") - if not CONF.compute_feature_enabled.live_migration: - raise cls.skipException("Live migration is not enabled") - - cls.initial_compute_nodes_setup = cls.get_compute_nodes_setup() - enabled_compute_nodes = [cn for cn in cls.initial_compute_nodes_setup - if cn.get('status') == 'enabled'] - - cls.wait_for_compute_node_setup() - - if len(enabled_compute_nodes) < 2: - raise cls.skipException( - "Less than 2 compute nodes are enabled, " - "skipping multinode tests.") - - @classmethod - def get_compute_nodes_setup(cls): - services_client = cls.mgr.services_client - available_services = services_client.list_services()['services'] - - return [srv for srv in available_services - if srv.get('binary') == 'nova-compute'] - - @classmethod - def wait_for_compute_node_setup(cls): - - def _are_compute_nodes_setup(): - try: - hypervisors_client = cls.mgr.hypervisor_client - hypervisors = hypervisors_client.list_hypervisors( - detail=True)['hypervisors'] - available_hypervisors = set( - hyp['hypervisor_hostname'] for hyp in hypervisors) - available_services = set( - service['host'] - for service in cls.get_compute_nodes_setup()) - - return ( - available_hypervisors == available_services and - len(hypervisors) >= 2) - except Exception: - return False - - assert test_utils.call_until_true( - func=_are_compute_nodes_setup, - duration=600, - sleep_for=2 - ) - - @classmethod - def rollback_compute_nodes_status(cls): - current_compute_nodes_setup = cls.get_compute_nodes_setup() - for cn_setup in current_compute_nodes_setup: - cn_hostname = cn_setup.get('host') - matching_cns = [ - cns for cns in cls.initial_compute_nodes_setup - if cns.get('host') == cn_hostname - ] - initial_cn_setup = matching_cns[0] # Should return a single result - if cn_setup.get('status') != initial_cn_setup.get('status'): - if initial_cn_setup.get('status') == 'enabled': - rollback_func = cls.mgr.services_client.enable_service - else: - rollback_func = cls.mgr.services_client.disable_service - rollback_func(binary='nova-compute', host=cn_hostname) - - def _create_one_instance_per_host(self): - """Create 1 instance per compute node - - This goes up to the min_compute_nodes threshold so that things don't - get crazy if you have 1000 compute nodes but set min to 3. - """ - host_client = self.mgr.hosts_client - all_hosts = host_client.list_hosts()['hosts'] - compute_nodes = [x for x in all_hosts if x['service'] == 'compute'] - - for idx, _ in enumerate( - compute_nodes[:CONF.compute.min_compute_nodes], start=1): - # by getting to active state here, this means this has - # landed on the host in question. - self.create_server( - name="instance-%d" % idx, - image_id=CONF.compute.image_ref, - wait_until='ACTIVE', - clients=self.mgr) - - def test_execute_basic_action_plan(self): - """Execute an action plan based on the BASIC strategy - - - create an audit template with the basic strategy - - run the audit to create an action plan - - get the action plan - - run the action plan - - get results and make sure it succeeded - """ - self.addCleanup(self.rollback_compute_nodes_status) - self._create_one_instance_per_host() - - _, goal = self.client.show_goal(self.GOAL_NAME) - _, strategy = self.client.show_strategy("basic") - _, audit_template = self.create_audit_template( - goal['uuid'], strategy=strategy['uuid']) - _, audit = self.create_audit(audit_template['uuid']) - - try: - self.assertTrue(test_utils.call_until_true( - func=functools.partial( - self.has_audit_finished, audit['uuid']), - duration=600, - sleep_for=2 - )) - except ValueError: - self.fail("The audit has failed!") - - _, finished_audit = self.client.show_audit(audit['uuid']) - if finished_audit.get('state') in ('FAILED', 'CANCELLED', 'SUSPENDED'): - self.fail("The audit ended in unexpected state: %s!" - % finished_audit.get('state')) - - _, action_plans = self.client.list_action_plans( - audit_uuid=audit['uuid']) - action_plan = action_plans['action_plans'][0] - - _, action_plan = self.client.show_action_plan(action_plan['uuid']) - - if action_plan['state'] in ('SUPERSEDED', 'SUCCEEDED'): - # This means the action plan is superseded so we cannot trigger it, - # or it is empty. - return - - # Execute the action by changing its state to PENDING - _, updated_ap = self.client.start_action_plan(action_plan['uuid']) - - self.assertTrue(test_utils.call_until_true( - func=functools.partial( - self.has_action_plan_finished, action_plan['uuid']), - duration=600, - sleep_for=2 - )) - _, finished_ap = self.client.show_action_plan(action_plan['uuid']) - _, action_list = self.client.list_actions( - action_plan_uuid=finished_ap["uuid"]) - - self.assertIn(updated_ap['state'], ('PENDING', 'ONGOING')) - self.assertIn(finished_ap['state'], ('SUCCEEDED', 'SUPERSEDED')) - - for action in action_list['actions']: - self.assertEqual('SUCCEEDED', action.get('state')) diff --git a/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py b/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py deleted file mode 100644 index 33b108a6c..000000000 --- a/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -import collections -import functools - -from tempest.lib.common.utils import test_utils - -from watcher_tempest_plugin.tests.scenario import base - - -class TestExecuteDummyStrategy(base.BaseInfraOptimScenarioTest): - """Tests for action plans""" - - def test_execute_dummy_action_plan(self): - """Execute an action plan based on the 'dummy' strategy - - - create an audit template with the 'dummy' strategy - - run the audit to create an action plan - - get the action plan - - run the action plan - - get results and make sure it succeeded - """ - _, goal = self.client.show_goal("dummy") - _, audit_template = self.create_audit_template(goal['uuid']) - _, audit = self.create_audit(audit_template['uuid']) - - self.assertTrue(test_utils.call_until_true( - func=functools.partial(self.has_audit_finished, audit['uuid']), - duration=30, - sleep_for=.5 - )) - - self.assertTrue(self.has_audit_succeeded(audit['uuid'])) - - _, action_plans = self.client.list_action_plans( - audit_uuid=audit['uuid']) - action_plan = action_plans['action_plans'][0] - - _, action_plan = self.client.show_action_plan(action_plan['uuid']) - - if action_plan['state'] in ['SUPERSEDED', 'SUCCEEDED']: - # This means the action plan is superseded so we cannot trigger it, - # or it is empty. - return - - # Execute the action by changing its state to PENDING - _, updated_ap = self.client.start_action_plan(action_plan['uuid']) - - self.assertTrue(test_utils.call_until_true( - func=functools.partial( - self.has_action_plan_finished, action_plan['uuid']), - duration=30, - sleep_for=.5 - )) - _, finished_ap = self.client.show_action_plan(action_plan['uuid']) - _, action_list = self.client.list_actions( - action_plan_uuid=finished_ap["uuid"]) - - action_counter = collections.Counter( - act['action_type'] for act in action_list['actions']) - - self.assertIn(updated_ap['state'], ('PENDING', 'ONGOING')) - self.assertIn(finished_ap['state'], ('SUCCEEDED', 'SUPERSEDED')) - - # A dummy strategy generates 2 "nop" actions and 1 "sleep" action - self.assertEqual(3, len(action_list['actions'])) - self.assertEqual(2, action_counter.get("nop")) - self.assertEqual(1, action_counter.get("sleep")) diff --git a/watcher_tempest_plugin/tests/scenario/test_execute_workload_balancing.py b/watcher_tempest_plugin/tests/scenario/test_execute_workload_balancing.py deleted file mode 100644 index 8594e9429..000000000 --- a/watcher_tempest_plugin/tests/scenario/test_execute_workload_balancing.py +++ /dev/null @@ -1,198 +0,0 @@ -# -*- 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. - -from __future__ import unicode_literals - -import functools - -from oslo_log import log -from tempest import config -from tempest.lib.common.utils import test_utils - -from watcher_tempest_plugin.tests.scenario import base - -CONF = config.CONF -LOG = log.getLogger(__name__) - - -class TestExecuteWorkloadBalancingStrategy(base.BaseInfraOptimScenarioTest): - """Tests for action plans""" - - GOAL = "workload_balancing" - - @classmethod - def skip_checks(cls): - super(TestExecuteWorkloadBalancingStrategy, cls).skip_checks() - - @classmethod - def resource_setup(cls): - super(TestExecuteWorkloadBalancingStrategy, cls).resource_setup() - if CONF.compute.min_compute_nodes < 2: - raise cls.skipException( - "Less than 2 compute nodes, skipping multinode tests.") - if not CONF.compute_feature_enabled.live_migration: - raise cls.skipException("Live migration is not enabled") - - cls.initial_compute_nodes_setup = cls.get_compute_nodes_setup() - enabled_compute_nodes = [cn for cn in cls.initial_compute_nodes_setup - if cn.get('status') == 'enabled'] - - cls.wait_for_compute_node_setup() - - if len(enabled_compute_nodes) < 2: - raise cls.skipException( - "Less than 2 compute nodes are enabled, " - "skipping multinode tests.") - - @classmethod - def get_hypervisors_setup(cls): - hypervisors_client = cls.mgr.hypervisor_client - hypervisors = hypervisors_client.list_hypervisors( - detail=True)['hypervisors'] - return hypervisors - - @classmethod - def get_compute_nodes_setup(cls): - services_client = cls.mgr.services_client - available_services = services_client.list_services()['services'] - - return [srv for srv in available_services - if srv.get('binary') == 'nova-compute'] - - def _migrate_server_to(self, server_id, dest_host, volume_backed=False): - kwargs = dict() - kwargs['disk_over_commit'] = False - block_migration = (CONF.compute_feature_enabled. - block_migration_for_live_migration and - not volume_backed) - body = self.mgr.servers_client.live_migrate_server( - server_id, host=dest_host, block_migration=block_migration, - **kwargs) - return body - - @classmethod - def wait_for_compute_node_setup(cls): - - def _are_compute_nodes_setup(): - try: - hypervisors = cls.get_hypervisors_setup() - available_hypervisors = set( - hyp['hypervisor_hostname'] for hyp in hypervisors - if hyp['state'] == 'up') - available_services = set( - service['host'] - for service in cls.get_compute_nodes_setup() - if service['state'] == 'up') - return ( - len(available_hypervisors) == len(available_services) and - len(hypervisors) >= 2) - except Exception as exc: - LOG.exception(exc) - return False - - assert test_utils.call_until_true( - func=_are_compute_nodes_setup, - duration=600, - sleep_for=2 - ) - - @classmethod - def rollback_compute_nodes_status(cls): - current_compute_nodes_setup = cls.get_compute_nodes_setup() - for cn_setup in current_compute_nodes_setup: - cn_hostname = cn_setup.get('host') - matching_cns = [ - cns for cns in cls.initial_compute_nodes_setup - if cns.get('host') == cn_hostname - ] - initial_cn_setup = matching_cns[0] # Should return a single result - if cn_setup.get('status') != initial_cn_setup.get('status'): - if initial_cn_setup.get('status') == 'enabled': - rollback_func = cls.mgr.services_client.enable_service - else: - rollback_func = cls.mgr.services_client.disable_service - rollback_func(binary='nova-compute', host=cn_hostname) - - def _create_one_instance_per_host(self): - """Create 1 instance per compute node - - This goes up to the min_compute_nodes threshold so that things don't - get crazy if you have 1000 compute nodes but set min to 3. - """ - host_client = self.mgr.hosts_client - all_hosts = host_client.list_hosts()['hosts'] - compute_nodes = [x for x in all_hosts if x['service'] == 'compute'] - - created_instances = [] - for _ in compute_nodes[:CONF.compute.min_compute_nodes]: - # by getting to active state here, this means this has - # landed on the host in question. - created_instances.append( - self.create_server(image_id=CONF.compute.image_ref, - wait_until='ACTIVE', clients=self.mgr)) - return created_instances - - def _pack_all_created_instances_on_one_host(self, instances): - hypervisors = [ - hyp['hypervisor_hostname'] for hyp in self.get_hypervisors_setup() - if hyp['state'] == 'up'] - node = hypervisors[0] - for instance in instances: - if instance.get('OS-EXT-SRV-ATTR:hypervisor_hostname') != node: - self._migrate_server_to(instance['id'], node) - - def test_execute_workload_stabilization(self): - """Execute an action plan using the workload_stabilization strategy""" - self.addCleanup(self.rollback_compute_nodes_status) - instances = self._create_one_instance_per_host() - self._pack_all_created_instances_on_one_host(instances) - - audit_parameters = { - "metrics": ["cpu_util"], - "thresholds": {"cpu_util": 0.2}, - "weights": {"cpu_util_weight": 1.0}, - "instance_metrics": {"cpu_util": "compute.node.cpu.percent"}} - - _, goal = self.client.show_goal(self.GOAL) - _, strategy = self.client.show_strategy("workload_stabilization") - _, audit_template = self.create_audit_template( - goal['uuid'], strategy=strategy['uuid']) - _, audit = self.create_audit( - audit_template['uuid'], parameters=audit_parameters) - - try: - self.assertTrue(test_utils.call_until_true( - func=functools.partial( - self.has_audit_finished, audit['uuid']), - duration=600, - sleep_for=2 - )) - except ValueError: - self.fail("The audit has failed!") - - _, finished_audit = self.client.show_audit(audit['uuid']) - if finished_audit.get('state') in ('FAILED', 'CANCELLED'): - self.fail("The audit ended in unexpected state: %s!" % - finished_audit.get('state')) - - _, action_plans = self.client.list_action_plans( - audit_uuid=audit['uuid']) - action_plan = action_plans['action_plans'][0] - - _, action_plan = self.client.show_action_plan(action_plan['uuid']) - _, action_list = self.client.list_actions( - action_plan_uuid=action_plan["uuid"])