Retire Senlin: remove repo content

Senlin project is retiring
- https://review.opendev.org/c/openstack/governance/+/919347

this commit remove the content of this project repo

Depends-On: https://review.opendev.org/c/openstack/project-config/+/919348/
Change-Id: I02c14c754009f34fb36328e5dac73568bea7f672
This commit is contained in:
Ghanshyam Mann
2024-05-10 14:29:03 -07:00
parent dadcdeb7ea
commit 83ccfcbeb3
163 changed files with 8 additions and 12327 deletions

View File

@@ -1,6 +0,0 @@
[run]
branch = True
source = senlin_tempest_plugin
[report]
ignore_errors = True

19
.gitignore vendored
View File

@@ -1,19 +0,0 @@
__pycache__
.testrepository
*~
*.eggs
*.egg-info
*.iml
*.log
*.pyc
*.swp
*.swo
.coverage
.coverage.*
.idea
.project
.pydevproject
.tox
.venv
.DS_Store
.stestr

View File

@@ -1,3 +0,0 @@
[DEFAULT]
test_path=./senlin_tempest_plugin/tests/
top_dir=./

View File

@@ -1,52 +0,0 @@
- project:
queue: senlin
templates:
- check-requirements
- tempest-plugin-jobs
check:
jobs:
- senlin-dsvm-tempest-py3-api
- senlin-dsvm-tempest-py3-api-2023-1
- senlin-dsvm-tempest-py3-api-zed
- senlin-dsvm-tempest-py3-api-yoga
- senlin-dsvm-tempest-py3-api-xena
- senlin-dsvm-tempest-py3-api-wallaby
- senlin-tempest-api-ipv6-only
- senlin-dsvm-tempest-py3-functional
- senlin-dsvm-tempest-py3-integration:
voting: false
gate:
jobs:
- senlin-dsvm-tempest-py3-api
- senlin-tempest-api-ipv6-only
- senlin-dsvm-tempest-py3-functional
- job:
name: senlin-dsvm-tempest-py3-api-2023-1
parent: senlin-dsvm-tempest-py3-api
nodeset: openstack-single-node-jammy
override-checkout: stable/2023.1
- job:
name: senlin-dsvm-tempest-py3-api-zed
parent: senlin-dsvm-tempest-py3-api
nodeset: openstack-single-node-focal
override-checkout: stable/zed
- job:
name: senlin-dsvm-tempest-py3-api-yoga
parent: senlin-dsvm-tempest-py3-api
nodeset: openstack-single-node-focal
override-checkout: stable/yoga
- job:
name: senlin-dsvm-tempest-py3-api-xena
parent: senlin-dsvm-tempest-py3-api
nodeset: openstack-single-node-focal
override-checkout: stable/xena
- job:
name: senlin-dsvm-tempest-py3-api-wallaby
parent: senlin-dsvm-tempest-py3-api
nodeset: openstack-single-node-focal
override-checkout: stable/wallaby

View File

@@ -1,91 +0,0 @@
Before You Start
================
If you would like to contribute to the development of OpenStack,
you must follow the steps in this page:
https://docs.openstack.org/infra/manual/developers.html
Once those steps have been completed, changes to OpenStack
should be submitted for review via the Gerrit tool, following
the workflow documented at:
https://docs.openstack.org/infra/manual/developers.html#development-workflow
Where to Start
==============
There are many ways to start your contribution.
Sign on a bug to fix
--------------------
Bugs related to senlin are reported and tracked on the individual sites on
Launchpad:
- Senlin Server: https://bugs.launchpad.net/senlin
- Senlin Client: https://bugs.launchpad.net/python-senlinclient
- Senlin Dashboard: https://bugs.launchpad.net/senlin-dashboard
You can pick any bug item that has not been assigned to work on. Each bug fix
patch should be accompanied with a release note.
Pick a TODO item
----------------
Senlin team maintains a ``TODO.rst`` file under the root directory, where you
can add new items, claim existing items and remove items that are completed.
You may want to check if there are items you can pick by:
#. Propose a patch to remove the item from the ``TODO.rst`` file.
#. Add an item to the `etherpad page`_ which the core team uses to track the
progress of individual work items.
#. Start working on the item and keep updating your progress on the `etherpad
page`_, e.g. paste the patch review link to the page.
#. Mark the item from the `etherpad page`_ as completed when the patches are
all merged.
Start a Bigger Effort
---------------------
Senlin team also maintains a ``FEATURES.rst`` file under the root directory,
where you can add new items by proposing a patch to the file or claim an item
to work on. However, the work items in the ``FEATURES.rst`` file are all
non-trivial, thus demands for a deeper discussion before being worked on. The
expected workflow for these items is:
#. Propose a spec file to the ``doc/specs`` directory describing the detailed
design and other options, if any.
#. Work with the reviewers to polish the design until it is accepted.
#. Propose blueprint(s) to track the progress of the work item by registering
them at the `blueprint page`_.
#. Start working on the blueprints and checking in patches. Each patch should
have a ``partial-blueprint: <blueprint>`` tag in its commit message.
#. For each blueprint, add an item to the `etherpad page`_ so that it can be
closely tracked in weekly meetings.
#. Mark the blueprint(s) as completed when all related patches are merged.
#. Propose a patch to the ``FEATURES.rst`` file to remove the work item.
#. Propose a separate release note patch for the new feature.
Reporting Bugs
==============
Bugs should be filed on Launchpad site:
- Senlin Server: https://bugs.launchpad.net/senlin
- Senlin Client: https://bugs.launchpad.net/python-senlinclient
- Senlin Dashboard: https://bugs.launchpad.net/senlin-dashboard
Meet the Developers
===================
Real-time communication among developers are mostly done via IRC.
The team is using the #senlin channel on oftc.net.
.. _`etherpad page`: https://etherpad.openstack.org/p/senlin-newton-workitems
.. _`blueprint page`: https://blueprints.launchpad.net/senlin

View File

@@ -1,18 +0,0 @@
Senlin Style Commandments
=========================
- Step 1: Read the OpenStack Style Commandments
https://docs.openstack.org/hacking/latest/
- Step 2: Read on
Senlin Specific Commandments
----------------------------
- [S318] Use assertion ``assertIsNone(A)`` instead of ``assertEqual(A, None)``
or ``assertEqual(None, A)``.
- [S319] Use ``jsonutils`` functions rather than using the ``json`` package
directly.
- [S320] Default arguments of a method should not be mutable.
- [S321] The api_version decorator has to be the first decorator on a method.
- [S322] LOG.warn is deprecated. Enforce use of LOG.warning.
- [S323] Use assertTrue(...) rather than assertEqual(True, ...).

176
LICENSE
View File

@@ -1,176 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

View File

@@ -1,46 +1,10 @@
========================
Team and repository tags
========================
This project is no longer maintained.
.. image:: https://governance.openstack.org/tc/badges/senlin.svg
:target: https://governance.openstack.org/tc/reference/tags/index.html
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
.. Change things from this point on
==============================
Tempest integration of Senlin
==============================
This project contains the Tempest plugin for the Senlin project for
OpenStack Clustering.
For more information about Senlin see:
https://docs.openstack.org/senlin/latest/
For more information about Tempest plugins see:
https://docs.openstack.org/tempest/latest/plugin.html
* Free software: Apache license
* Source: https://opendev.org/openstack/senlin-tempest-plugin
Installing
----------
Clone this repository to the destination machine, and call from the tempest repo::
$ tox -e venv-tempest -- pip install (path to the senlin-tempest-plugin directory)
Running the tests
-----------------
To run all the tests from this plugin, call from the tempest repo::
$ tox -e all -- senlin_tempest_plugin
To run a single test case, call with full path, for example::
$ tox -e all -- senlin_tempest_plugin.tests.api.policies.test_policy_update.TestPolicyUpdate.test_policy_update
To retrieve a list of all tempest tests, run::
$ testr list-tests
For any further questions, please email
openstack-discuss@lists.openstack.org or join #openstack-dev on
OFTC.

View File

@@ -1,2 +0,0 @@
sphinx!=2.1.0,>=2.0.0 # BSD
reno>=2.5.0 # Apache-2.0

View File

@@ -1,6 +0,0 @@
---
upgrade:
- |
Python 2.7 support has been dropped. Last release of senlin-tempest-plugin
to support python 2.7 is OpenStack Train. The minimum version of Python now
supported by senlin-tempest-plugin is Python 3.6.

View File

@@ -1,11 +0,0 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr!=2.1.0,>=2.0.0 # Apache-2.0
openstacksdk>=0.24.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0
tempest>=17.1.0 # Apache-2.0
tenacity>=4.9.0 # Apache-2.0

View File

@@ -1,21 +0,0 @@
====================
Tempest Integration
====================
This directory contains Tempest tests to cover senlin project.
To list all senlin tempest cases, go to tempest directory, then run::
$ testr list-tests senlin
To run only these tests in tempest, go to tempest directory, then run::
$ ./run_tempest.sh -N -- senlin
To run a single test case, go to tempest directory, then run with test case name, e.g.::
$ ./run_tempest.sh -N -- senlin_tempest_plugin.tempest.api.test_cluster_basic.TestClusterBasic.test_cluster_create_delete
If you can't find run_tempest.sh script in tempest directory, that means the script has been removed in a certain version.
Then you can use "nosetests -v" to replace "./run_tempest.sh -N" in above command.
More information about running tempest test can be found here: https://docs.openstack.org/tempest/latest/

View File

@@ -1,53 +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.
from tempest import config
from tempest import test
CONF = config.CONF
class BaseSenlinTest(test.BaseTestCase):
credentials = ['primary', 'admin']
default_params = config.service_client_config()
# NOTE: Tempest uses timeout values of compute API if project specific
# timeout values don't exist.
default_params_with_timeout_values = {
'build_interval': CONF.compute.build_interval,
'build_timeout': CONF.compute.build_timeout
}
default_params_with_timeout_values.update(default_params)
client = None
max_api_version = '1.0'
@classmethod
def skip_checks(cls):
super(BaseSenlinTest, cls).skip_checks()
if not CONF.service_available.senlin:
skip_msg = 'Senlin is disabled'
raise cls.skipException(skip_msg)
@classmethod
def setup_clients(cls):
super(BaseSenlinTest, cls).setup_clients()
@classmethod
def resource_setup(cls):
super(BaseSenlinTest, cls).resource_setup()
cls.max_api_version = cls.client.get_max_api_version()

View File

@@ -1,220 +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 time
from urllib import parse as urllib
from oslo_serialization import jsonutils
from oslo_utils import timeutils
from tempest import config as cfg
from tempest.lib.common import rest_client
from tempest.lib import exceptions
CONF = cfg.CONF
class ClusteringAPIClient(rest_client.RestClient):
version = 'v1'
api_microversion = 'latest'
def get_headers(self, accept_type=None, send_type=None):
headers = super(ClusteringAPIClient, self).get_headers(
accept_type=accept_type, send_type=send_type)
headers['openstack-api-version'] = ('clustering %s' %
self.api_microversion)
return headers
def get_resp(self, resp, body):
# Parse status code and location
res = {
'status': int(resp.pop('status')),
'location': resp.pop('location', None)
}
# Parse other keys included in resp
res.update(resp)
# Parse body, Python 3 returns string "b'null'" for delete operations
if (body is not None and
str(body) not in ["", "null", "b'null'", "b''"]):
res['body'] = self._parse_resp(body)
else:
res['body'] = None
return res
def get_obj(self, obj_type, obj_id, params=None):
uri = '{0}/{1}/{2}'.format(self.version, obj_type, obj_id)
if params:
uri += '?{0}'.format(urllib.urlencode(params))
resp, body = self.get(uri)
return self.get_resp(resp, body)
def create_obj(self, obj_type, attrs):
uri = '{0}/{1}'.format(self.version, obj_type)
resp, body = self.post(uri, body=jsonutils.dumps(attrs))
return self.get_resp(resp, body)
def list_objs(self, obj_type, params=None):
uri = '{0}/{1}'.format(self.version, obj_type)
if params:
uri += '?{0}'.format(urllib.urlencode(params))
resp, body = self.get(uri)
return self.get_resp(resp, body)
def update_obj(self, obj_type, obj_id, attrs):
uri = '{0}/{1}/{2}'.format(self.version, obj_type, obj_id)
resp, body = self.patch(uri, body=jsonutils.dumps(attrs))
return self.get_resp(resp, body)
def delete_obj(self, obj_type, obj_id):
uri = '{0}/{1}/{2}'.format(self.version, obj_type, obj_id)
resp, body = self.request('DELETE', uri)
return self.get_resp(resp, body)
def validate_obj(self, obj_type, attrs):
uri = '{0}/{1}/validate'.format(self.version, obj_type)
headers = {'openstack-api-version': 'clustering 1.2'}
resp, body = self.post(uri, body=jsonutils.dumps(attrs),
headers=headers)
return self.get_resp(resp, body)
def trigger_webhook(self, webhook_url, params=None):
if params is not None:
params = jsonutils.dumps(params)
resp, body = self.raw_request(webhook_url, 'POST', body=params)
return self.get_resp(resp, body)
def trigger_action(self, obj_type, obj_id, params=None):
uri = '{0}/{1}/{2}/actions'.format(self.version, obj_type, obj_id)
if params is not None:
params = jsonutils.dumps(params)
resp, body = self.request('POST', uri, body=params)
return self.get_resp(resp, body)
def trigger_operation(self, obj_type, obj_id, params):
uri = '{0}/{1}/{2}/ops'.format(self.version, obj_type, obj_id)
data = jsonutils.dumps(params)
resp, body = self.post(uri, body=data)
return self.get_resp(resp, body)
def cluster_replace_nodes(self, obj_type, obj_id, params=None):
uri = '{0}/{1}/{2}/actions'.format(self.version, obj_type, obj_id)
if params is not None:
params = jsonutils.dumps(params)
headers = self.get_headers()
resp, body = self.post(uri, body=params, headers=headers)
return self.get_resp(resp, body)
def list_cluster_policies(self, cluster_id, params=None):
uri = '{0}/clusters/{1}/policies'.format(self.version, cluster_id)
if params:
uri += '?{0}'.format(urllib.urlencode(params))
resp, body = self.get(uri)
return self.get_resp(resp, body)
def get_cluster_policy(self, cluster_id, policy_id):
uri = '{0}/clusters/{1}/policies/{2}'.format(self.version, cluster_id,
policy_id)
resp, body = self.get(uri)
return self.get_resp(resp, body)
def cluster_collect(self, cluster, path):
uri = '{0}/clusters/{1}/attrs/{2}'.format(self.version, cluster, path)
resp, body = self.get(uri)
return self.get_resp(resp, body)
def list_profile_type_operation(self, profile_type, params=None):
uri = '{0}/profile-types/{1}/ops'.format(self.version, profile_type)
if params:
uri += '?{0}'.format(urllib.urlencode(params))
resp, body = self.get(uri)
return self.get_resp(resp, body)
def wait_for_status(self, obj_type, obj_id, expected_status, timeout=None):
if isinstance(expected_status, list):
expected_status_list = expected_status
else:
expected_status_list = [expected_status]
if timeout is None:
timeout = CONF.clustering.wait_timeout
with timeutils.StopWatch(timeout) as timeout_watch:
while timeout > 0:
time.sleep(5)
res = self.get_obj(obj_type, obj_id)
if res['body']['status'] in expected_status_list:
return res
timeout = timeout_watch.leftover(True)
raise Exception('Timeout waiting for status.')
def wait_for_delete(self, obj_type, obj_id, timeout=None):
if timeout is None:
timeout = CONF.clustering.wait_timeout
with timeutils.StopWatch(timeout) as timeout_watch:
while timeout > 0:
try:
self.get_obj(obj_type, obj_id)
except exceptions.NotFound:
return
time.sleep(5)
timeout = timeout_watch.leftover(True)
raise Exception('Timeout waiting for deletion.')
def get_max_api_version(self):
resp, body = self.get('/')
res = self.get_resp(resp, body)
# Verify resp of API versions list
self.expected_success(300, res['status'])
versions = res['body']
# Only version 1.0 API is now supported
if (len(versions) != 1 or 'id' not in versions[0] or
versions[0]['id'] != '1.0'):
raise Exception(versions)
if 'max_version' not in versions[0]:
raise Exception('max_version is missing')
return versions[0]['max_version']
class ClusteringFunctionalClient(ClusteringAPIClient):
"""This is the tempest client for Senlin functional test"""
pass
class ClusteringIntegrationClient(ClusteringAPIClient):
"""This is the tempest client for Senlin integration test"""
pass

View File

@@ -1,80 +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.
from oslo_serialization import jsonutils
from oslo_utils import timeutils
from tempest import config
from tempest.lib.common import rest_client
from tempest.lib import exceptions
import time
from urllib import parse as urllib
CONF = config.CONF
class V21ComputeClient(rest_client.RestClient):
def __init__(self, auth_provider, service, region, **kwargs):
super(V21ComputeClient, self).__init__(
auth_provider, service, region, **kwargs)
def get_resp(self, resp, body):
# Parse status code and location
res = {
'status': int(resp.pop('status')),
'location': resp.pop('location', None)
}
# Parse other keys included in resp
res.update(resp)
# Parse body
res['body'] = self._parse_resp(body)
return res
def create_obj(self, obj_type, attrs):
uri = '/{0}'.format(obj_type)
resp, body = self.post(uri, body=jsonutils.dumps(attrs))
return self.get_resp(resp, body)
def delete_obj(self, obj_type, obj_id):
uri = '/{0}/{1}'.format(obj_type, obj_id)
resp, body = self.delete(uri)
return self.get_resp(resp, body)
def run_operation_obj(self, obj_type, obj_id, operation, attrs):
uri = '/{0}/{1}/{2}'.format(obj_type, obj_id, operation)
resp, body = self.post(uri, body=jsonutils.dumps(attrs))
return self.get_resp(resp, body)
def get_obj(self, obj_type, obj_id, params=None):
uri = '/{0}/{1}'.format(obj_type, obj_id)
if params:
uri += '?{0}'.format(urllib.urlencode(params))
resp, body = self.get(uri)
return self.get_resp(resp, body)
def wait_for_status(self, obj_type, obj_id, expected_status, timeout=None):
timeout = timeout or CONF.clustering.wait_timeout
with timeutils.StopWatch(timeout) as timeout_watch:
while not timeout_watch.expired():
time.sleep(5)
res = self.get_obj(obj_type, obj_id)
if res['body']['status'] == expected_status:
return res
raise exceptions.TimeoutException()

View File

@@ -1,217 +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.
spec_nova_server = {
"type": "os.nova.server",
"version": "1.0",
"properties": {
"flavor": "1",
"name": "new-server-test",
"image": "cirros-0.4.0-x86_64-disk",
"networks": [
{"network": "private"}
]
}
}
spec_heat_stack = {
"type": "os.heat.stack",
"version": "1.0",
"properties": {
"template": {
"heat_template_version": "2014-10-16",
"parameters": {
"str_length": {
"type": "number",
"default": 64
}
},
"resources": {
"random": {
"type": "OS::Heat::RandomString",
"properties": {
"length": {"get_param": "str_length"}
}
}
},
"outputs": {
"result": {
"value": {"get_attr": ["random", "value"]}
}
}
}
}
}
spec_scaling_policy = {
"type": "senlin.policy.scaling",
"version": "1.0",
"properties": {
"event": "CLUSTER_SCALE_IN",
"adjustment": {
"type": "CHANGE_IN_CAPACITY",
"number": 1,
"min_step": 1,
"best_effort": True
}
}
}
spec_lb_policy = {
"type": "senlin.policy.loadbalance",
"version": "1.1",
"properties": {
"pool": {
"protocol": "HTTP",
"protocol_port": 80,
"subnet": "private-subnet",
"lb_method": "ROUND_ROBIN",
"session_persistence": {
"type": "SOURCE_IP",
"cookie_name": "test-cookie"
}
},
"vip": {
"subnet": "private-subnet",
"connection_limit": 100,
"protocol": "HTTP",
"protocol_port": 80
},
"health_monitor": {
"type": "HTTP",
"delay": "1",
"timeout": 1,
"max_retries": 5,
"admin_state_up": True,
"http_method": "GET",
"url_path": "/index.html",
"expected_codes": "200,201,202"
},
"lb_status_timeout": 300
}
}
spec_batch_policy = {
"type": "senlin.policy.batch",
"version": "1.0",
"properties": {
"min_in_service": 1,
"max_batch_size": 1,
"pause_time": 3
}
}
spec_deletion_policy = {
"type": "senlin.policy.deletion",
"version": "1.1",
"properties": {
"criteria": "OLDEST_FIRST"
}
}
spec_deletion_policy_with_hook = {
"type": "senlin.policy.deletion",
"version": "1.1",
"properties": {
"hooks": {
"type": "zaqar",
"timeout": 300,
"params": {
"queue": "test_queue"
}
},
"criteria": "OLDEST_FIRST"
}
}
spec_health_policy = {
"version": "1.1",
"type": "senlin.policy.health",
"description": "A policy for maintaining node health from a cluster.",
"properties": {
"detection": {
"detection_modes": [
{
"type": "NODE_STATUS_POLLING"
},
{
"type": "NODE_STATUS_POLL_URL",
"options": {
"poll_url_retry_limit": 3,
"poll_url": "http://127.0.0.1:5050",
"poll_url_retry_interval": 2
}
}
],
"node_update_timeout": 10,
"interval": 10
},
"recovery": {
"node_delete_timeout": 90,
"actions": [
{
"name": "RECREATE"
}
],
"node_force_recreate": True
}
}
}
spec_health_policy_duplicate_type = {
"version": "1.1",
"type": "senlin.policy.health",
"properties": {
"detection": {
"detection_modes": [
{
"type": "NODE_STATUS_POLLING"
},
{
"type": "NODE_STATUS_POLLING",
}
],
},
"recovery": {
"actions": [
{
"name": "RECREATE"
}
],
}
}
}
spec_health_policy_invalid_combo = {
"version": "1.1",
"type": "senlin.policy.health",
"properties": {
"detection": {
"detection_modes": [
{
"type": "NODE_STATUS_POLLING"
},
{
"type": "LIFECYCLE_EVENTS",
}
],
},
"recovery": {
"actions": [
{
"name": "RECREATE"
}
],
}
}
}

View File

@@ -1,71 +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.
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from tempest import config
from tempest.lib.common import rest_client
CONF = config.CONF
class V2MessagingClient(rest_client.RestClient):
def __init__(self, auth_provider, service, region, **kwargs):
super(V2MessagingClient, self).__init__(
auth_provider, service, region, **kwargs)
self.uri_prefix = 'v2'
client_id = uuidutils.generate_uuid()
self.headers = {'Client-ID': client_id}
def get_resp(self, resp, body):
# Parse status code and location
res = {
'status': int(resp.pop('status')),
'location': resp.pop('location', None)
}
# Parse other keys included in resp
res.update(resp)
# Parse body
res['body'] = self._parse_resp(body)
return res
def create_queue(self, queue_name):
uri = '{0}/queues/{1}'.format(self.uri_prefix, queue_name)
resp, body = self.put(uri, '', extra_headers=True,
headers=self.headers)
return self.get_resp(resp, body)
def delete_queue(self, queue_name):
uri = '{0}/queues/{1}'.format(self.uri_prefix, queue_name)
resp, body = self.delete(uri, extra_headers=True, headers=self.headers)
return self.get_resp(resp, body)
def list_messages(self, queue_name):
uri = '{0}/queues/{1}/messages'.format(self.uri_prefix, queue_name)
resp, body = self.get(uri, extra_headers=True, headers=self.headers)
return self.get_resp(resp, body)
def post_messages(self, queue_name, messages):
uri = '{0}/queues/{1}/messages'.format(self.uri_prefix, queue_name)
resp, body = self.post(uri, body=jsonutils.dumps(messages),
extra_headers=True,
headers=self.headers)
return self.get_resp(resp, body)

View File

@@ -1,76 +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.
from oslo_serialization import jsonutils
from oslo_utils import timeutils
import time
from tempest import config
from tempest.lib.common import rest_client
from tempest.lib import exceptions
CONF = config.CONF
class NetworkClient(rest_client.RestClient):
def __init__(self, auth_provider, service, region, **kwargs):
super(NetworkClient, self).__init__(
auth_provider, service, region, **kwargs)
self.uri_prefix = 'v2.0'
def get_resp(self, resp, body):
# Parse status code and location
res = {
'status': int(resp.pop('status')),
'location': resp.pop('location', None)
}
# Parse other keys included in resp
res.update(resp)
# Parse body
res['body'] = self._parse_resp(body)
return res
def create_obj(self, obj_type, attrs):
uri = '{0}/{1}'.format(self.uri_prefix, obj_type)
resp, body = self.post(uri, body=jsonutils.dumps(attrs))
return self.get_resp(resp, body)
def get_obj(self, obj_type, obj_id):
uri = '{0}/{1}/{2}'.format(self.uri_prefix, obj_type, obj_id)
resp, body = self.get(uri)
return self.get_resp(resp, body)
def delete_obj(self, obj_type, obj_id):
uri = '{0}/{1}/{2}'.format(self.uri_prefix, obj_type, obj_id)
resp, body = self.delete(uri)
return self.get_resp(resp, body)
def wait_for_delete(self, obj_type, obj_id, timeout=None):
timeout = timeout or CONF.clustering.wait_timeout
with timeutils.StopWatch(timeout) as timeout_watch:
while not timeout_watch.expired():
try:
self.get_obj(obj_type, obj_id)
except exceptions.NotFound:
return
time.sleep(5)
raise exceptions.TimeoutException()

View File

@@ -1,729 +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 functools
from http import client as http
import http.server as BaseHTTPServer
import os
import signal
import tempfile
import tenacity
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
from senlin_tempest_plugin.common import constants
CONF = config.CONF
def is_version_supported(api_version, version):
"""Check if a version is supported by the API.
:param api_version: Reference endpoint API version.
:param version: Version to check against API version.
:return: boolean if the version is supported.
"""
api_split = list(map(int, api_version.split('.')))
ver_split = list(map(int, version.split('.')))
if api_split[0] > ver_split[0] or (
api_split[0] == ver_split[0] and api_split[1] >= ver_split[1]):
return True
return False
def api_microversion(api_microversion):
"""Decorator used to specify api_microversion for test."""
def decorator(func):
@functools.wraps(func)
def wrapped(self):
if not is_version_supported(self.max_api_version,
api_microversion):
msg = ('This test case uses a client with API version {0}, '
'but Senlin service has max API version {1}.').format(
api_microversion,
self.max_api_version)
raise self.skipException(msg)
old = self.client.api_microversion
self.client.api_microversion = api_microversion
try:
func(self)
finally:
self.client.api_microversion = old
return wrapped
return decorator
def prepare_and_cleanup_for_nova_server(base, cidr, spec=None):
keypair_name = create_a_keypair(base, is_admin_manager=False)
if spec is None:
base.spec = constants.spec_nova_server
else:
base.spec = spec
base.spec['properties']['key_name'] = keypair_name
base.addCleanup(delete_a_keypair, base, keypair_name,
is_admin_manager=False)
n_name = base.spec['properties']['networks'][0]['network']
network_id = create_a_network(base, name=n_name)
base.addCleanup(delete_a_network, base, network_id)
subnet_id = create_a_subnet(base, network_id, cidr)
base.addCleanup(delete_a_subnet, base, subnet_id)
def create_spec_from_config(network_name=None):
"""Utility function that creates a spec object from tempest config"""
spec = constants.spec_nova_server
spec['properties']['flavor'] = CONF.compute.flavor_ref
spec['properties']['image'] = CONF.compute.image_ref
if network_name:
spec['properties']['networks'][0]['network'] = network_name
return spec
def create_a_profile(base, spec=None, name=None, metadata=None):
"""Utility function that generates a Senlin profile."""
if spec is None:
spec = constants.spec_nova_server
if name is None:
name = data_utils.rand_name("tempest-created-profile")
if metadata:
spec['properties']['metadata'] = metadata
params = {
'profile': {
'name': name,
'spec': spec,
}
}
res = base.client.create_obj('profiles', params)
return res['body']['id']
def delete_a_profile(base, profile_id, ignore_missing=False):
"""Utility function that deletes a Senlin profile."""
res = base.client.delete_obj('profiles', profile_id)
if res['status'] == 404:
if ignore_missing:
return
raise exceptions.NotFound()
def create_a_cluster(base, profile_id, desired_capacity=0, min_size=0,
max_size=-1, timeout=None, metadata=None, name=None,
config=None, wait_timeout=None):
"""Utility function that generates a Senlin cluster.
Create a cluster and return it after it is ACTIVE. The function is used for
deduplicate code in API tests where an 'existing' cluster is needed.
"""
if name is None:
name = data_utils.rand_name("tempest-created-cluster")
params = {
'cluster': {
'profile_id': profile_id,
'desired_capacity': desired_capacity,
'min_size': min_size,
'max_size': max_size,
'timeout': timeout,
'metadata': metadata,
'name': name,
'config': config
}
}
res = base.client.create_obj('clusters', params)
cluster_id = res['body']['id']
action_id = res['location'].split('/actions/')[1]
base.client.wait_for_status('actions', action_id, 'SUCCEEDED',
wait_timeout)
return cluster_id
def update_a_cluster(base, cluster_id, profile_id=None, name=None,
expected_status='SUCCEEDED', metadata=None,
timeout=None, wait_timeout=None):
"""Utility function that updates a Senlin cluster.
Update a cluster and return it after it is ACTIVE.
"""
params = {
'cluster': {
'profile_id': profile_id,
'metadata': metadata,
'name': name,
'timeout': timeout
}
}
res = base.client.update_obj('clusters', cluster_id, params)
action_id = res['location'].split('/actions/')[1]
base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']
def get_a_cluster(base, cluster_id, expected_status=None, wait_timeout=None):
"""Utility function that gets a Senlin cluster."""
if expected_status is None:
res = base.client.get_obj('clusters', cluster_id)
else:
base.client.wait_for_status('clusters', cluster_id, expected_status,
wait_timeout)
res = base.client.get_obj('clusters', cluster_id)
return res['body']
def list_clusters(base):
"""Utility function that lists Senlin clusters."""
res = base.client.list_objs('clusters')
return res['body']
def _return_last_value(retry_state):
return retry_state.outcome.result()
@tenacity.retry(
retry=(tenacity.retry_if_exception_type(exceptions.Conflict) |
tenacity.retry_if_result(lambda x: x is False)),
wait=tenacity.wait_fixed(2),
retry_error_callback=_return_last_value,
stop=tenacity.stop_after_attempt(5)
)
def delete_a_cluster(base, cluster_id, wait_timeout=None):
"""Utility function that deletes a Senlin cluster."""
res = base.client.delete_obj('clusters', cluster_id)
action_id = res['location'].split('/actions/')[1]
action = base.client.wait_for_status(
'actions', action_id, ['SUCCEEDED', 'FAILED'], wait_timeout)
if action['body']['status'] == 'FAILED':
return False
base.client.wait_for_delete("clusters", cluster_id, wait_timeout)
return True
def create_a_node(base, profile_id, cluster_id=None, metadata=None,
role=None, name=None, wait_timeout=None):
"""Utility function that creates a node.
Create a node and return it after it is ACTIVE. This function is for
minimizing the code duplication that could happen in API tests where
an 'existing' Senlin node is needed.
"""
if name is None:
name = data_utils.rand_name("tempest-created-node")
params = {
'node': {
'profile_id': profile_id,
'cluster_id': cluster_id,
'metadata': metadata,
'role': role,
'name': name
}
}
res = base.client.create_obj('nodes', params)
node_id = res['body']['id']
action_id = res['location'].split('/actions/')[1]
base.client.wait_for_status('actions', action_id, 'SUCCEEDED',
wait_timeout)
res = base.client.get_obj('nodes', node_id)
return res['body']['id']
def get_a_node(base, node_id, show_details=False):
"""Utility function that gets a Senlin node."""
params = None
if show_details:
params = {'show_details': True}
res = base.client.get_obj('nodes', node_id, params)
return res['body']
def list_nodes(base):
"""Utility function that lists Senlin nodes."""
res = base.client.list_objs('nodes')
return res['body']
def update_a_node(base, node_id, profile_id=None, name=None,
metadata=None, tainted=None, role=None,
wait_timeout=None):
"""Utility function that updates a Senlin node.
Update a node and return it after it is ACTIVE.
"""
params = {
'node': {
'profile_id': profile_id,
'metadata': metadata,
'name': name,
'role': role
}
}
if tainted is not None:
params['node']['tainted'] = tainted
res = base.client.update_obj('nodes', node_id, params)
action_id = res['location'].split('/actions/')[1]
base.client.wait_for_status('actions', action_id, 'SUCCEEDED',
wait_timeout)
return res['body']['status_reason']
def delete_a_node(base, node_id, wait_timeout=None):
"""Utility function that deletes a Senlin node."""
res = base.client.delete_obj('nodes', node_id)
action_id = res['location'].split('/actions/')[1]
base.client.wait_for_status('actions', action_id, 'SUCCEEDED',
wait_timeout)
return
def create_a_policy(base, spec=None, name=None):
"""Utility function that generates a Senlin policy."""
params = {
'policy': {
'name': name or data_utils.rand_name("tempest-created-policy"),
'spec': spec or constants.spec_scaling_policy
}
}
res = base.client.create_obj('policies', params)
return res['body']['id']
def get_a_policy(base, policy_id):
"""Utility function that gets a Senlin policy."""
res = base.client.get_obj('policies', policy_id)
return res['body']
def delete_a_policy(base, policy_id, ignore_missing=False):
"""Utility function that deletes a policy."""
res = base.client.delete_obj('policies', policy_id)
if res['status'] == 404:
if ignore_missing:
return
raise exceptions.NotFound()
return
def get_a_action(base, action_id):
"""Utility function that gets a Senlin action."""
res = base.client.get_obj('actions', action_id)
return res['body']
def cluster_attach_policy(base, cluster_id, policy_id,
expected_status='SUCCEEDED', wait_timeout=None):
"""Utility function that attach a policy to cluster."""
params = {
'policy_attach': {
'enabled': True,
'policy_id': policy_id
}
}
res = base.client.trigger_action('clusters', cluster_id, params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']['status_reason']
@tenacity.retry(
retry=(tenacity.retry_if_exception_type(exceptions.Conflict) |
tenacity.retry_if_result(lambda x: x is False)),
wait=tenacity.wait_fixed(2),
retry_error_callback=_return_last_value,
stop=tenacity.stop_after_attempt(5)
)
def cluster_detach_policy(base, cluster_id, policy_id,
expected_status='SUCCEEDED', wait_timeout=None):
"""Utility function that detach a policy from cluster."""
params = {
'policy_detach': {
'policy_id': policy_id
}
}
res = base.client.trigger_action('clusters', cluster_id, params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status(
'actions', action_id, ['SUCCEEDED', 'FAILED'], wait_timeout)
if res['body']['status'] == 'FAILED':
return False
return res['body']['status_reason']
def cluster_replace_nodes(base, cluster_id, nodes,
expected_status='SUCCEEDED', wait_timeout=None):
"""Utility function that replace nodes of cluster."""
params = {
'replace_nodes': {
'nodes': nodes
}
}
res = base.client.cluster_replace_nodes('clusters', cluster_id,
params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']['status_reason']
def cluster_add_nodes(base, cluster_id, nodes, expected_status='SUCCEEDED',
wait_timeout=None):
"""Utility function that add nodes to cluster."""
params = {
'add_nodes': {
'nodes': nodes
}
}
res = base.client.trigger_action('clusters', cluster_id, params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']['status_reason']
def cluster_del_nodes(base, cluster_id, nodes, expected_status='SUCCEEDED',
wait_timeout=None):
"""Utility function that delete nodes from cluster."""
params = {
'del_nodes': {
'nodes': nodes
}
}
res = base.client.trigger_action('clusters', cluster_id, params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']['status_reason']
def cluster_scale_out(base, cluster_id, count=None,
expected_status='SUCCEEDED', wait_timeout=None):
"""Utility function that scale out cluster."""
params = {
'scale_out': {
'count': count
}
}
res = base.client.trigger_action('clusters', cluster_id, params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']['status_reason']
def cluster_scale_in(base, cluster_id, count=None,
expected_status='SUCCEEDED', wait_timeout=None):
"""Utility function that scale in cluster."""
params = {
'scale_in': {
'count': count
}
}
res = base.client.trigger_action('clusters', cluster_id, params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']['status_reason'], action_id
def cluster_resize(base, cluster_id, adj_type=None, number=None, min_size=None,
max_size=None, min_step=None, strict=True,
expected_status='SUCCEEDED', wait_timeout=None):
"""Utility function that resize cluster."""
params = {
'resize': {
'adjustment_type': adj_type,
'number': number,
'min_size': min_size,
'max_size': max_size,
'min_step': min_step,
'strict': strict
}
}
res = base.client.trigger_action('clusters', cluster_id, params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']['status_reason']
def cluster_complete_lifecycle(base, cluster_id, lifecycle_action_token,
expected_status='SUCCEEDED', wait_timeout=None):
"""Utility function that completes lifecycle for a cluster."""
params = {
'complete_lifecycle': {
'lifecycle_action_token': lifecycle_action_token
}
}
res = base.client.trigger_action('clusters', cluster_id, params=params)
action_id = res['location'].split('/actions/')[1]
res = base.client.wait_for_status('actions', action_id, expected_status,
wait_timeout)
return res['body']['status_reason']
def create_a_receiver(base, cluster_id, action, r_type=None, name=None,
params=None):
"""Utility function that generates a Senlin receiver."""
if name is None:
name = data_utils.rand_name("tempest-created-receiver")
body = {
'receiver': {
'name': name,
'cluster_id': cluster_id,
'type': r_type or 'webhook',
'params': params or {}
}
}
if action is not None:
body['receiver']['action'] = action
res = base.client.create_obj('receivers', body)
return res['body']['id']
def get_a_receiver(base, receiver_id):
"""Utility function that gets a Senlin receiver."""
res = base.client.get_obj('receivers', receiver_id)
return res['body']
def delete_a_receiver(base, receiver_id, ignore_missing=False):
"""Utility function that deletes a Senlin receiver."""
res = base.client.delete_obj('receivers', receiver_id)
if res['status'] == 404:
if ignore_missing:
return
raise exceptions.NotFound()
def create_a_keypair(base, name=None, is_admin_manager=True):
"""Utility function that creates a Nova keypair."""
if name is None:
name = data_utils.rand_name("tempest-created-keypair")
if is_admin_manager is True:
body = base.os_admin.keypairs_client.create_keypair(name=name)
body = body['keypair']
else:
params = {
"keypair": {
"name": name,
}
}
body = base.compute_client.create_obj('os-keypairs', params)['body']
return body['name']
def delete_a_keypair(base, name, is_admin_manager=True, ignore_missing=False):
"""Utility function that deletes a Nova keypair."""
if is_admin_manager is True:
base.os_admin.keypairs_client.delete_keypair(name)
return
res = base.compute_client.delete_obj('os-keypairs', name)
if res['status'] == 404:
if ignore_missing is True:
return
raise exceptions.NotFound()
def create_a_network(base, name=None):
"""Utility function that creates a Neutron network"""
if name is None:
name = data_utils.rand_name("tempest-created-network")
params = {
"network": {
"name": name,
}
}
body = base.network_client.create_obj('networks', params)
return body['body']['id']
@tenacity.retry(
retry=tenacity.retry_if_exception_type(exceptions.Conflict),
wait=tenacity.wait_fixed(5),
retry_error_callback=_return_last_value,
stop=tenacity.stop_after_attempt(3)
)
def delete_a_network(base, network_id, ignore_missing=False,
wait_timeout=None):
"""Utility function that deletes a Neutron network."""
res = base.network_client.delete_obj('networks', network_id)
if res['status'] == 404:
if ignore_missing:
return
raise exceptions.NotFound()
base.network_client.wait_for_delete('networks', network_id, wait_timeout)
def create_a_subnet(base, network_id, cidr, ip_version=4, name=None):
"""Utility function that creates a Neutron subnet"""
if name is None:
name = data_utils.rand_name("tempest-created-subnet")
params = {
"subnet": {
"name": name,
"network_id": network_id,
"cidr": cidr,
"ip_version": ip_version,
}
}
body = base.network_client.create_obj('subnets', params)
return body['body']['id']
@tenacity.retry(
retry=tenacity.retry_if_exception_type(exceptions.Conflict),
wait=tenacity.wait_fixed(5),
retry_error_callback=_return_last_value,
stop=tenacity.stop_after_attempt(3)
)
def delete_a_subnet(base, subnet_id, ignore_missing=False, wait_timeout=None):
"""Utility function that deletes a Neutron subnet."""
res = base.network_client.delete_obj('subnets', subnet_id)
if res['status'] == 404:
if ignore_missing:
return
raise exceptions.NotFound()
base.network_client.wait_for_delete('subnets', subnet_id, wait_timeout)
def create_queue(base, queue_name):
"""Utility function that creates Zaqar queue."""
res = base.messaging_client.create_queue(queue_name)
if res['status'] != 201 and res['status'] != 204:
msg = 'Failed in creating Zaqar queue %s' % queue_name
raise Exception(msg)
def delete_queue(base, queue_name):
"""Utility function that deletes Zaqar queue."""
res = base.messaging_client.delete_queue(queue_name)
if res['status'] != 204:
msg = 'Failed in deleting Zaqar queue %s' % queue_name
raise Exception(msg)
def list_messages(base, queue_name):
"""Utility function that lists messages in Zaqar queue."""
res = base.messaging_client.list_messages(queue_name)
if res['status'] != 200:
msg = 'Failed in listing messsages for Zaqar queue %s' % queue_name
raise Exception(msg)
return res['body']['messages']
def post_messages(base, queue_name, messages):
"""Utility function that posts message(s) to Zaqar queue."""
res = base.messaging_client.post_messages(queue_name,
{'messages': messages})
if res['status'] != 201:
msg = 'Failed in posting messages to Zaqar queue %s' % queue_name
raise Exception(msg)
def start_http_server(port=5050):
def _get_http_handler_class(filename):
class StaticHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
data = b'healthy\n'
self.send_response(http.OK)
self.send_header('Content-Length', str(len(data)))
self.end_headers()
self.wfile.write(data)
return
def log_message(self, fmt, *args):
msg = fmt % args
with open(filename, 'a+') as tmp:
tmp.write("%s\n" % msg)
return
return StaticHTTPRequestHandler
server_address = ('127.0.0.1', port)
new_file, filename = tempfile.mkstemp()
handler_class = _get_http_handler_class(filename)
httpd = BaseHTTPServer.HTTPServer(server_address, handler_class)
pid = os.fork()
if pid == 0:
httpd.serve_forever()
else:
return pid, filename
def terminate_http_server(pid, filename):
os.kill(pid, signal.SIGKILL)
if not os.path.isfile(filename):
return ''
with open(filename, 'r') as f:
contents = f.read()
os.remove(filename)
return contents

View File

@@ -1,54 +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.
from oslo_config import cfg
service_option = cfg.BoolOpt("senlin",
default=True,
help="Whether or not senlin is expected to be"
"available")
clustering_group = cfg.OptGroup(name="clustering",
title="Clustering Service Options")
ClusteringGroup = [
cfg.StrOpt("catalog_type",
default="clustering",
help="Catalog type of the clustering service."),
cfg.IntOpt("wait_timeout",
default=180,
help="Waiting time for a specific status, in seconds."),
cfg.StrOpt('min_microversion',
default=None,
help="Lower version of the test target microversion range. "
"The format is 'X.Y', where 'X' and 'Y' are int values. "
"Tempest selects tests based on the range between "
"min_microversion and max_microversion. If both values "
"are None, Tempest avoids tests which require a "
"microversion."),
cfg.StrOpt('max_microversion',
default='latest',
help="Upper version of the test target microversion range. "
"The format is 'X.Y'. where 'X' and 'Y' are int values. "
"Tempest selects tests based on the range between "
"microversion and max_microversion. If both values "
"are None, Tempest avoids tests which require a "
"microversion."),
cfg.BoolOpt('delete_with_dependency',
default=False,
help="Enables tests that delete clusters with resources such "
"as policies or receivers attached to it."),
cfg.StrOpt('health_policy_version',
default='1.0',
help='Supported version of the health policy.'),
]

View File

@@ -1,40 +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 senlin_tempest_plugin import config as config_senlin
class SenlinTempestPlugin(plugins.TempestPlugin):
def load_tests(self):
base_path = os.path.split(os.path.dirname(
os.path.abspath(__file__)))[0]
test_dir = "senlin_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(config_senlin.service_option,
group='service_available')
conf.register_group(config_senlin.clustering_group)
conf.register_opts(config_senlin.ClusteringGroup,
group='clustering')
def get_opt_lists(self):
return [(config_senlin.clustering_group.name,
config_senlin.ClusteringGroup),
('service_available', [config_senlin.service_option])]

View File

@@ -1,27 +0,0 @@
#!/bin/bash -xe
# 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.
# This script is executed inside post_test_hook function in devstack gate.
set -ex
export DEST=${DEST:-/opt/stack/new}
export DEVSTACK_DIR=$DEST/devstack
export SENLIN_DIR=$DEST/senlin
source $DEVSTACK_DIR/openrc admin admin
cd $DEST/tempest
echo "Running tempest " $SENLIN_TEST_TYPE "tests"
sudo tox -evenv-tempest -- tempest run --regex $DEVSTACK_GATE_TEMPEST_REGEX

View File

@@ -1,46 +0,0 @@
#!/bin/bash
#
# 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.
# This script is executed inside pre_test_hook function in devstack gate.
set -x
export localconf=$BASE/new/devstack/local.conf
export SENLIN_CONF=/etc/senlin/senlin.conf
export ZAQAR_CONF=/etc/zaqar/zaqar.conf
export SENLIN_BACKEND=${SENLIN_BACKEND:-'openstack_test'}
_LOG_CFG='default_log_levels ='
_LOG_CFG+='amqp=WARN,amqplib=WARN,sqlalchemy=WARN,oslo_messaging=WARN'
_LOG_CFG+=',iso8601=WARN,requests.packages.urllib3.connectionpool=WARN'
_LOG_CFG+=',urllib3.connectionpool=WARN'
_LOG_CFG+=',requests.packages.urllib3.util.retry=WARN,urllib3.util.retry=WARN'
_LOG_CFG+=',keystonemiddleware=WARN'
_LOG_CFG+=',routes.middleware=WARN'
_LOG_CFG+=',stevedore=WARN'
_LOG_CFG+=',oslo_messaging._drivers.amqp=WARN'
_LOG_CFG+=',oslo_messaging._drivers.amqpdriver=WARN'
echo -e '[[post-config|$SENLIN_CONF]]\n[DEFAULT]\n' >> $localconf
echo -e 'num_engine_workers=2\n' >> $localconf
echo -e "cloud_backend=$SENLIN_BACKEND\n" >> $localconf
echo -e "health_check_interval_min=10\n" >> $localconf
echo -e $_LOG_CFG >> $localconf
if [[ "$SENLIN_BACKEND" == "openstack" ]]; then
echo -e "[[post-config|$ZAQAR_CONF]]\n[DEFAULT]\n" >> $localconf
echo -e "auth_strategy=keystone\n" >> $localconf
echo -e "[storage]\n" >> $localconf
echo -e "message_pipeline=zaqar.notification.notifier" >> $localconf
fi

View File

@@ -1,56 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestActionList(base.BaseSenlinAPITest):
def setUp(self):
super(TestActionList, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, cluster_id)
@decorators.idempotent_id('2e47639b-7f58-4fb4-a147-a8c6bf184e97')
def test_action_list(self):
res = self.client.list_objs('actions')
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
actions = res['body']
for action in actions:
for key in ['action', 'cause', 'created_at', 'data',
'depended_by', 'depends_on', 'end_time', 'id',
'inputs', 'interval', 'name', 'outputs', 'owner',
'start_time', 'status', 'status_reason', 'target',
'timeout', 'updated_at']:
self.assertIn(key, action)
@decorators.idempotent_id('7cf0b29f-c799-4bac-9e36-57d10f222086')
def test_action_list_by_status(self):
res = self.client.list_objs(
'actions', params={'status': 'SUCCEEDED'})
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
actions = res['body']
if res['body']:
for action in actions:
self.assertEqual('SUCCEEDED', action['status'])

View File

@@ -1,86 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestActionListNegativeBadRequest(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('2f857bee-99a8-4881-bde9-5909e3ff121a')
def test_action_list_invalid_params(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'actions', {'bogus': 'foo'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid parameter bogus", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('6b93221e-ca14-443b-be54-4423a636672a')
def test_action_list_limit_not_int(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'actions', {'limit': 'not-int'})
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for limit must be an integer: 'not-int'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d13e3011-32b5-4f9d-bb57-3532c4c46228')
def test_action_list_global_project_false(self):
ex = self.assertRaises(exceptions.Forbidden,
self.client.list_objs,
'actions', {'global_project': 'True'})
message = ex.resp_body['error']['message']
self.assertEqual("You are not authorized to complete this operation.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d7804b5e-6efd-4084-adec-531d0a8399dc')
def test_action_list_global_project_not_bool(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'actions', {'global_project': 'not-bool'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid value 'not-bool' specified for "
"'global_project'", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('6ca48051-6030-4333-9483-074e03da5ba0')
def test_action_list_invalid_sort(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'actions', {'sort': 'bad-sort'})
message = ex.resp_body['error']['message']
self.assertEqual("Unsupported sort key 'bad-sort' for 'sort'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('588517ed-bfcf-4d92-9d7c-5ed11fc2c2ee')
def test_action_list_invalid_marker(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'actions', {'marker': 'bad-marker'})
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for marker is not a valid UUID: 'bad-marker'.",
str(message))

View File

@@ -1,69 +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 time
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestActionShow(base.BaseSenlinAPITest):
def setUp(self):
super(TestActionShow, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.lower_time_bound = time.time()
params = {
'cluster': {
'profile_id': profile_id,
'desired_capacity': 0,
'min_size': 0,
'max_size': -1,
'timeout': None,
'metadata': {},
'name': 'test-cluster-action-show'
}
}
res = self.client.create_obj('clusters', params)
self.action_id = res['location'].split('/actions/')[1]
self.addCleanup(utils.delete_a_cluster, self, res['body']['id'])
self.client.wait_for_status('actions', self.action_id, 'SUCCEEDED')
self.upper_time_bound = time.time()
@decorators.idempotent_id('c6376f60-8f52-4384-8b6d-57df264f2e23')
def test_action_show(self):
res = self.client.get_obj('actions', self.action_id)
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
action = res['body']
for key in ['action', 'cause', 'created_at', 'data',
'depended_by', 'depends_on', 'end_time', 'id',
'inputs', 'interval', 'name', 'outputs', 'owner',
'start_time', 'status', 'status_reason', 'target',
'timeout', 'updated_at']:
self.assertIn(key, action)
for key in ['start_time', 'end_time']:
self.assertTrue(action[key] > self.lower_time_bound,
'{} is smaller than {}'.format(
action[key], self.lower_time_bound))
self.assertTrue(action[key] < self.upper_time_bound,
'{} is larger than {}'.format(
action[key], self.upper_time_bound))

View File

@@ -1,31 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestActionShowNegativeNotFound(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('26cb0f3a-4e6c-49f6-8475-7cb472933bff')
def test_action_show_not_found(self):
ex = self.assertRaises(
exceptions.NotFound, self.client.get_obj,
'actions', '26cb0f3a-4e6c-49f6-8475-7cb472933bff')
message = ex.resp_body['error']['message']
self.assertEqual(
"The action '26cb0f3a-4e6c-49f6-8475-7cb472933bff' could "
"not be found.", str(message))

View File

@@ -1,68 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestActionUpdateNegative(base.BaseSenlinAPITest):
def setUp(self):
super(TestActionUpdateNegative, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
params = {
'cluster': {
'profile_id': profile_id,
'desired_capacity': 0,
'min_size': 0,
'max_size': -1,
'timeout': None,
'metadata': {},
'name': 'test-cluster-action-show'
}
}
res = self.client.create_obj('clusters', params)
self.action_id = res['location'].split('/actions/')[1]
self.addCleanup(utils.delete_a_cluster, self, res['body']['id'])
self.client.wait_for_status('actions', self.action_id, 'SUCCEEDED')
@utils.api_microversion('1.12')
@decorators.attr(type=['negative'])
@decorators.idempotent_id('72a123c3-cbce-4452-bb53-d3c939116c2f')
def test_action_update_not_found(self):
params = {
"action": {
"status": "CANCELLED",
}
}
self.assertRaises(
exceptions.NotFound, self.client.update_obj, 'actions',
'26cb0f3a-4e6c-49f6-8475-7cb472933bff', params)
@utils.api_microversion('1.12')
@decorators.attr(type=['negative'])
@decorators.idempotent_id('802b24bc-7004-4b25-8b53-a3709e0ad6ed')
def test_action_update_status_invalid(self):
params = {
"action": {
"status": "invalid",
}
}
self.assertRaises(
exceptions.BadRequest, self.client.update_obj, 'actions',
self.action_id, params)

View File

@@ -1,31 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.tests.api import base
class TestAPIVersionShow(base.BaseSenlinAPITest):
@decorators.idempotent_id('4a270caa-9917-4acd-98ef-6636f9618037')
def test_API_version_show(self):
resp, body = self.client.request('GET', '/v1/')
res = self.client.get_resp(resp, body)
# Verify resp of API version show
self.assertEqual(200, res['status'])
self.assertIsNotNone(res['body'])
version = res['body']
for key in ['id', 'links', 'media-types', 'status', 'updated']:
self.assertIn(key, version)
self.assertEqual('1.0', version['id'])

View File

@@ -1,26 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestAPIVersionShowNegative(base.BaseSenlinAPITest):
@decorators.idempotent_id('a3b02638-2459-41ab-a70b-a6f1a269914e')
def test_API_version_show_invalid_version(self):
resp, body = self.client.request('GET', '/v1/')
self.assertRaises(exceptions.NotFound,
self.client.request,
'GET', '/vx')

View File

@@ -1,35 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.tests.api import base
class TestAPIVersions(base.BaseSenlinAPITest):
@decorators.idempotent_id('c7378a80-9a82-4148-937d-25e046c6316f')
def test_API_versions_list(self):
resp, body = self.client.request('GET', '/')
res = self.client.get_resp(resp, body)
# Verify resp of API versions list
self.assertEqual(300, res['status'])
self.assertIsNotNone(res['body'])
versions = res['body']
for version in versions:
for key in ['id', 'links', 'max_version', 'media-types',
'min_version', 'status', 'updated']:
self.assertIn(key, version)
# Only version 1.0 API is now supported
self.assertEqual(1, len(versions))
self.assertEqual('1.0', versions[0]['id'])

View File

@@ -1,62 +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.
from tempest import config
from senlin_tempest_plugin import base
from senlin_tempest_plugin.common import clustering_client
from senlin_tempest_plugin.common import compute_client
from senlin_tempest_plugin.common import network_client
CONF = config.CONF
class BaseSenlinAPITest(base.BaseSenlinTest):
@classmethod
def setup_credentials(cls):
# Set no network resources for test accounts
cls.set_network_resources()
super(BaseSenlinAPITest, cls).setup_credentials()
@classmethod
def setup_clients(cls):
super(BaseSenlinAPITest, cls).setup_clients()
cls.client = clustering_client.ClusteringAPIClient(
cls.os_primary.auth_provider,
CONF.clustering.catalog_type,
CONF.identity.region,
**cls.default_params_with_timeout_values
)
cls.admin_client = clustering_client.ClusteringAPIClient(
cls.os_admin.auth_provider,
CONF.clustering.catalog_type,
CONF.identity.region,
**cls.default_params_with_timeout_values
)
cls.compute_client = compute_client.V21ComputeClient(
cls.os_primary.auth_provider,
CONF.compute.catalog_type,
CONF.identity.region,
**cls.default_params_with_timeout_values
)
cls.network_client = network_client.NetworkClient(
cls.os_primary.auth_provider,
CONF.network.catalog_type,
CONF.identity.region,
**cls.default_params_with_timeout_values
)

View File

@@ -1,29 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.tests.api import base
class TestBuildInfo(base.BaseSenlinAPITest):
@decorators.idempotent_id('bf7a8bdf-d896-49ff-a7a8-7c8fdbfc3667')
def test_build_info_get(self):
uri = '{0}/build-info'.format(self.client.version)
resp, info = self.client.get(uri)
# Verify resp of get build-info API
self.assertEqual(200, int(resp['status']))
self.assertIsNotNone(info)
for key in ['api', 'engine']:
self.assertIn(key, str(info))

View File

@@ -1,49 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterPolicyList(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyList, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
utils.cluster_attach_policy(self, self.cluster_id, self.policy_id)
self.addCleanup(utils.cluster_detach_policy, self, self.cluster_id,
self.policy_id)
@decorators.idempotent_id('ebaeedcb-7198-4997-9b9c-a8f1eccfc2a6')
def test_cluster_policy_list(self):
res = self.client.list_cluster_policies(self.cluster_id)
# Verify resp of cluster policy list API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
policies = res['body']
self.assertEqual(1, len(policies))
for key in ['cluster_id', 'cluster_name', 'enabled', 'id',
'policy_id', 'policy_name', 'policy_type']:
self.assertIn(key, policies[0])
self.assertEqual(self.policy_id, policies[0]['policy_id'])

View File

@@ -1,66 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestClusterPolicyListNegativeBadRequest(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('7f23de64-60c4-456e-9e24-db86ac89480c')
def test_cluster_policy_list_invalid_params(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_cluster_policies,
'7f23de64-60c4-456e-9e24-db86ac89480c',
{'bogus': 'foo'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid parameter bogus", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0259cbac-0fb3-480b-8f23-1ec59616f3af')
def test_cluster_policy_list_cluster_not_found(self):
ex = self.assertRaises(exceptions.NotFound,
self.client.list_cluster_policies,
'0259cbac-0fb3-480b-8f23-1ec59616f3af')
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster '0259cbac-0fb3-480b-8f23-1ec59616f3af' "
"could not be found.", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('958c6fd2-a647-4b5a-8133-8b4232267b40')
def test_cluster_policy_list_invalid_sort(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_cluster_policies,
'958c6fd2-a647-4b5a-8133-8b4232267b40',
{'sort': 'bad-sort'})
message = ex.resp_body['error']['message']
self.assertEqual("Unsupported sort key 'bad-sort' for 'sort'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0e59c1c7-7477-45ee-8f12-7e5b87373bf7')
def test_cluster_policy_list_enabled_not_bool(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_cluster_policies,
'0e59c1c7-7477-45ee-8f12-7e5b87373bf7',
{'enabled': 'bad-enabled'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid value 'bad-enabled' specified for 'enabled'",
str(message))

View File

@@ -1,48 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterPolicyShow(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyShow, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
utils.cluster_attach_policy(self, self.cluster_id, self.policy_id)
self.addCleanup(utils.cluster_detach_policy, self, self.cluster_id,
self.policy_id)
@decorators.idempotent_id('fdf4dbf9-fcc6-4eb0-96c1-d8e8caa90f6d')
def test_cluster_policy_show(self):
res = self.client.get_cluster_policy(self.cluster_id, self.policy_id)
# Verify resp of cluster policy show API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
policy = res['body']
for key in ['cluster_id', 'cluster_name', 'id', 'policy_id',
'policy_name', 'policy_type', 'enabled']:
self.assertIn(key, policy)
self.assertEqual(self.policy_id, policy['policy_id'])

View File

@@ -1,117 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterPolicyShowNegativeClusterNotFound(base.BaseSenlinAPITest):
# @decorators.attr(type=['negative'])
@decorators.idempotent_id('965d7324-e3f7-4d77-8e7f-44c862b851f7')
def test_cluster_policy_show_cluster_not_found(self):
ex = self.assertRaises(exceptions.NotFound,
self.client.get_cluster_policy,
'965d7324-e3f7-4d77-8e7f-44c862b851f7',
'POLICY_ID')
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster '965d7324-e3f7-4d77-8e7f-44c862b851f7' "
"could not be found.", str(message))
class TestClusterPolicyShowNegativePolicyNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyShowNegativePolicyNotFound, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('e3e24058-ed07-42b6-b47c-a972c6047509')
def test_cluster_policy_show_policy_not_found(self):
ex = self.assertRaises(exceptions.NotFound,
self.client.get_cluster_policy,
self.cluster_id,
'fake_policy')
message = ex.resp_body['error']['message']
self.assertEqual("The policy 'fake_policy' could not be found.",
str(message))
class TestClusterPolicyShowNegativeNoPolicyBinding(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyShowNegativeNoPolicyBinding, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('9c9e01fc-dfb9-4a27-9a06-f4d6de2b2d1c')
def test_cluster_policy_show_no_policy_binding(self):
ex = self.assertRaises(exceptions.NotFound,
self.client.get_cluster_policy,
self.cluster_id, self.policy_id)
message = ex.resp_body['error']['message']
self.assertEqual(
"The policy '%s' is not found attached to the specified cluster "
"'%s'." % (self.policy_id, self.cluster_id), str(message))
class TestClusterPolicyShowNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyShowNegativeBadRequest, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id1 = utils.create_a_policy(self, name='test-policy')
self.addCleanup(utils.delete_a_policy, self, self.policy_id1)
self.policy_id2 = utils.create_a_policy(self, name='test-policy')
self.addCleanup(utils.delete_a_policy, self, self.policy_id2)
utils.cluster_attach_policy(self, self.cluster_id, self.policy_id1)
self.addCleanup(utils.cluster_detach_policy, self, self.cluster_id,
self.policy_id1)
utils.cluster_attach_policy(self, self.cluster_id, self.policy_id2)
self.addCleanup(utils.cluster_detach_policy, self, self.cluster_id,
self.policy_id2)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('3faaaf65-0606-4853-a660-2bb04f10485b')
def test_cluster_policy_show_multiple_choice(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.get_cluster_policy,
self.cluster_id, 'test-policy')
message = ex.resp_body['error']['message']
self.assertEqual(
"Multiple results found matching the query criteria "
"'test-policy'. Please be more specific.", str(message))

View File

@@ -1,64 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestClusterActionNegativeCommon(base.BaseSenlinAPITest):
@decorators.idempotent_id('9c972d49-81bd-4448-9afc-b93053aa835d')
def test_cluster_action_no_action_specified(self):
# No action is specified
params = {}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("No action specified", str(message))
@decorators.idempotent_id('997d2e19-7914-4883-9b6a-86e907898d3b')
def test_cluster_action_multiple_action_specified(self):
# Multiple actions are specified
params = {
'resize': {},
'check': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Multiple actions specified", str(message))
@decorators.idempotent_id('43e142ac-9579-40d9-845a-b8190691b91a')
def test_cluster_action_unrecognized_action(self):
# Unrecognized action is specified
params = {
'bogus': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Unrecognized action 'bogus' specified",
str(message))

View File

@@ -1,268 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionAddNodes(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionAddNodes, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.node_id = utils.create_a_node(self, self.profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.idempotent_id('db0faadf-9cd2-457f-b434-4891b77938ab')
def test_cluster_action_add_nodes(self):
params = {
"add_nodes": {
"nodes": [
self.node_id
]
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterAddNodesNegativeInvalidNodesParams(base.BaseSenlinAPITest):
@decorators.idempotent_id('912bb24d-73e1-4801-a6de-bdd453cbbdbf')
def test_cluster_add_nodes_missing_nodes_params(self):
params = {
'add_nodes': {
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Value for 'nodes' must have at least 1 item(s).",
str(message))
@decorators.idempotent_id('6cb029f7-9b72-4f10-a28b-3ed5bd3ed7b0')
def test_cluster_add_nodes_params_not_list(self):
params = {
'add_nodes': {
'nodes': 'node_id'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Items for 'nodes' must be unique", str(message))
@decorators.idempotent_id('b8ae9b5f-967f-48a6-8e31-c77f86ba06aa')
def test_cluster_add_nodes_params_empty_list(self):
params = {
'add_nodes': {
'nodes': []
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Value for 'nodes' must have at least 1 item(s).",
str(message))
@decorators.idempotent_id('a97f1712-46ba-43c1-abd6-12c209c9d640')
def test_cluster_add_mult_node_with_same_name(self):
params = {
'add_nodes': {
'nodes': ['id1', 'id1']
}
}
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Items for 'nodes' must be unique",
str(message))
class TestClusterAddNodesNegativeNodeNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterAddNodesNegativeNodeNotFound, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('5ddf7e5a-3f67-4f1e-af1e-c5a7da319dc0')
def test_cluster_add_nodes_node_not_found(self):
params = {
'add_nodes': {
'nodes': ['5ddf7e5a-3f67-4f1e-af1e-c5a7da319dc0']
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertIn("Nodes not found", str(message))
class TestClusterAddNodesNegativeNodeNotOrphan(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterAddNodesNegativeNodeNotOrphan, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.cluster_id2 = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id2)
self.node_id = utils.create_a_node(self, self.profile_id,
cluster_id=self.cluster_id2)
@decorators.idempotent_id('08e1271c-025e-4670-a20e-4a96fa179dca')
def test_cluster_add_nodes_node_not_orphan(self):
params = {
'add_nodes': {
'nodes': [self.node_id]
}
}
# Verify conflict exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Nodes ['%s'] already owned by some cluster." % self.node_id,
str(message))
class TestClusterAddNodesNegativeProfileTypeUnmatch(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterAddNodesNegativeProfileTypeUnmatch, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.profile_id2 = utils.create_a_profile(
self, spec=constants.spec_heat_stack)
self.addCleanup(utils.delete_a_profile, self, self.profile_id2)
self.node_id = utils.create_a_node(self, self.profile_id2)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.idempotent_id('7a27c697-6d29-46f0-8b2a-3a7282c15b33')
def test_cluster_add_nodes_profile_type_unmatch(self):
params = {
'add_nodes': {
'nodes': [
self.node_id
]
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Profile type of nodes ['%s'] does not match that of the "
"cluster." % self.node_id, str(message))
class TestClusterAddNodesNegativeSizeCheckFailed(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterAddNodesNegativeSizeCheckFailed, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id,
desired_capacity=1,
max_size=1)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.node_id = utils.create_a_node(self, self.profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.idempotent_id('3b485352-53e3-481d-b471-9a042c76d758')
def test_cluster_add_nodes_cluster_size_check_failed(self):
params = {
'add_nodes': {
'nodes': [
self.node_id
]
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The target capacity (2) is greater than the cluster's "
"max_size (1).", str(message))
class TestClusterAddNodesNegativeClusterNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('22f10d36-c29a-4cde-a975-af262a5775a1')
def test_cluster_add_nodes_cluster_not_found(self):
params = {
'add_nodes': {
'nodes': ['node_id']
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'db0faadf-9cd2-457f-b434-4891b77938ab', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster 'db0faadf-9cd2-457f-b434-4891b77938ab' "
"could not be found.", str(message))

View File

@@ -1,79 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionCheck(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionCheck, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id,
desired_capacity=1)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('0d2063a8-b3d3-489d-b949-e2c61a0997ee')
def test_cluster_action_check(self):
params = {
"check": {}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterCheckNegativeInvalidParams(base.BaseSenlinAPITest):
@decorators.idempotent_id('9a305b4f-2f05-4aa9-95ae-e08fd24b0593')
def test_cluster_check_params_not_dict(self):
params = {
'check': ['k1', 'v1']
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("The data provided is not a map", str(message))
class TestClusterCheckNegativeNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('bbbe3feb-8482-4ae4-9c29-b4732efce931')
def test_cluster_check_cluster_not_found(self):
params = {
'check': {'k1': 'v1'}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'bbbe3feb-8482-4ae4-9c29-b4732efce931', params)
message = ex.resp_body['error']['message']
self.assertEqual("The cluster 'bbbe3feb-8482-4ae4-9c29-b4732efce931' "
"could not be found.", str(message))

View File

@@ -1,97 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterCollect(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterCollect, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(
self, profile_id, config={'node.name.format': 'N$2I'})
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.node1 = utils.create_a_node(self, profile_id, name='N01',
cluster_id=self.cluster_id)
self.addCleanup(utils.delete_a_node, self, self.node1)
self.node2 = utils.create_a_node(self, profile_id, name='N02',
cluster_id=self.cluster_id)
self.addCleanup(utils.delete_a_node, self, self.node2)
@utils.api_microversion('1.2')
@decorators.idempotent_id('00c389f8-3e83-4171-bd72-2202f1f3954d')
def test_cluster_collect(self):
# Collect on a basic path
res = self.client.cluster_collect(self.cluster_id, path='name')
self.assertEqual(200, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn({u'id': self.node1, u'value': u'N01'}, res['body'])
self.assertIn({u'id': self.node2, u'value': u'N02'}, res['body'])
@utils.api_microversion('1.2')
@decorators.idempotent_id('1ec4ca67-20db-43eb-95af-1301aa2b1948')
def test_cluster_collect_details(self):
# Collect on a basic path
res = self.client.cluster_collect(self.cluster_id,
path='details.status')
self.assertEqual(200, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn({u'id': self.node1, u'value': u'ACTIVE'}, res['body'])
self.assertIn({u'id': self.node2, u'value': u'ACTIVE'}, res['body'])
class TestClusterCollectNegative(base.BaseSenlinAPITest):
@utils.api_microversion('1.1')
@decorators.idempotent_id('88fff27d-27eb-4a4d-906b-b3a9761072ba')
def test_cluster_collect_failed_api_version(self):
# Collect on a basic path
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_collect,
'FAKE_CLUSTER', path='name')
message = ex.resp_body['error']['message']
self.assertEqual("API version '1.1' is not supported on this method.",
str(message))
@decorators.idempotent_id('1b080a66-9c76-4321-9d88-74e38eb44df7')
@utils.api_microversion('1.2')
def test_cluster_collect_failed_path(self):
# Collect on a basic path
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_collect,
'FAKE_CLUSTER', path='None')
message = ex.resp_body['error']['message']
self.assertEqual("Required path attribute is missing.",
str(message))
@utils.api_microversion('1.2')
@decorators.idempotent_id('a3d59666-93be-47ee-a484-c248ed2f49fe')
def test_cluster_collect_cluster_not_found(self):
# Collect on a basic path
ex = self.assertRaises(exceptions.NotFound,
self.client.cluster_collect,
'a3d59666-93be-47ee-a484-c248ed2f49fe',
path='name')
message = ex.resp_body['error']['message']
self.assertEqual("The cluster 'a3d59666-93be-47ee-a484-c248ed2f49fe' "
"could not be found.", str(message))

View File

@@ -1,63 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionCompleteLifecycle(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionCompleteLifecycle, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id,
desired_capacity=1)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('d1338e7d-c954-4e1f-8601-dae0709e21fb')
def test_cluster_action_complete_lifecycle(self):
params = {
"scale_out": {
"count": "1"
}
}
# call cluster scale out to get a valid action id
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
# use scale out action id to call complete_lifecycle
# since a valid action id is needed
params = {
"complete_lifecycle": {
"lifecycle_action_token": action_id
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')

View File

@@ -1,74 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterCreate(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterCreate, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# TODO(Anyone): The following line is an example to use api_microversion
# decorator. It is not necessary for this test case. Remove it after any
# usage of api_microversion decorator is added.
@utils.api_microversion('1.0')
@decorators.idempotent_id('61cbe340-937a-40d5-9d2f-067f2c7cafcc')
def test_cluster_create_all_attrs_defined(self):
# Create cluster
name = 'test-cluster'
desired_capacity = 2
min_size = 1
max_size = 3
metadata = {'k1': 'v1'}
timeout = 120
params = {
'cluster': {
'profile_id': self.profile_id,
'desired_capacity': desired_capacity,
'min_size': min_size,
'max_size': max_size,
'timeout': timeout,
'metadata': {'k1': 'v1'},
'name': name
}
}
res = self.client.create_obj('clusters', params)
# Verify resp of cluster create API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
cluster = res['body']
for key in ['created_at', 'data', 'domain', 'id', 'init_at', 'nodes',
'policies', 'profile_id', 'profile_name', 'project',
'status', 'status_reason', 'updated_at', 'user']:
self.assertIn(key, cluster)
self.assertIn(name, cluster['name'])
self.assertEqual(desired_capacity, cluster['desired_capacity'])
self.assertEqual(min_size, cluster['min_size'])
self.assertEqual(max_size, cluster['max_size'])
self.assertEqual(metadata, cluster['metadata'])
self.assertEqual(timeout, cluster['timeout'])
# Wait cluster to be active before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
self.addCleanup(utils.delete_a_cluster, self, cluster['id'])

View File

@@ -1,111 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterCreateNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterCreateNegativeBadRequest, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
@decorators.attr(type=['negative'])
@decorators.idempotent_id('498a06eb-8c5f-4d9e-852f-87ac295f1a96')
def test_cluster_create_cluster_data_not_specified(self):
# cluster key is missing in request data
params = {
'bad': {
'profile_id': self.profile_id,
'name': 'test-cluster'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'clusters', params)
message = ex.resp_body['error']['message']
self.assertEqual("Request body missing 'cluster' key.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('3aced30b-ccb2-4e40-90c2-7b6aa205e3d6')
def test_cluster_create_profile_invalid(self):
# Invalid profile_id is provided
params = {
'cluster': {
'profile_id': '3aced30b-ccb2-4e40-90c2-7b6aa205e3d6',
'name': 'test-cluster'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'clusters', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified profile '3aced30b-ccb2-4e40-90c2-7b6aa205e3d6' "
"could not be found.", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('61b190bb-ef5a-47b3-acbe-6467fbb44439')
def test_cluster_create_miss_profile(self):
# Invalid profile_id is provided
params = {
'cluster': {
'name': 'test-cluster'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'clusters', params)
message = ex.resp_body['error']['message']
self.assertEqual("'profile_id' is a required property",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('7eaf60c3-f33d-403b-a4ee-0276ae90928c')
def test_cluster_create_size_constraint_illegal(self):
# Invalid size limitation is defined: min_size > max_size
desired_capacity = 2
min_size = 5
max_size = 3
params = {
'cluster': {
'profile_id': self.profile_id,
'desired_capacity': desired_capacity,
'min_size': min_size,
'max_size': max_size,
'name': 'test-cluster'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'clusters', params)
message = ex.resp_body['error']['message']
self.assertEqual("The target capacity (2) is less than the "
"specified min_size (5).", str(message))

View File

@@ -1,263 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionDelNodes(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionDelNodes, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.node_id = utils.create_a_node(self, profile_id,
cluster_id=self.cluster_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.idempotent_id('ab4e0738-98f9-4521-a4a9-81ed151b4c71')
def test_cluster_action_del_nodes(self):
params = {
"del_nodes": {
"nodes": [
self.node_id
]
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterDelNodesNegativeInvalidNodesParams(base.BaseSenlinAPITest):
@decorators.idempotent_id('89af1e6f-17b4-4c6f-ae5f-91a2da784c05')
def test_cluster_del_nodes_missing_nodes_params(self):
params = {
'del_nodes': {
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Value for 'nodes' must have at least 1 item(s).",
str(message))
@decorators.idempotent_id('c15c387c-3c3c-4005-8818-8fc0cdbfe679')
def test_cluster_del_nodes_params_not_list(self):
params = {
'del_nodes': {
'nodes': 'node_id'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Items for 'nodes' must be unique",
str(message))
@decorators.idempotent_id('662c548d-a89a-40e3-a238-72b2193e5dc2')
def test_cluster_del_nodes_params_empty_list(self):
params = {
'del_nodes': {
'nodes': []
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Value for 'nodes' must have at least 1 item(s).",
str(message))
@decorators.idempotent_id('26479afe-b8bf-4389-85b2-cccf6b83d2b7')
def test_cluster_del_nodes_destroy_not_bool(self):
params = {
'del_nodes': {
'nodes': ['fake_id'],
'destroy_after_deletion': 'not_bool'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Unrecognized value 'not_bool', acceptable values are: '0', '1', "
"'f', 'false', 'n', 'no', 'off', 'on', 't', 'true', 'y', 'yes'",
str(message))
class TestClusterDelNodesNegativeNodeNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterDelNodesNegativeNodeNotFound, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('e896948b-44c0-4dfa-9466-407391504833')
def test_cluster_del_nodes_node_not_found(self):
params = {
'del_nodes': {
'nodes': ['e896948b-44c0-4dfa-9466-407391504833']
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertIn("Nodes not found", str(message))
class TestClusterDelNodesNegativeOrphanNode(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterDelNodesNegativeOrphanNode, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.node_id = utils.create_a_node(self, self.profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.idempotent_id('d7e77bd2-2a80-4995-b2e8-e4e9c58b3de5')
def test_cluster_del_nodes_orphan_node(self):
params = {
'del_nodes': {
'nodes': [self.node_id]
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual("Nodes not members of specified cluster: "
"['%s']." % self.node_id, str(message))
class TestClusterDelNodesNegativeNodeOfOtherCluster(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterDelNodesNegativeNodeOfOtherCluster, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.cluster_id2 = utils.create_a_cluster(self, self.profile_id)
self.node_id = utils.create_a_node(self, self.profile_id,
cluster_id=self.cluster_id2)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id2)
@decorators.idempotent_id('e83135af-1d42-4a80-a039-7e78cbc7e3f4')
def test_cluster_del_nodes_node_of_other_cluster(self):
params = {
'del_nodes': {
'nodes': [self.node_id]
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual("Nodes not members of specified cluster: "
"['%s']." % self.node_id, str(message))
class TestClusterDelNodesNegativeSizeCheckFailed(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterDelNodesNegativeSizeCheckFailed, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id,
desired_capacity=1,
min_size=1)
self.cluster = utils.get_a_cluster(self, self.cluster_id)
self.node_id = self.cluster['nodes'][0]
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('b83bdeac-8d23-46ec-9c50-8c47140982a4')
def test_cluster_del_nodes_cluster_size_check_failed(self):
params = {
'del_nodes': {
'nodes': [
self.node_id
]
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual("The target capacity (0) is less than the cluster's "
"min_size (1).", str(message))
class TestClusterDelNodesNegativeClusterNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('dc8f106a-10b7-47a3-8494-c86035207351')
def test_cluster_del_nodes_cluster_not_found(self):
params = {
'del_nodes': {
'nodes': ['node_id']
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'dc8f106a-10b7-47a3-8494-c86035207351', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster 'dc8f106a-10b7-47a3-8494-c86035207351' could not "
"be found.", str(message))

View File

@@ -1,100 +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.
from tempest import config
from tempest.lib import decorators
import testtools
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
CONF = config.CONF
class TestClusterDelete(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterDelete, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
# cluster will be deleted by test case
self.cluster_id = utils.create_a_cluster(self, profile_id)
@decorators.idempotent_id('33d7426e-0138-42c9-9ab4-ba796a7d1fdc')
def test_cluster_delete_in_active_status(self):
res = self.client.delete_obj('clusters', self.cluster_id)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIsNone(res['body'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterDeleteWithPolicy(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterDeleteWithPolicy, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, policy_id)
utils.cluster_attach_policy(self, self.cluster_id, policy_id)
@decorators.idempotent_id('e563b05d-6b7f-4207-a7ac-e48e6607d4d8')
@testtools.skipUnless(CONF.clustering.delete_with_dependency,
'Deleting clusters with dependancies not enabled')
def test_cluster_delete_policy(self):
res = self.client.delete_obj('clusters', self.cluster_id)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIsNone(res['body'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterDeleteReceiver(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterDeleteReceiver, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.receiver_id = utils.create_a_receiver(
self, self.cluster_id, 'CLUSTER_SCALE_OUT', 'webhook',
'fake', params={'count': '1'})
@decorators.idempotent_id('bff84a28-1b81-42f2-ae88-42f50c9f0bb9')
@testtools.skipUnless(CONF.clustering.delete_with_dependency,
'Deleting clusters with dependancies not enabled')
def test_cluster_delete_receiver(self):
res = self.client.delete_obj('clusters', self.cluster_id)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIsNone(res['body'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')

View File

@@ -1,62 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterDeleteNegativeNotFound(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('8a583b8e-eeaa-4920-a6f5-2880b070624f')
def test_cluster_delete_not_found(self):
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.delete_obj,
'clusters',
'8a583b8e-eeaa-4920-a6f5-2880b070624f')
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster '8a583b8e-eeaa-4920-a6f5-2880b070624f' could "
"not be found.", str(message))
class TestClusterDeleteNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterDeleteNegativeBadRequest, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id1 = utils.create_a_cluster(self, profile_id,
name='c-01')
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id1)
self.cluster_id2 = utils.create_a_cluster(self, profile_id,
name='c-01')
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id2)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('3d8b73db-e2d2-42c2-952c-936048d36f21')
def test_cluster_delete_multiple_choice(self):
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.delete_obj,
'clusters', 'c-01')
message = ex.resp_body['error']['message']
self.assertEqual(
"Multiple results found matching the query criteria 'c-01'. "
"Please be more specific.", str(message))

View File

@@ -1,48 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterList(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterList, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('943cdc02-81bd-4200-a08d-bc1932d932f7')
def test_cluster_list(self):
res = self.client.list_objs('clusters')
# Verify resp of cluster list API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
clusters = res['body']
cluster_ids = []
for cluster in clusters:
for key in ['created_at', 'data', 'desired_capacity', 'domain',
'id', 'init_at', 'max_size', 'metadata', 'min_size',
'name', 'nodes', 'policies', 'profile_id',
'profile_name', 'project', 'status', 'status_reason',
'timeout', 'updated_at', 'user']:
self.assertIn(key, cluster)
cluster_ids.append(cluster['id'])
self.assertIn(self.cluster_id, cluster_ids)

View File

@@ -1,96 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestClusterListNegativeBadRequest(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('b18c2b98-0dcf-489f-8245-080db10298d8')
def test_cluster_list_invalid_params(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'clusters', {'bogus': 'foo'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid parameter 'bogus'", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f6687ce2-288c-42fa-8132-e0faec813129')
def test_cluster_list_limit_not_int(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'clusters', {'limit': 'not-int'})
message = ex.resp_body['error']['message']
self.assertEqual("The value for limit must be an integer: 'not-int'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('32a9f3ad-0284-4fe7-b6e8-d98915637c7f')
def test_cluster_list_global_project_false(self):
ex = self.assertRaises(exceptions.Forbidden,
self.client.list_objs,
'clusters', {'global_project': 'True'})
message = ex.resp_body['error']['message']
self.assertEqual("You are not authorized to complete this operation.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('2944d7dd-72d5-4dd0-9f3e-6f5c6ead8b30')
def test_cluster_list_global_project_not_bool(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'clusters', {'global_project': 'not-bool'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid value 'not-bool' specified for "
"'global_project'", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('dc4194f1-6487-47c4-8ad7-da6ca0e88ca2')
def test_cluster_list_invalid_sort(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'clusters', {'sort': 'bad-sort'})
message = ex.resp_body['error']['message']
self.assertEqual("Unsupported sort key 'bad-sort' for 'sort'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('91489f49-686d-470d-9b74-c5e8f46eaae5')
def test_cluster_list_invalid_marker(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'clusters', {'marker': 'bad-marker'})
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for marker is not a valid UUID: 'bad-marker'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d13ff32f-aa8a-4704-824a-54d52a160874')
def test_cluster_list_unsupported_status(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'clusters', {'status': ['bad-status']})
message = ex.resp_body['error']['message']
self.assertEqual("Field value ['bad-status'] is invalid",
str(message))

View File

@@ -1,157 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionPolicyAttach(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionPolicyAttach, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
self.addCleanup(utils.cluster_detach_policy, self, self.cluster_id,
self.policy_id)
@decorators.idempotent_id('214c48f8-cca9-4512-a904-2985743a1155')
def test_cluster_action_policy_attach(self):
params = {
"policy_attach": {
"enabled": True,
"policy_id": self.policy_id
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterPolicyAttachNegativeInvalidParams(base.BaseSenlinAPITest):
@decorators.idempotent_id('76dcdc8d-7680-4e27-bccd-26ad9d697528')
def test_cluster_policy_attach_params_not_dict(self):
params = {
'policy_attach': 'POLICY_ID'
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("The data provided is not a map",
str(message))
@decorators.idempotent_id('34f6ceec-bde2-4013-87fe-db704ada5987')
def test_cluster_policy_attach_missing_policy_id_param(self):
params = {
'policy_attach': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("'policy_id' is a required property",
str(message))
@decorators.idempotent_id('5f5c42be-8ef4-4150-93cf-1e6b2515a293')
def test_cluster_policy_attach_invalid_enabled_param(self):
params = {
'policy_attach': {
'policy_id': 'POLICY_ID',
'enabled': 'not-bool'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Unrecognized value 'not-bool', acceptable values are: '0', '1', "
"'f', 'false', 'n', 'no', 'off', 'on', 't', 'true', 'y', 'yes'",
str(message))
class TestClusterPolicyAttachNegativePolicyNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyAttachNegativePolicyNotFound, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('7ee49643-a5a0-4567-b9d0-0210b05a6138')
def test_cluster_policy_attach_policy_not_found(self):
params = {
'policy_attach': {
'poilicy_id': '7ee49643-a5a0-4567-b9d0-0210b05a6138'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
if 'required' in str(message):
self.assertEqual("'policy_id' is a required property",
str(message))
else:
self.assertIn("Additional properties are not allowed",
str(message))
class TestClusterPolicyAttachNegativeNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('29e66d49-9ffa-47c9-bbe3-e0cf9c3370ee')
def test_cluster_policy_attach_cluster_not_found(self):
params = {
'policy_attach': {
'policy_id': 'POLICY_ID'
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'29e66d49-9ffa-47c9-bbe3-e0cf9c3370ee',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster '29e66d49-9ffa-47c9-bbe3-e0cf9c3370ee' could "
"not be found.", str(message))

View File

@@ -1,161 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionPolicyDetach(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionPolicyDetach, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
utils.cluster_attach_policy(self, self.cluster_id, self.policy_id)
@decorators.idempotent_id('8643245e-ee32-41bb-a736-e33c9e77202a')
def test_cluster_action_policy_detach(self):
params = {
"policy_detach": {
"policy_id": self.policy_id
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterPolicyDetachNegativeInvalidParams(base.BaseSenlinAPITest):
@decorators.idempotent_id('832c3c2d-0434-4d8d-bbe4-6552528d8647')
def test_cluster_policy_detach_param_not_dict(self):
params = {
'policy_detach': 'fake_policy'
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("The data provided is not a map",
str(message))
@decorators.idempotent_id('815a1c5a-f27b-4620-8711-bbef46507447')
def test_cluster_policy_detach_missing_profile_id_param(self):
params = {
'policy_detach': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("'policy_id' is a required property",
str(message))
class TestClusterPolicyDetachNegativePolicyNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyDetachNegativePolicyNotFound, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('d8edc8bd-530c-4495-94ea-52d844633335')
def test_cluster_policy_detach_policy_not_found(self):
params = {
'policy_detach': {
'policy_id': '7ee49643-a5a0-4567-b9d0-0210b05a6138'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified policy '7ee49643-a5a0-4567-b9d0-0210b05a6138' "
"could not be found.", str(message))
class TestClusterPolicyDetachNegativeUnattached(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyDetachNegativeUnattached, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
@decorators.idempotent_id('f302142c-3536-4524-8ce2-da86306731cb')
def test_cluster_policy_detach_policy_unattached(self):
params = {
'policy_detach': {
'policy_id': self.policy_id
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The policy '%s' is not attached to the specified cluster "
"'%s'." % (self.policy_id, self.cluster_id), str(message))
class TestClusterPolicyDetachNegativeNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('11ff0486-a022-4b28-9def-9b2d78d47fab')
def test_cluster_policy_detach_cluster_not_found(self):
params = {
'policy_detach': {
'policy_id': 'POLICY_ID'
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'11ff0486-a022-4b28-9def-9b2d78d47fab',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster '11ff0486-a022-4b28-9def-9b2d78d47fab' could "
"not be found.", str(message))

View File

@@ -1,185 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionPolicyUpdate(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionPolicyUpdate, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
utils.cluster_attach_policy(self, self.cluster_id, self.policy_id)
self.addCleanup(utils.cluster_detach_policy, self, self.cluster_id,
self.policy_id)
@decorators.idempotent_id('0b9efe3e-0abf-4230-a278-b282578df111')
def test_cluster_action_policy_update(self):
params = {
"policy_update": {
"enabled": False,
"policy_id": self.policy_id
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterPolicyUpdateNegativeInvalidParams(base.BaseSenlinAPITest):
@decorators.idempotent_id('653d8ea9-9c7e-48f2-b568-6167bb7f8666')
def test_cluster_policy_update_params_not_dict(self):
params = {
'policy_update': 'POLICY_ID'
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("The data provided is not a map",
str(message))
@decorators.idempotent_id('b47dff55-3ff0-4303-b86e-c4ab5f9a0878')
def test_cluster_policy_update_missing_profile_id_param(self):
params = {
'policy_update': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("'policy_id' is a required property",
str(message))
@decorators.idempotent_id('4adb03f4-35e6-40eb-b867-d75315ca8a89')
def test_cluster_policy_update_invalid_enabled_param(self):
params = {
'policy_update': {
'policy_id': 'POLICY_ID',
'enabled': 'not-bool'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Unrecognized value 'not-bool', acceptable values are: '0', '1', "
"'f', 'false', 'n', 'no', 'off', 'on', 't', 'true', 'y', 'yes'",
str(message))
class TestClusterPolicyUpdateNegativePolicyNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyUpdateNegativePolicyNotFound, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('7528bfa5-2106-459a-9ece-f201498b3ace')
def test_cluster_policy_update_policy_not_found(self):
params = {
'policy_update': {
'policy_id': '7528bfa5-2106-459a-9ece-f201498b3ace'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified policy '7528bfa5-2106-459a-9ece-f201498b3ace' "
"could not be found.", str(message))
class TestClusterPolicyUpdateNegativeUnattached(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterPolicyUpdateNegativeUnattached, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
@decorators.idempotent_id('81931b14-0a4c-43e5-a5eb-fdfd5386627e')
def test_cluster_policy_update_policy_unattached(self):
params = {
'policy_update': {
'policy_id': self.policy_id
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The policy '%s' is not attached to the specified cluster "
"'%s'." % (self.policy_id, self.cluster_id), str(message))
class TestClusterPolicyUpdateNegativeNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('c7605959-3bc9-41e2-9685-7e11fa03b9e6')
def test_cluster_policy_update_cluster_not_found(self):
params = {
'policy_update': {
'policy_id': 'POLICY_ID',
'enabled': False
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'c7605959-3bc9-41e2-9685-7e11fa03b9e6',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster 'c7605959-3bc9-41e2-9685-7e11fa03b9e6' could "
"not be found.", str(message))

View File

@@ -1,85 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionRecover(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionRecover, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id,
desired_capacity=1)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('9020bc7e-db2a-47d0-9f78-7e6a3d231fad')
def test_cluster_action_recover(self):
params = {
"recover": {
"operation": "rebuild"
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterRecoverNegativeInvalidParams(base.BaseSenlinAPITest):
@decorators.idempotent_id('1f93e909-b271-4e46-acd8-8cb621b27546')
def test_cluster_recover_params_not_dict(self):
params = {
'recover': ['k1', 'v1']
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The data provided is not a map",
str(message))
class TestClusterRecoverNegativeNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('e6e522f4-34d4-42f7-b7f1-45004e06f3d9')
def test_cluster_recover_cluster_not_found(self):
params = {
'recover': {'k1': 'v1'}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'e6e522f4-34d4-42f7-b7f1-45004e06f3d9',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster 'e6e522f4-34d4-42f7-b7f1-45004e06f3d9' "
"could not be found.", str(message))

View File

@@ -1,330 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionReplaceNodes(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionReplaceNodes, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.origin_node = utils.create_a_node(
self, profile_id, cluster_id=self.cluster_id)
self.addCleanup(utils.delete_a_node, self, self.origin_node)
self.replace_node = utils.create_a_node(self, profile_id)
@utils.api_microversion('1.3')
@decorators.idempotent_id('a17c2bff-eab7-4d02-a49f-9388eb53aa14')
def test_cluster_action_replace(self):
params = {
"replace_nodes": {
'nodes': {
self.origin_node: self.replace_node
}
}
}
# Trigger cluster action
res = self.client.cluster_replace_nodes('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('action', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterReplaceNodesNegativeInvalidParams(base.BaseSenlinAPITest):
@utils.api_microversion('1.3')
@decorators.idempotent_id('fbfb0819-6a15-4926-a21f-44fda6960bed')
def test_cluster_replace_nodes_params_not_map(self):
params = {
'replace_nodes': {
'nodes': []
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("The data provided is not a map",
str(message))
@utils.api_microversion('1.3')
@decorators.idempotent_id('600baf2f-e74f-467d-9883-3dcf1c357b57')
def test_cluster_replace_nodes_params_empty_map(self):
params = {
'replace_nodes': {
'nodes': {}
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("The data provided is not a map",
str(message))
@utils.api_microversion('1.3')
@decorators.idempotent_id('3e227f8f-7da3-4dc1-b647-68ed8fbdd111')
def test_cluster_replace_nodes_missing_new_node(self):
params = {
'replace_nodes': {
'nodes': {
'old_node': None
}
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Field `nodes[old_node]' cannot be None",
str(message))
@utils.api_microversion('1.3')
@decorators.idempotent_id('82ebbd5a-47fc-4d32-be3c-7bf3262dd574')
def test_cluster_replace_nodes_duplicated_node(self):
params = {
'replace_nodes': {
'nodes': {
'old_node1': 'new_node',
'old_node2': 'new_node'
}
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Map contains duplicated values",
str(message))
class TestClusterReplaceNodesNegativeOldNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterReplaceNodesNegativeOldNotFound, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.new_node = utils.create_a_node(self, self.profile_id)
self.addCleanup(utils.delete_a_node, self, self.new_node)
@utils.api_microversion('1.3')
@decorators.idempotent_id('911d6e85-220f-4208-9a0b-b91e83c5e787')
def test_cluster_replace_nodes_old_node_not_found(self):
params = {
'replace_nodes': {
'nodes': {
'old_node': self.new_node
}
}
}
# Verify not found badrequest is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertIn("Original nodes not found", str(message))
class TestClusterReplaceNodesNegativeNewNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterReplaceNodesNegativeNewNotFound, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.old_node = utils.create_a_node(self, self.profile_id,
cluster_id=self.cluster_id)
self.addCleanup(utils.delete_a_node, self, self.old_node)
@utils.api_microversion('1.3')
@decorators.idempotent_id('1282c521-c479-42f3-b375-8d6d62b1d5dc')
def test_cluster_replace_nodes_old_node_not_found(self):
params = {
'replace_nodes': {
'nodes': {
self.old_node: 'new_node'
}
}
}
# Verify badrequest exception is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertIn("Replacement nodes not found", str(message))
class TestClusterReplaceNodesNegativeNewNotOrphan(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterReplaceNodesNegativeNewNotOrphan, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.old_node = utils.create_a_node(self, self.profile_id,
cluster_id=self.cluster_id)
self.addCleanup(utils.delete_a_node, self, self.old_node)
self.cluster_id2 = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id2)
self.new_node = utils.create_a_node(self, self.profile_id,
self.cluster_id2)
self.addCleanup(utils.delete_a_node, self, self.new_node)
@utils.api_microversion('1.3')
@decorators.idempotent_id('e001ba28-f7ad-4af5-a281-5652ca040c65')
def test_cluster_replace_nodes_new_node_not_orphan(self):
params = {
'replace_nodes': {
'nodes': {
self.old_node: self.new_node
}
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertIn("already member of a cluster", str(message))
class TestClusterReplaceNodeNegativeProfileUnmatch(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterReplaceNodeNegativeProfileUnmatch, self).setUp()
self.profile_id = utils.create_a_profile(
self, spec=constants.spec_nova_server)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.old_node = utils.create_a_node(self, self.profile_id,
self.cluster_id)
self.addCleanup(utils.delete_a_node, self, self.old_node)
self.profile_id2 = utils.create_a_profile(
self, spec=constants.spec_heat_stack)
self.addCleanup(utils.delete_a_profile, self, self.profile_id2)
self.new_node = utils.create_a_node(self, self.profile_id2)
self.addCleanup(utils.delete_a_node, self, self.new_node)
@utils.api_microversion('1.3')
@decorators.idempotent_id('d1cb2068-b23c-4023-ad25-271f8e5b1bfa')
def test_cluster_replace_nodes_profile_type_unmatch(self):
params = {
'replace_nodes': {
'nodes': {
self.old_node: self.new_node
}
}
}
# Verify badrequest exception is raised
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertIn("Profile type of nodes", str(message))
self.assertIn("do not match that of the cluster", str(message))
class TestClusterReplaceNodeNegativeOldOrphan(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterReplaceNodeNegativeOldOrphan, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.old_node = utils.create_a_node(self, self.profile_id)
self.addCleanup(utils.delete_a_node, self, self.old_node)
self.new_node = utils.create_a_node(self, self.profile_id)
self.addCleanup(utils.delete_a_node, self, self.new_node)
@utils.api_microversion('1.3')
@decorators.idempotent_id('ca0afe22-e758-477c-8ca5-cd1b686747dc')
def test_cluster_replace_nodes_old_node_orphan(self):
params = {
'replace_nodes': {
'nodes': {
self.old_node: self.new_node
}
}
}
# Versify badrequest exeception is raised
ex = self.assertRaises(exceptions.BadRequest,
self.client.cluster_replace_nodes, 'clusters',
self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertIn("to be replaced are not members of the cluster",
str(message))
class TestClusterReplaceNodeNegativeClusterNotFound(base.BaseSenlinAPITest):
@utils.api_microversion('1.3')
@decorators.idempotent_id('086c0657-f7a1-4722-a585-ac281725bcfc')
def test_cluster_replace_nodes_cluster_not_found(self):
params = {
'replace_nodes': {
'nodes': {
'old_node': 'new_node'
}
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.cluster_replace_nodes, 'clusters',
'db0faadf-9cd2-457f-b434-4891b77938ab',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster 'db0faadf-9cd2-457f-b434-4891b77938ab' could "
"not be found.", str(message))

View File

@@ -1,347 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionResize(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionResize, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('f5f75882-df3d-481f-bd05-019e4d08af65')
def test_cluster_action_resize(self):
params = {
"resize": {
"adjustment_type": "CHANGE_IN_CAPACITY",
"max_size": 20,
"min_step": 1,
"min_size": 5,
"number": 20,
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterResizeNegativeInvalidResizeParams(base.BaseSenlinAPITest):
@decorators.idempotent_id('57bc61a2-df38-4bf5-a26a-d23d2cc67ca3')
def test_cluster_resize_invalid_adj_type(self):
params = {
"resize": {
"adjustment_type": "bogus",
"max_size": 20,
"min_size": 5,
"number": 5
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Value 'bogus' is not acceptable for field "
"'adjustment_type'.", str(message))
@decorators.idempotent_id('cef85ed4-9dd3-4f9f-91fe-4372d9aa8956')
def test_cluster_resize_missing_adj_type(self):
params = {
"resize": {
"max_size": 20,
"min_size": 5,
"number": 5
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Missing adjustment_type value for size adjustment.",
str(message))
@decorators.idempotent_id('e42dd7e1-aa36-4e46-8b5b-2571d00574c9')
def test_cluster_resize_missing_adj_number(self):
params = {
"resize": {
"adjustment_type": "CHANGE_IN_CAPACITY",
"max_size": 20,
"min_size": 5
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Missing number value for size adjustment.",
str(message))
@decorators.idempotent_id('7e669b3e-8fbd-4820-a281-7cc4b29c6020')
def test_cluster_resize_invalid_adj_number(self):
params = {
"resize": {
"adjustment_type": "EXACT_CAPACITY",
"max_size": 20,
"min_size": 5,
"number": -1
}
}
# Verify badrequest exception(400) is raised.
self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
@decorators.idempotent_id('5a069782-d6d8-4389-a68c-beb32375a39e')
def test_cluster_resize_min_size_over_max_size(self):
params = {
"resize": {
"adjustment_type": "CHANGE_IN_CAPACITY",
"max_size": 10,
"min_size": 20,
"number": 5
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'cluster_id', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified min_size (20) is greater than the "
"specified max_size (10).", str(message))
class TestClusterResizeNegativeClusterNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('087ef694-55d2-4616-a58b-1073cacb2bcd')
def test_cluster_resize_cluster_not_found(self):
params = {
"resize": {
"adjustment_type": "CHANGE_IN_CAPACITY",
"max_size": 20,
"min_size": 5,
"number": 5
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'087ef694-55d2-4616-a58b-1073cacb2bcd',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster '087ef694-55d2-4616-a58b-1073cacb2bcd' could "
"not be found.", str(message))
class TestClusterResizeNegativeSizeCheckFailed(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterResizeNegativeSizeCheckFailed, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id,
min_size=2, max_size=5,
desired_capacity=3)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('92e7d3c8-2d38-4766-86c3-41dc14bf89a1')
def test_cluster_resize_break_upper_limit(self):
# New desired_capacity will be greater than max_size
params = {
"resize": {
"adjustment_type": "CHANGE_IN_CAPACITY",
"number": 3,
"strict": True
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The target capacity (6) is greater than the cluster's "
"max_size (5).", str(message))
@decorators.idempotent_id('9dcac577-d768-44d1-b119-02d27202ef08')
def test_cluster_resize_break_lower_limit(self):
# New desired_capacity will be less than min_size
params = {
"resize": {
"adjustment_type": "CHANGE_IN_CAPACITY",
"number": -2,
"strict": True
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The target capacity (1) is less than the cluster's "
"min_size (2).", str(message))
@decorators.idempotent_id('d7a96d95-2944-4749-be34-cfe39a5dbcb4')
def test_cluster_resize_max_size_under_current_desired_capacity(self):
# New max_size is less than current desired_capacity of cluster
params = {
"resize": {
"max_size": 2,
"strict": True
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified max_size (2) is less than the current "
"desired_capacity (3) of the cluster.", str(message))
@decorators.idempotent_id('3b35938f-a73a-4096-bf13-af3709aed47f')
def test_cluster_resize_max_size_under_current_min_size(self):
# New max_size is less than current min_size of cluster
# with strict set to False
params = {
"resize": {
"max_size": 1,
"strict": False
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified max_size (1) is less than the current "
"min_size (2) of the cluster.", str(message))
# New max_size is less than current min_size of cluster
# with strict set to True
params = {
"resize": {
"max_size": 1,
"strict": True
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified max_size (1) is less than the current min_size "
"(2) of the cluster.", str(message))
@decorators.idempotent_id('1d7595a4-a7a8-42a4-9f90-7501a4bbb7e5')
def test_cluster_resize_min_size_over_current_desired_capacity(self):
# New min_size is greater than current desired_capacity of cluster
params = {
"resize": {
"min_size": 4,
"strict": True
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified min_size (4) is greater than the current "
"desired_capacity (3) of the cluster.", str(message))
@decorators.idempotent_id('606e5d3f-0857-4bfe-b52d-2ea1ad0cec16')
def test_cluster_resize_min_size_over_current_max_size(self):
# New min_size is greater than current max_size of cluster
# with strict set to False
params = {
"resize": {
"min_size": 6,
"strict": False
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified min_size (6) is greater than the current "
"max_size (5) of the cluster.", str(message))
# New min_size is greater than current max_size of cluster
# with strict set to True
params = {
"resize": {
"min_size": 6,
"strict": True
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified min_size (6) is greater than the current "
"max_size (5) of the cluster.", str(message))

View File

@@ -1,118 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterActionScaleIn(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionScaleIn, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id,
desired_capacity=1)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('a579cd01-8096-4bee-9978-d095025f605c')
def test_cluster_action_scale_in(self):
params = {
"scale_in": {
"count": "1"
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterScaleInInvalidRequest(base.BaseSenlinAPITest):
@decorators.idempotent_id('d826dc0f-ef1c-47ee-b31f-3042aaa8ec54')
def test_cluster_scale_in_invalid_count(self):
params = {
"scale_in": {
"count": "bad-count"
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'fake', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for count must be an integer: 'bad-count'.",
str(message))
class TestClusterScaleInNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterScaleInNegativeBadRequest, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id,
min_size=0, max_size=5,
desired_capacity=1)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('337a8a8f-a368-4d4f-949a-8b116dbb6a75')
def test_cluster_scale_in_invalid_count(self):
params = {
"scale_in": {
"count": -1
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual("Value must be >= 0 for field 'count'.",
str(message))
class TestClusterScaleInNegativeNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('0124c7de-66d0-4a84-9c8f-80bc4d246b79')
def test_cluster_scale_in_cluster_not_found(self):
params = {
"scale_in": {
"count": 1
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'0124c7de-66d0-4a84-9c8f-80bc4d246b79',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster '0124c7de-66d0-4a84-9c8f-80bc4d246b79' could "
"not be found.", str(message))

View File

@@ -1,178 +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.
from tempest import config
from tempest.lib.common import api_version_utils
from tempest.lib import decorators
from tempest.lib import exceptions
import time
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
CONF = config.CONF
class TestClusterActionScaleOut(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterActionScaleOut, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('f15ff8cc-4be3-4c93-9979-6be428e83cd7')
def test_cluster_action_scale_out(self):
params = {
"scale_out": {
"count": "1"
}
}
# Trigger cluster action
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestClusterScaleOutNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterScaleOutNegativeBadRequest, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id,
min_size=0, max_size=5,
desired_capacity=1)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('2bbf6e0c-a8cc-4b29-8060-83652ffd6cd2')
def test_cluster_scale_out_invalid_count(self):
params = {
"scale_out": {
"count": -1
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual("Value must be >= 0 for field 'count'.",
str(message))
class TestClusterScaleOutInvalidRequest(base.BaseSenlinAPITest):
@decorators.idempotent_id('7aa3fd0c-c092-4a54-8dae-3814492101b0')
def test_cluster_scale_out_invalid_count(self):
params = {
"scale_out": {
"count": "bad-count"
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'clusters', 'fake', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for count must be an integer: 'bad-count'.",
str(message))
class TestClusterScaleOutNegativeNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('b7038d95-204c-455f-a866-94dc535dd840')
def test_cluster_scale_out_cluster_not_found(self):
params = {
"scale_out": {
"count": 1
}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'clusters',
'b7038d95-204c-455f-a866-94dc535dd840',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster 'b7038d95-204c-455f-a866-94dc535dd840' could "
"not be found.", str(message))
class TestClusterScaleOutNegativeResourceIsLocked(base.BaseSenlinAPITest):
min_microversion = '1.11'
max_microversion = 'latest'
@classmethod
def skip_checks(cls):
super(base.BaseSenlinAPITest, cls).skip_checks()
api_version_utils.check_skip_with_microversion(
cls.min_microversion, cls.max_microversion,
CONF.clustering.min_microversion,
CONF.clustering.max_microversion)
def setUp(self):
super(TestClusterScaleOutNegativeResourceIsLocked, self).setUp()
# create profile with simulated wait time to test
# cluster locked scenario
profile_id = utils.create_a_profile(
self, metadata={'simulated_wait_time': 10})
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('3ecc8a73-03b2-4937-bb98-5b49554baf06')
def test_cluster_action_scale_out_locked_cluster(self):
params = {
"scale_out": {
"count": "1"
}
}
# Trigger cluster scale out
res = self.client.trigger_action('clusters', self.cluster_id,
params=params)
self.assertEqual(202, res['status'])
# sleep long enough for the action to start executing and locking
# the clusters
time.sleep(5)
# Verify resource locked exception(409) is raised
# when another scale out is called within 5 secs
ex = self.assertRaises(exceptions.Conflict,
self.client.trigger_action, 'clusters',
self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
("CLUSTER_SCALE_OUT for cluster '{}' cannot be completed because "
"it is already locked.").format(self.cluster_id), str(message))
# sleep long enough for the cluster lock to clear
time.sleep(15)

View File

@@ -1,43 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterShow(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterShow, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('45f56c9a-4589-48dd-9256-9b368727dd6c')
def test_cluster_show(self):
res = self.client.get_obj('clusters', self.cluster_id)
# Verify resp of cluster get API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
cluster = res['body']
for key in ['created_at', 'data', 'desired_capacity', 'domain',
'id', 'init_at', 'max_size', 'metadata', 'min_size',
'name', 'nodes', 'policies', 'profile_id', 'profile_name',
'project', 'status', 'status_reason', 'timeout',
'updated_at', 'user']:
self.assertIn(key, cluster)

View File

@@ -1,58 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterShowNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterShowNegativeBadRequest, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id1 = utils.create_a_cluster(self, profile_id,
name='c-01')
self.cluster_id2 = utils.create_a_cluster(self, profile_id,
name='c-01')
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id1)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id2)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('3365dca5-8895-4dc3-befe-fd15b17c824c')
def test_cluster_show_multiple_choice(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.get_obj,
'clusters', 'c-01')
message = ex.resp_body['error']['message']
self.assertEqual(
"Multiple results found matching the query criteria "
"'c-01'. Please be more specific.", str(message))
class TestClusterShowNegativeNotFound(base.BaseSenlinAPITest):
@decorators.idempotent_id('4706516e-002d-42b2-9805-69058178cd9c')
def test_cluster_show_cluster_not_found(self):
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.get_obj,
'clusters', 'c-01')
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster 'c-01' could "
"not be found.", str(message))

View File

@@ -1,55 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterUpdate(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterUpdate, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('ba8514b2-b3c4-47a4-9176-7fe9bb2781ae')
def test_cluster_update_basic_properties(self):
# Update basic properties of cluster
params = {
'cluster': {
'timeout': 240,
'metadata': {'k2': 'v2'},
'name': 'cluster_new_name'
}
}
res = self.client.update_obj('clusters', self.cluster_id, params)
# Verify resp of cluster update API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
cluster = res['body']
for key in ['created_at', 'data', 'domain', 'id', 'init_at',
'metadata', 'name', 'nodes', 'policies', 'profile_id',
'profile_name', 'project', 'status', 'status_reason',
'timeout', 'updated_at', 'user']:
self.assertIn(key, cluster)
# Wait for cluster update to be done before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')

View File

@@ -1,203 +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 copy
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterUpdateNegativeInvalidParam(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('7bddd411-b890-4a36-a523-3e49b87cb645')
def test_cluster_update_cluster_invalid_param(self):
params = {
'cluster': {
'bad': 'invalid'
}
}
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj, 'clusters',
'f7a97fce-f495-44a8-b41a-7408139adacf',
params)
message = ex.resp_body['error']['message']
self.assertIn("Additional properties are not allowed", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('80cd0acd-772f-482f-8c6d-90843d986eb1')
def test_cluster_update_cluster_empty_param(self):
params = {}
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj, 'clusters',
'80cd0acd-772f-482f-8c6d-90843d986eb1',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Malformed request data, missing 'cluster' key in "
"request body.", str(message))
class TestClusterUpdateNegativeNotFound(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f7a97fce-f495-44a8-b41a-7408139adacf')
def test_cluster_update_cluster_not_found(self):
ex = self.assertRaises(exceptions.NotFound,
self.client.update_obj, 'clusters',
'f7a97fce-f495-44a8-b41a-7408139adacf',
{'cluster': {'name': 'new-name'}})
message = ex.resp_body['error']['message']
self.assertEqual(
"The cluster 'f7a97fce-f495-44a8-b41a-7408139adacf' could "
"not be found.", str(message))
class TestClusterUpdateNegativeProfileNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterUpdateNegativeProfileNotFound, self).setUp()
# Create a profile
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# Create a cluster
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('fb68921d-1fe8-4c14-be9a-51fa43d4f705')
def test_cluster_update_profile_not_found(self):
# Provided profile can not be found
params = {
'cluster': {
'profile_id': 'fb68921d-1fe8-4c14-be9a-51fa43d4f705',
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified profile 'fb68921d-1fe8-4c14-be9a-51fa43d4f705' "
"could not be found.", str(message))
class TestClusterUpdateNegativeProfileMultichoices(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterUpdateNegativeProfileMultichoices, self).setUp()
# Create a profile
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# Create a cluster
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
# Create two new profiles of the same type with the same name
new_spec = copy.deepcopy(constants.spec_nova_server)
new_spec['properties']['flavor'] = 'new_flavor'
new_profile_id1 = utils.create_a_profile(self, new_spec, name='p-nova')
new_profile_id2 = utils.create_a_profile(self, new_spec, name='p-nova')
self.addCleanup(utils.delete_a_profile, self, new_profile_id1)
self.addCleanup(utils.delete_a_profile, self, new_profile_id2)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('6a3eb86c-4b5c-4cfc-891c-7b0be17715f2')
def test_cluster_update_profile_multichoices(self):
# Multiple profiles are found for given name
params = {
'cluster': {
'profile_id': 'p-nova',
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Multiple results found matching the query criteria 'p-nova'. "
"Please be more specific.", str(message))
class TestClusterUpdateNegativeProfileTypeUnmatch(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterUpdateNegativeProfileTypeUnmatch, self).setUp()
# Create a profile
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# Create a cluster
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
# Create a new profile of different type
self.new_profile_id = utils.create_a_profile(
self, spec=constants.spec_heat_stack)
self.addCleanup(utils.delete_a_profile, self, self.new_profile_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('c28e1bc0-fadb-4394-b2d0-67ad8b87ac04')
def test_cluster_update_profile_type_unmatch(self):
# New profile type is different from original cone
params = {
'cluster': {
'profile_id': self.new_profile_id,
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Cannot update a cluster to a different profile type, "
"operation aborted.", str(message))
class TestClusterUpdateNegativeNoPropertyUpdated(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterUpdateNegativeNoPropertyUpdated, self).setUp()
# Create a profile
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# Create a cluster
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0fbd8fd9-7789-47da-b806-d91631a28556')
def test_cluster_update_no_property_updated(self):
# No any property is updated
params = {
'cluster': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj,
'clusters', self.cluster_id, params)
message = ex.resp_body['error']['message']
self.assertEqual("No property needs an update.", str(message))

View File

@@ -1,126 +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 copy
from tempest.lib import decorators
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestClusterUpdateProfile(base.BaseSenlinAPITest):
def setUp(self):
super(TestClusterUpdateProfile, self).setUp()
self.old_profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.old_profile_id)
# create a new profile
new_spec = copy.deepcopy(constants.spec_nova_server)
new_spec['properties']['flavor'] = 'new_flavor'
new_spec['properties']['image'] = 'new_image'
self.new_profile_id = utils.create_a_profile(self, new_spec)
self.addCleanup(utils.delete_a_profile, self, self.new_profile_id)
self.cluster_id = utils.create_a_cluster(self, self.old_profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
self.node1_id = utils.create_a_node(
self, self.old_profile_id, name='N01', cluster_id=self.cluster_id)
# The following logic is not needed, when cluster is deleted, the
# node is deleted automatially.
# self.addCleanup(utils.delete_a_node, self, self.node1_id)
@decorators.idempotent_id('abff7891-21af-4c37-a8df-5bc7379ce349')
def test_cluster_update_profile(self):
# Update cluster with new profile
params = {
'cluster': {
'profile_id': self.new_profile_id
}
}
res = self.client.update_obj('clusters', self.cluster_id, params)
# Verify resp of cluster update API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
cluster = res['body']
for key in ['created_at', 'data', 'domain', 'id', 'init_at',
'metadata', 'name', 'nodes', 'policies', 'profile_id',
'profile_name', 'project', 'status', 'status_reason',
'timeout', 'updated_at', 'user']:
self.assertIn(key, cluster)
# Wait for cluster update to be done before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
@utils.api_microversion('1.6')
@decorators.idempotent_id('686cd38a-16eb-4364-b495-367fc7c8bb26')
def test_cluster_update_profile_profile_only(self):
# Update cluster with new profile
params = {
'cluster': {
'profile_id': self.new_profile_id,
'profile_only': True
}
}
res = self.client.update_obj('clusters', self.cluster_id, params)
# Verify resp of cluster update API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
# Wait for cluster update to be done before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
# verify cluster profile id changed
res = self.client.get_obj('clusters', self.cluster_id)
self.assertEqual(self.new_profile_id, res['body']['profile_id'])
# verify node profile id not changed.
res = self.client.get_obj('nodes', self.node1_id)
self.assertEqual(self.old_profile_id, res['body']['profile_id'])
@utils.api_microversion('1.5')
@decorators.idempotent_id('8a67876f-e9ea-4cbe-807a-73eefe482536')
def test_cluster_update_profile_profile_only_backward(self):
# Update cluster with new profile
params = {
'cluster': {
'profile_id': self.new_profile_id,
'profile_only': True
}
}
res = self.client.update_obj('clusters', self.cluster_id, params)
# Verify resp of cluster update API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
# Wait for cluster update to be done before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
# verify cluster profile id changed
res = self.client.get_obj('clusters', self.cluster_id)
self.assertEqual(self.new_profile_id, res['body']['profile_id'])
# verify node profile id not changed.
res = self.client.get_obj('nodes', self.node1_id)
self.assertEqual(self.new_profile_id, res['body']['profile_id'])

View File

@@ -1,41 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestEventList(base.BaseSenlinAPITest):
def setup(self):
super(TestEventList, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, cluster_id)
@decorators.idempotent_id('498a7e22-7ada-415b-a7cf-927b0ad3d9f6')
def test_event_list(self):
res = self.client.list_objs('events')
# Verify resp of event list API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
events = res['body']
for event in events:
for key in ['action', 'cluster_id', 'id', 'level', 'oid',
'oname', 'otype', 'project', 'status',
'status_reason', 'timestamp', 'user']:
self.assertIn(key, event)

View File

@@ -1,108 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestEventListNegativeBadRequest(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('ddbc0735-869c-4da4-ae1d-ec67984cca46')
def test_event_list_invalid_params(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'events', {'bogus': 'foo'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid parameter bogus", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('fc3a5097-d332-42e5-8fb8-682cb248d9ad')
def test_event_list_limit_not_int(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'events', {'limit': 'not-int'})
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for limit must be an integer: 'not-int'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('da0871d7-9eb4-4349-b1db-0287d444ff58')
def test_event_list_global_project_false(self):
ex = self.assertRaises(exceptions.Forbidden,
self.client.list_objs,
'events', {'global_project': 'True'})
message = ex.resp_body['error']['message']
self.assertEqual("You are not authorized to complete this operation.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('bccbbec0-08e5-4594-8fa4-c874d4359033')
def test_event_list_global_project_not_bool(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'events', {'global_project': 'not-bool'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid value 'not-bool' specified for "
"'global_project'", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('84d2d883-9402-4735-8be6-b726b0e0edbd')
def test_event_list_invalid_sort(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'events', {'sort': 'bad-sort'})
message = ex.resp_body['error']['message']
self.assertEqual("Unsupported sort key 'bad-sort' for 'sort'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('c53500fc-0504-4f0f-b540-1f39e524db53')
def test_event_list_invalid_marker(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'events', {'marker': 'bad-marker'})
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for marker is not a valid UUID: 'bad-marker'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('72e0fd48-2dc7-47ce-abad-299e508fabd4')
def test_event_list_unsupported_level(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'events', {'level': ['bad-level']})
message = ex.resp_body['error']['message']
self.assertEqual("Field value ['bad-level'] is invalid",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('7a6c1133-733b-481f-98d0-3e4a2d55e027')
def test_event_list_unsupported_action(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'events', {'action': ['bad-action']})
message = ex.resp_body['error']['message']
self.assertEqual("Field value ['bad-action'] is invalid",
str(message))

View File

@@ -1,43 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestEventShow(base.BaseSenlinAPITest):
def setUp(self):
super(TestEventShow, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.idempotent_id('b23490a7-0ae2-44be-a1f9-9f2d82dfe6aa')
def test_event_show(self):
# Get cluster events
events = self.client.list_objs('events',
{'oid': self.cluster_id})['body']
res = self.client.get_obj('events', events[0]['id'])
# Verify resp of event list API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
event = res['body']
for key in ['action', 'cluster_id', 'id', 'level', 'oid',
'oname', 'otype', 'project', 'status',
'status_reason', 'timestamp', 'user']:
self.assertIn(key, event)

View File

@@ -1,31 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestEventShowNegativeNotFound(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('318ae275-ed68-48f5-9151-523085107112')
def test_event_show_not_found(self):
ex = self.assertRaises(exceptions.NotFound,
self.client.get_obj, 'events',
'318ae275-ed68-48f5-9151-523085107112')
message = ex.resp_body['error']['message']
self.assertEqual(
"The event '318ae275-ed68-48f5-9151-523085107112' could "
"not be found.", str(message))

View File

@@ -1,66 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestNodeActionNegativeBadRequest(base.BaseSenlinAPITest):
@decorators.idempotent_id('9ab462e2-ea3a-49f8-bd78-5a056ae80a48')
def test_no_action_specified(self):
# No action is specified
params = {}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'nodes', 'node_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("No action specified.",
str(message))
@decorators.idempotent_id('9e696e4f-1ec8-45d7-8461-81d275aae81d')
def test_multiple_action_specified(self):
# Multiple actions are specified
params = {
'check': {},
'recover': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'nodes', 'node_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Multiple actions specified.",
str(message))
@decorators.idempotent_id('4bd97c71-fbfc-421d-95ff-b3f4a212cc38')
def test_unrecognized_action(self):
# Unrecoginized action is specified
params = {
'bogus': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'nodes', 'node_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("Unrecognized action 'bogus' specified",
str(message))

View File

@@ -1,80 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeCheck(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeCheck, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.idempotent_id('ae124bfe-9fcf-4e87-91b7-319102efbdcc')
def test_check(self):
params = {
'check': {
}
}
# Trigger node action
res = self.client.trigger_action('nodes', self.node_id, params=params)
# Verfiy resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestNodeCheckNegative(base.BaseSenlinAPITest):
@decorators.idempotent_id('723ea351-1bcb-4d45-bfe7-35c656d29761')
def test_param_is_not_map(self):
# Check action parameter is not a map
params = {
'check': []
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'nodes', 'node_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("The params provided is not a map.",
str(message))
@decorators.idempotent_id('90c46123-f992-4833-859a-46f6d2ccd8e9')
def test_node_not_found(self):
params = {
'check': {}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action, 'nodes',
'90c46123-f992-4833-859a-46f6d2ccd8e9',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The node '90c46123-f992-4833-859a-46f6d2ccd8e9' could "
"not be found.", str(message))

View File

@@ -1,78 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeCreate(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeCreate, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.cluster_id = utils.create_a_cluster(
self, self.profile_id,
config={'node.name.format': 'test-node'})
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@utils.api_microversion('1.0')
@decorators.idempotent_id('14d06753-7f0a-4ad2-84be-37fce7114a8f')
def test_node_create_all_attrs_defined(self):
expected_keys = ['cluster_id', 'created_at', 'data', 'domain', 'id',
'index', 'init_at', 'metadata', 'name', 'physical_id',
'profile_id', 'profile_name', 'project', 'role',
'status', 'status_reason', 'updated_at', 'user']
self._create_node(expected_keys)
@decorators.idempotent_id('b678a196-01bf-42cb-a079-9595d937d211')
@utils.api_microversion('1.13')
def test_node_create_all_attrs_defined_v1_13(self):
expected_keys = ['cluster_id', 'created_at', 'data', 'domain', 'id',
'index', 'init_at', 'metadata', 'name', 'physical_id',
'profile_id', 'profile_name', 'project', 'role',
'status', 'status_reason', 'tainted',
'updated_at', 'user']
self._create_node(expected_keys)
def _create_node(self, expected_keys):
params = {
'node': {
'profile_id': self.profile_id,
'cluster_id': self.cluster_id,
'metadata': {'k1': 'v1'},
'role': 'member',
'name': 'test-node'
}
}
res = self.client.create_obj('nodes', params)
# Verify resp of node create API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
node = res['body']
self.addCleanup(utils.delete_a_node, self, node['id'])
for key in expected_keys:
self.assertIn(key, node)
self.assertIn('test-node', node['name'])
self.assertEqual(self.profile_id, node['profile_id'])
self.assertEqual(self.cluster_id, node['cluster_id'])
self.assertEqual({'k1': 'v1'}, node['metadata'])
self.assertEqual('member', node['role'])
self.assertEqual('test-node', node['name'])
# Wait node to be active before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')

View File

@@ -1,174 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeCreateNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeCreateNegativeBadRequest, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
self.profile_id2 = utils.create_a_profile(
self, spec=constants.spec_heat_stack)
self.addCleanup(utils.delete_a_profile, self, self.profile_id2)
self.cluster_id = utils.create_a_cluster(self, self.profile_id)
self.addCleanup(utils.delete_a_cluster, self, self.cluster_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('cbe7827a-60ca-42c0-99d2-38167cb4f46d')
def test_node_create_profile_invalid(self):
# Invalid profile_id is provided
params = {
'node': {
'profile_id': 'cbe7827a-60ca-42c0-99d2-38167cb4f46d',
'cluster_id': self.cluster_id,
'metadata': {'k1': 'v1'},
'role': 'member',
'name': 'test-node'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'nodes', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified profile 'cbe7827a-60ca-42c0-99d2-38167cb4f46d' "
"could not be found.", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('960cd427-2487-4c83-b679-1b5e1f9dd985')
def test_node_create_cluster_invalid(self):
# Invalid cluster_id is provided
params = {
'node': {
'profile_id': self.profile_id,
'cluster_id': '960cd427-2487-4c83-b679-1b5e1f9dd985',
'metadata': {'k1': 'v1'},
'role': 'member',
'name': 'test-node'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'nodes', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified cluster '960cd427-2487-4c83-b679-1b5e1f9dd985' "
"could not be found.", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('8ddf45a5-f45f-4cc8-813d-2bff75498576')
def test_node_create_profile_type_unmatch(self):
# Node profile type is different from cluster profile type
params = {
'node': {
'profile_id': self.profile_id2,
'cluster_id': self.cluster_id,
'metadata': {'k1': 'v1'},
'role': 'member',
'name': 'test-node'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'nodes', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Node and cluster have different profile type, "
"operation aborted.", str(message))
class TestNodeCreateNegativeInvalidRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeCreateNegativeInvalidRequest, self).setUp()
self.profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, self.profile_id)
@decorators.idempotent_id('b109aa66-2a54-493e-8a07-1ea6f20e17ce')
def test_node_create_empty_param(self):
params = {}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'nodes', params)
message = ex.resp_body['error']['message']
self.assertEqual("Request body missing 'node' key.",
str(message))
@decorators.idempotent_id('080946ef-a9e0-46b4-add7-da70d05391d6')
def test_node_create_unsupported_param(self):
params = {
'node': {
'profile_id': self.profile_id,
'name': 'fake_name',
'boo': 'foo'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'nodes', params)
message = ex.resp_body['error']['message']
self.assertIn("Additional properties are not allowed",
str(message))
@decorators.idempotent_id('0ac2a77e-082c-47d2-8156-92e7fb43689c')
def test_node_create_miss_name(self):
params = {
'node': {
'profile_id': 'fake_profile',
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'nodes', params)
message = ex.resp_body['error']['message']
self.assertEqual("'name' is a required property",
str(message))
@decorators.idempotent_id('39eb68ed-7808-4a73-85b1-83faca124546')
def test_node_create_miss_profile(self):
params = {
'node': {
'name': 'fake',
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'nodes', params)
message = ex.resp_body['error']['message']
self.assertEqual("'profile_id' is a required property",
str(message))

View File

@@ -1,39 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeDelete(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeDelete, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id = utils.create_a_node(self, profile_id)
@decorators.idempotent_id('29b18f65-2e0e-4a61-b00a-e5803365525b')
def test_node_delete(self):
# Delete test node
res = self.client.delete_obj('nodes', self.node_id)
# Verify resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIsNone(res['body'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')

View File

@@ -1,59 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeDeleteNegativeNotFound(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('86bd7425-cddd-457e-a467-78e290aceab9')
def test_node_delete_not_found(self):
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.delete_obj, 'nodes',
'86bd7425-cddd-457e-a467-78e290aceab9')
message = ex.resp_body['error']['message']
self.assertEqual(
"The node '86bd7425-cddd-457e-a467-78e290aceab9' could "
"not be found.", str(message))
class TestNodeDeleteNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeDeleteNegativeBadRequest, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id1 = utils.create_a_node(self, profile_id, name='n-01')
self.addCleanup(utils.delete_a_node, self, self.node_id1)
self.node_id2 = utils.create_a_node(self, profile_id, name='n-01')
self.addCleanup(utils.delete_a_node, self, self.node_id2)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('669203c5-6abd-4e0e-bc66-0bdd588c7b63')
def test_node_delete_multiple_choice(self):
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.delete_obj,
'nodes', 'n-01')
message = ex.resp_body['error']['message']
self.assertEqual(
"Multiple results found matching the query criteria 'n-01'. "
"Please be more specific.", str(message))

View File

@@ -1,63 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeList(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeList, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@utils.api_microversion('1.0')
@decorators.idempotent_id('cd086dcb-7509-4125-adfc-6beb63b10d0a')
def test_node_list(self):
expected_keys = ['cluster_id', 'created_at', 'data', 'domain',
'id', 'index', 'init_at', 'metadata', 'name',
'physical_id', 'profile_id', 'profile_name',
'project', 'role', 'status', 'status_reason',
'updated_at', 'user']
self._list_node(expected_keys)
@decorators.idempotent_id('f7cc5f8a-8aed-468f-ad03-883071371c5d')
@utils.api_microversion('1.13')
def test_node_list_v1_13(self):
expected_keys = ['cluster_id', 'created_at', 'data', 'domain',
'id', 'index', 'init_at', 'metadata', 'name',
'physical_id', 'profile_id', 'profile_name',
'project', 'role', 'status', 'status_reason',
'updated_at', 'user']
self._list_node(expected_keys)
def _list_node(self, expected_keys):
res = self.client.list_objs('nodes')
# Verify resp of node list API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
nodes = res['body']
node_ids = []
for node in nodes:
for key in expected_keys:
self.assertIn(key, node)
node_ids.append(node['id'])
self.assertIn(self.node_id, node_ids)

View File

@@ -1,118 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestNodeListNegativeBadRequest(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d27172ca-00ad-465d-b854-4fac7f1edc13')
def test_node_list_invalid_params(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'nodes', {'bogus': 'foo'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid parameter bogus", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('bb668501-b48e-4355-8bf0-eb9b2e2a89fd')
def test_node_list_cluster_not_found(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'nodes', {'cluster_id': 'fake'})
message = ex.resp_body['error']['message']
self.assertEqual("Cannot find the given cluster: fake.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('2e6677ad-8f0d-410e-bd30-baea7882c6fd')
def test_node_list_limit_negative(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'nodes', {'limit': '-5'})
message = ex.resp_body['error']['message']
self.assertEqual("Value must be >= 0 for field 'limit'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('57e4fd0c-35ae-4270-a9a2-01e9f89fdaf3')
def test_node_list_limit_not_int(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'nodes', {'limit': 'not-int'})
message = ex.resp_body['error']['message']
self.assertEqual("The value for limit must be an integer: 'not-int'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('733b1812-0ce6-4b21-ab07-3e4dfda10273')
def test_node_list_global_project_false(self):
ex = self.assertRaises(exceptions.Forbidden,
self.client.list_objs,
'nodes', {'global_project': 'True'})
message = ex.resp_body['error']['message']
self.assertEqual("You are not authorized to complete this operation.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('a9936950-0127-475f-bee6-700a553a7465')
def test_node_list_global_project_not_bool(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'nodes', {'global_project': 'not-bool'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid value 'not-bool' specified for "
"'global_project'", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('688270f0-9f08-43fe-8ff3-4598aa637493')
def test_node_list_invalid_sort(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'nodes', {'sort': 'bad-sort'})
message = ex.resp_body['error']['message']
self.assertEqual("Unsupported sort key 'bad-sort' for 'sort'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0ae75360-4445-4c20-8d26-55a86770ad21')
def test_node_list_invalid_marker(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'nodes', {'marker': 'bad-marker'})
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for marker is not a valid UUID: 'bad-marker'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d8d7dd1e-afd8-4921-83b2-c4ce73b1cb22')
def test_node_list_unsupported_status(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'nodes', {'status': ['bad-status']})
message = ex.resp_body['error']['message']
self.assertEqual("Field value ['bad-status'] is invalid",
str(message))

View File

@@ -1,141 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeOperation(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeOperation, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@utils.api_microversion('1.4')
@decorators.idempotent_id('a824fe2c-b8cc-455d-9ec1-73ff9606f9cc')
def test_reboot(self):
params = {
'reboot': {
'type': 'SOFT'
}
}
# Trigger node action
res = self.client.trigger_operation('nodes', self.node_id, params)
# Verfiy resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestNodeOperationNegative(base.BaseSenlinAPITest):
@utils.api_microversion('1.3')
@decorators.idempotent_id('4b3fc5dd-507a-4414-859c-44c87a2879fc')
def test_bad_microversion(self):
params = {'reboot': {}}
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_operation,
'nodes', 'FAKE_ID', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"API version '1.3' is not supported on this method.",
str(message))
@utils.api_microversion('1.4')
@decorators.idempotent_id('2b53a240-7ec6-4d92-bc2d-aaba2e63ee21')
def test_no_operation(self):
params = {}
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_operation,
'nodes', 'FAKE_ID', params)
message = ex.resp_body['error']['message']
self.assertEqual("No operation specified.",
str(message))
@utils.api_microversion('1.4')
@decorators.idempotent_id('b1c3a00b-e00c-4829-ba4a-475f8d34d1d9')
def test_multiple_ops(self):
params = {'foo': {}, 'bar': {}}
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_operation,
'nodes', 'FAKE_ID', params)
message = ex.resp_body['error']['message']
self.assertEqual("Multiple operations specified.",
str(message))
class TestNodeOperationNegativeEngineFailure(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeOperationNegativeEngineFailure, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@utils.api_microversion('1.4')
@decorators.idempotent_id('bbfbd693-4c46-4670-a9d3-5658a43eb0d5')
def test_node_not_found(self):
params = {'dance': {}}
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_operation, 'nodes',
'bbfbd693-4c46-4670-a9d3-5658a43eb0d5',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The node 'bbfbd693-4c46-4670-a9d3-5658a43eb0d5' could "
"not be found.", str(message))
@utils.api_microversion('1.4')
@decorators.idempotent_id('5c0a23c0-9efe-4d04-9208-0f11da690e79')
def test_operation_not_supported(self):
params = {'dance': {}}
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_operation,
'nodes', self.node_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The requested operation 'dance' is not supported by "
"the profile type 'os.nova.server-1.0'.", str(message))
@utils.api_microversion('1.4')
@decorators.idempotent_id('b00f1ef8-9ae6-4ed3-8622-566e7d0d3a75')
def test_operation_bad_params(self):
params = {'reboot': {'type': 'Unknown'}}
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_operation,
'nodes', self.node_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"'Unknown' must be one of the allowed values: SOFT, HARD.",
str(message))

View File

@@ -1,81 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeRecover(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeRecover, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.idempotent_id('217af65a-4029-40ce-a833-74faeac8c1f5')
def test_node_action_recover(self):
params = {
"recover": {
"operation": "REBUILD"
}
}
# Trigger node action
res = self.client.trigger_action('nodes', self.node_id, params=params)
# Verfiy resp code, body and location in headers
self.assertEqual(202, res['status'])
self.assertIn('actions', res['location'])
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
class TestNodeRecoverNegative(base.BaseSenlinAPITest):
@decorators.idempotent_id('60790d8a-fd9e-47d8-b9e2-c06aa7701c33')
def test_param_is_not_map(self):
# Recover action parameter is not a map
params = {
'recover': []
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.trigger_action,
'nodes', 'node_id', params)
message = ex.resp_body['error']['message']
self.assertEqual("The params provided is not a map.",
str(message))
@decorators.idempotent_id('694e59ce-551e-4e77-a684-e77781583e12')
def test_node_not_found(self):
params = {
'recover': {}
}
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.trigger_action,
'nodes', '694e59ce-551e-4e77-a684-e77781583e12',
params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The node '694e59ce-551e-4e77-a684-e77781583e12' could "
"not be found.", str(message))

View File

@@ -1,58 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeShow(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeShow, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@utils.api_microversion('1.0')
@decorators.idempotent_id('302372e8-efa2-4348-88dd-8a1829e5e26c')
def test_node_show(self):
expected_keys = ['cluster_id', 'created_at', 'data', 'domain',
'id', 'index', 'init_at', 'metadata', 'name',
'physical_id', 'profile_id', 'profile_name',
'project', 'role', 'status', 'status_reason',
'updated_at', 'user']
self._show_node(expected_keys)
@decorators.idempotent_id('81e65048-c68b-44a1-b351-d572bd68bf06')
@utils.api_microversion('1.13')
def test_node_show_v1_13(self):
expected_keys = ['cluster_id', 'created_at', 'data', 'domain',
'id', 'index', 'init_at', 'metadata', 'name',
'physical_id', 'profile_id', 'profile_name',
'project', 'role', 'status', 'status_reason',
'tainted', 'updated_at', 'user']
self._show_node(expected_keys)
def _show_node(self, expected_keys):
res = self.client.get_obj('nodes', self.node_id)
# Verify resp of node get API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
node = res['body']
for key in expected_keys:
self.assertIn(key, node)

View File

@@ -1,56 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeShowNegativeNotFound(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f7a2ed7e-bf92-452b-bc76-37a8bbde2169')
def test_node_show_not_found(self):
ex = self.assertRaises(exceptions.NotFound,
self.client.get_obj, 'nodes',
'f7a2ed7e-bf92-452b-bc76-37a8bbde2169')
message = ex.resp_body['error']['message']
self.assertEqual(
"The node 'f7a2ed7e-bf92-452b-bc76-37a8bbde2169' could "
"not be found.", str(message))
class TestNodeShowNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeShowNegativeBadRequest, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id1 = utils.create_a_node(self, profile_id, name='n-01')
self.node_id2 = utils.create_a_node(self, profile_id, name='n-01')
self.addCleanup(utils.delete_a_node, self, self.node_id1)
self.addCleanup(utils.delete_a_node, self, self.node_id2)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('49db9d49-76f1-47a7-9bd2-5e67311c453c')
def test_node_show_multiple_choice(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.get_obj,
'nodes', 'n-01')
message = ex.resp_body['error']['message']
self.assertEqual(
"Multiple results found matching the query criteria 'n-01'. "
"Please be more specific.", str(message))

View File

@@ -1,86 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeUpdate(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeUpdate, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.node_id = utils.create_a_node(self, profile_id, name='node1',
metadata={'k1': 'v1'},
role='member')
self.addCleanup(utils.delete_a_node, self, self.node_id)
@utils.api_microversion('1.0')
@decorators.idempotent_id('bd8a39bf-eee0-4056-aec0-0d8f8706efea')
def test_node_update_basic_properties(self):
# Update basic properties of node
params = {
'node': {
'name': 'node_new_name',
'role': 'admin',
'metadata': {'k2': 'v2'}
}
}
res = self.client.update_obj('nodes', self.node_id, params)
# Verify resp of node update API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
node = res['body']
for key in ['cluster_id', 'created_at', 'data', 'domain', 'id',
'index', 'init_at', 'metadata', 'name', 'physical_id',
'profile_id', 'profile_name', 'project', 'role', 'status',
'status_reason', 'updated_at', 'user']:
self.assertIn(key, node)
# Wait for node update to be done before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')
@decorators.idempotent_id('f8816054-6864-4c55-8770-6fdd66239fda')
@utils.api_microversion('1.13')
def test_node_update_basic_properties_v1_13(self):
# Update basic properties of node
params = {
'node': {
'name': 'node_new_name',
'role': 'admin',
'metadata': {'k2': 'v2'},
'tainted': True
}
}
res = self.client.update_obj('nodes', self.node_id, params)
# Verify resp of node update API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
node = res['body']
for key in ['cluster_id', 'created_at', 'data', 'domain', 'id',
'index', 'init_at', 'metadata', 'name', 'physical_id',
'profile_id', 'profile_name', 'project', 'role', 'status',
'status_reason', 'tainted', 'updated_at', 'user']:
self.assertIn(key, node)
# Wait for node update to be done before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')

View File

@@ -1,172 +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 copy
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeUpdateNegativeNotFound(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('608addc9-cbbe-45cd-a00a-495cae7db400')
def test_node_update_node_not_found(self):
ex = self.assertRaises(exceptions.NotFound,
self.client.update_obj, 'nodes',
'608addc9-cbbe-45cd-a00a-495cae7db400',
{'node': {'name': 'new-name'}})
message = ex.resp_body['error']['message']
self.assertEqual(
"The node '608addc9-cbbe-45cd-a00a-495cae7db400' could "
"not be found.", str(message))
class TestNodeUpdateNegativeProfileNotFound(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeUpdateNegativeProfileNotFound, self).setUp()
# Create a profile
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# Create a node
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('3243dd63-1008-4181-849a-0058af975800')
def test_node_update_profile_not_found(self):
# Provided profile can not be found
params = {
'node': {
'profile_id': '3243dd63-1008-4181-849a-0058af975800',
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj,
'nodes', self.node_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The specified profile '3243dd63-1008-4181-849a-0058af975800' "
"could not be found.", str(message))
class TestNodeUpdateNegativeProfileMultichoices(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeUpdateNegativeProfileMultichoices, self).setUp()
# Create a profile
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# Create a node
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
# Create two new profiles of the same type with the same name
new_spec = copy.deepcopy(constants.spec_nova_server)
new_spec['properties']['flavor'] = 'new_flavor'
new_profile_id1 = utils.create_a_profile(self, new_spec, name='p-nova')
new_profile_id2 = utils.create_a_profile(self, new_spec, name='p-nova')
self.addCleanup(utils.delete_a_profile, self, new_profile_id1)
self.addCleanup(utils.delete_a_profile, self, new_profile_id2)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0d73eff6-1916-43e1-9518-31820fcfe01f')
def test_node_update_profile_multichoices(self):
# Multiple profiles are found for given name
params = {
'node': {
'profile_id': 'p-nova',
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj,
'nodes', self.node_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Multiple results found matching the query criteria 'p-nova'. "
"Please be more specific.", str(message))
class TestNodeUpdateNegativeProfileTypeUnmatch(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeUpdateNegativeProfileTypeUnmatch, self).setUp()
# Create a profile
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# Create a node
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
# Create a new profile of different type
self.new_profile_id = utils.create_a_profile(
self, spec=constants.spec_heat_stack)
self.addCleanup(utils.delete_a_profile, self, self.new_profile_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('ec5821d8-142e-4fff-a998-81428ecc239c')
def test_node_update_profile_type_unmatch(self):
# New profile type is different from original cone
params = {
'node': {
'profile_id': self.new_profile_id,
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj,
'nodes', self.node_id, params)
message = ex.resp_body['error']['message']
self.assertEqual(
"Cannot update a node to a different profile type, "
"operation aborted.", str(message))
class TestNodeUpdateNegativeNoPropertyUpdated(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeUpdateNegativeNoPropertyUpdated, self).setUp()
# Create a profile
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
self.profile_id = profile_id
# Create a node
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('ed6f385d-780b-4562-928d-3e00f27550d2')
def test_node_update_no_property_updated(self):
# Provided profile can not be found
params = {
'node': {}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.update_obj,
'nodes', self.node_id, params)
message = ex.resp_body['error']['message']
self.assertEqual("No property needs an update.",
str(message))

View File

@@ -1,60 +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 copy
from tempest.lib import decorators
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestNodeUpdateProfile(base.BaseSenlinAPITest):
def setUp(self):
super(TestNodeUpdateProfile, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
new_spec = copy.deepcopy(constants.spec_nova_server)
new_spec['properties']['flavor'] = 'new_flavor'
new_spec['properties']['image'] = 'new_image'
self.new_profile_id = utils.create_a_profile(self, new_spec)
self.addCleanup(utils.delete_a_profile, self, self.new_profile_id)
self.node_id = utils.create_a_node(self, profile_id)
self.addCleanup(utils.delete_a_node, self, self.node_id)
@decorators.idempotent_id('de9465f2-95b4-41ce-81f5-b092967cb2b8')
def test_node_update_profile(self):
# Update node with new profile
params = {
'node': {
'profile_id': self.new_profile_id
}
}
res = self.client.update_obj('nodes', self.node_id, params)
# Verify resp of node update API
self.assertEqual(202, res['status'])
self.assertIsNotNone(res['body'])
self.assertIn('actions', res['location'])
node = res['body']
for key in ['cluster_id', 'created_at', 'data', 'domain', 'id',
'index', 'init_at', 'metadata', 'name', 'physical_id',
'profile_id', 'profile_name', 'project', 'role', 'status',
'status_reason', 'updated_at', 'user']:
self.assertIn(key, node)
# Wait for node update to be done before moving on
action_id = res['location'].split('/actions/')[1]
self.client.wait_for_status('actions', action_id, 'SUCCEEDED')

View File

@@ -1,47 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.tests.api import base
class TestPolicyCreate(base.BaseSenlinAPITest):
def setUp(self):
super(TestPolicyCreate, self).setUp()
self.policy_id = None
@decorators.idempotent_id('f50648d9-f38c-479a-a82b-3c6909733496')
def test_policy_create(self):
params = {
'policy': {
'name': 'test-scaling-policy',
'spec': constants.spec_scaling_policy,
}
}
res = self.client.create_obj('policies', params)
# Verify resp of receiver create API
self.assertEqual(201, res['status'])
self.assertIsNotNone(res['body'])
policy = res['body']
self.addCleanup(self.client.delete_obj, 'policies', policy['id'])
for key in ['created_at', 'data', 'domain', 'id', 'name', 'project',
'spec', 'type', 'updated_at', 'user']:
self.assertIn(key, policy)
self.assertEqual('test-scaling-policy', policy['name'])
self.assertEqual('senlin.policy.scaling-1.0', policy['type'])
self.assertEqual(constants.spec_scaling_policy, policy['spec'])

View File

@@ -1,112 +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 copy
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import constants
from senlin_tempest_plugin.tests.api import base
class TestPolicyCreateNegativeBadRequest(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('3fea4aa9-6dee-4202-8611-cf2d008a4d42')
def test_policy_create_policy_data_not_specified(self):
params = {
'policy': {
'name': 'test-policy'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'policies', params)
message = ex.resp_body['error']['message']
self.assertEqual("'spec' is a required property", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('4a4d6c83-f0fa-4c9e-914b-d89478903d95')
def test_policy_create_name_not_specified(self):
params = {
'policy': {
'spec': constants.spec_scaling_policy
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'policies', params)
message = ex.resp_body['error']['message']
self.assertEqual("'name' is a required property", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('b898de6c-996a-4bc3-bdef-6490e62fb3b0')
def test_policy_create_invalid_param(self):
params = {
'policy': {
'name': 'bar',
'spec': {},
'boo': 'foo'
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.create_obj,
'policies', params)
message = ex.resp_body['error']['message']
self.assertIn("Additional properties are not allowed", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('1c0ed145-bca6-4e53-b222-44fc6978eb1f')
def test_policy_create_policy_type_incorrect(self):
spec = copy.deepcopy(constants.spec_scaling_policy)
spec['type'] = 'senlin.policy.bogus'
params = {
'policy': {
'name': 'test-policy',
'spec': spec
}
}
# Verify badrequest exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.create_obj,
'policies', params)
message = ex.resp_body['error']['message']
self.assertEqual(
"The policy_type 'senlin.policy.bogus-1.0' could "
"not be found.", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f55dc7eb-9863-49c2-b001-368d2057c53c')
def test_policy_create_spec_validation_failed(self):
spec = copy.deepcopy(constants.spec_scaling_policy)
spec['properties']['bogus'] = 'foo'
params = {
'policy': {
'name': 'test-policy',
'spec': spec
}
}
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.ServerFault,
self.client.create_obj,
'policies', params)
message = ex.resp_body['error']['message']
self.assertEqual("Unrecognizable spec item 'bogus'",
str(message))

View File

@@ -1,30 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestPolicyDelete(base.BaseSenlinAPITest):
def setUp(self):
super(TestPolicyDelete, self).setUp()
self.policy_id = utils.create_a_policy(self)
@decorators.idempotent_id('b707e4e3-3d42-4a9f-9a09-3e330959b498')
def test_policy_delete(self):
# Verify resp of policy delete API
res = self.client.delete_obj('policies', self.policy_id)
self.assertEqual(204, res['status'])
self.assertIsNone(res['body'])

View File

@@ -1,87 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestPolicyDeleteNegativeConflict(base.BaseSenlinAPITest):
def setUp(self):
super(TestPolicyDeleteNegativeConflict, self).setUp()
profile_id = utils.create_a_profile(self)
self.addCleanup(utils.delete_a_profile, self, profile_id)
cluster_id = utils.create_a_cluster(self, profile_id)
self.addCleanup(utils.delete_a_cluster, self, cluster_id)
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
utils.cluster_attach_policy(self, cluster_id, self.policy_id)
self.addCleanup(utils.cluster_detach_policy, self, cluster_id,
self.policy_id)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('b8b8fca8-962f-4cad-bfca-76683df7b617')
def test_policy_delete_conflict(self):
# Verify conflict exception(409) is raised.
ex = self.assertRaises(exceptions.Conflict,
self.client.delete_obj,
'policies', self.policy_id)
message = ex.resp_body['error']['message']
self.assertEqual(
"The policy '%s' cannot be deleted: still attached to some "
"clusters." % self.policy_id, str(message))
class TestPolicyDeleteNegativeNotFound(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('5591416f-4646-46c2-83b4-231e72aa4bfe')
def test_policy_delete_not_found(self):
# Verify notfound exception(404) is raised.
ex = self.assertRaises(exceptions.NotFound,
self.client.delete_obj, 'policies',
'5591416f-4646-46c2-83b4-231e72aa4bfe')
message = ex.resp_body['error']['message']
self.assertEqual(
"The policy '5591416f-4646-46c2-83b4-231e72aa4bfe' "
"could not be found.", str(message))
class TestPolicyDeleteNegativeBadRequest(base.BaseSenlinAPITest):
def setUp(self):
super(TestPolicyDeleteNegativeBadRequest, self).setUp()
self.policy_id1 = utils.create_a_policy(self, name='p-01')
self.addCleanup(utils.delete_a_policy, self, self.policy_id1)
self.policy_id2 = utils.create_a_policy(self, name='p-01')
self.addCleanup(utils.delete_a_policy, self, self.policy_id2)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d6f35043-2db5-49ff-8bc4-ba14a652f748')
def test_policy_delete_multiple_choice(self):
# Verify badrequest exception(400) is raised.
ex = self.assertRaises(exceptions.BadRequest,
self.client.delete_obj,
'policies', 'p-01')
message = ex.resp_body['error']['message']
self.assertEqual(
"Multiple results found matching the query criteria 'p-01'. "
"Please be more specific.", str(message))

View File

@@ -1,41 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestPolicyList(base.BaseSenlinAPITest):
def setUp(self):
super(TestPolicyList, self).setUp()
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
@decorators.idempotent_id('67ce5d15-c1fd-402f-bcd8-2974dbd93da8')
def test_policy_list(self):
res = self.client.list_objs('policies')
# Verify resp of policy list API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
policies = res['body']
ids = []
for policy in policies:
for key in ['created_at', 'data', 'domain', 'id', 'name',
'project', 'spec', 'type', 'updated_at', 'user']:
self.assertIn(key, policy)
ids.append(policy['id'])
self.assertIn(self.policy_id, ids)

View File

@@ -1,85 +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.
from tempest.lib import decorators
from tempest.lib import exceptions
from senlin_tempest_plugin.tests.api import base
class TestPolicyListNegativeBadRequest(base.BaseSenlinAPITest):
@decorators.attr(type=['negative'])
@decorators.idempotent_id('b936b936-f891-4389-bbeb-f81b7dc3c688')
def test_policy_list_invalid_params(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'policies', {'bogus': 'foo'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid parameter bogus", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('04ce3766-acf9-4549-91c8-e6ffdf7bafbd')
def test_policy_list_limit_not_int(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'policies', {'limit': 'not-int'})
message = ex.resp_body['error']['message']
self.assertEqual("The value for limit must be an integer: 'not-int'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('cfd50d13-5ed8-48d9-b03f-95480ba06fad')
def test_policy_list_global_project_false(self):
ex = self.assertRaises(exceptions.Forbidden,
self.client.list_objs,
'policies', {'global_project': 'True'})
message = ex.resp_body['error']['message']
self.assertEqual("You are not authorized to complete this operation.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('ab477cf8-6c37-4762-bd85-d55b46444d8f')
def test_policy_list_global_project_not_bool(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'policies', {'global_project': 'not-bool'})
message = ex.resp_body['error']['message']
self.assertEqual("Invalid value 'not-bool' specified for "
"'global_project'", str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('f5bd7807-2b3e-43b2-8ed6-7bdb5e9af46b')
def test_policy_list_invalid_sort(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'policies', {'sort': 'bad-sort'})
message = ex.resp_body['error']['message']
self.assertEqual("Unsupported sort key 'bad-sort' for 'sort'.",
str(message))
@decorators.attr(type=['negative'])
@decorators.idempotent_id('077f39f0-bb2a-4de8-9568-2ed49e99b720')
def test_policy_list_invalid_marker(self):
ex = self.assertRaises(exceptions.BadRequest,
self.client.list_objs,
'policies', {'marker': 'bad-marker'})
message = ex.resp_body['error']['message']
self.assertEqual(
"The value for marker is not a valid UUID: 'bad-marker'.",
str(message))

View File

@@ -1,37 +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.
from tempest.lib import decorators
from senlin_tempest_plugin.common import utils
from senlin_tempest_plugin.tests.api import base
class TestPolicyShow(base.BaseSenlinAPITest):
def setUp(self):
super(TestPolicyShow, self).setUp()
self.policy_id = utils.create_a_policy(self)
self.addCleanup(utils.delete_a_policy, self, self.policy_id)
@decorators.idempotent_id('7ab18be1-e554-452d-91ac-9b5e5c87430b')
def test_policy_show(self):
res = self.client.get_obj('policies', self.policy_id)
# Verify resp of policy show API
self.assertEqual(200, res['status'])
self.assertIsNone(res['location'])
self.assertIsNotNone(res['body'])
policy = res['body']
for key in ['created_at', 'data', 'domain', 'id', 'name', 'project',
'spec', 'type', 'updated_at', 'user']:
self.assertIn(key, policy)

Some files were not shown because too many files have changed in this diff Show More