Refactored existing tempest API tests
We must set up Tempest for Watcher (http://docs.openstack.org/developer/tempest/configuration.html) to run integration tests inside devstack environment. This patchset is a refactoring of the stale Tempest tests to now use the latest Tempest coding standards (like using plugins and credentials factory). This commit will have an effect on the doc as we need to integrate Tempest in the Watcher documentation. DocImpact Partially Implements: blueprint tempest-basic-set-up Change-Id: I7600ff8a28d524b56c7dd4903ac4d203634ae412
This commit is contained in:
committed by
Jean-Emile DARTOIS
parent
8832ad78e2
commit
595b13a622
@@ -21,6 +21,7 @@ classifier =
|
||||
[files]
|
||||
packages =
|
||||
watcher
|
||||
watcher_tempest_plugin
|
||||
data_files =
|
||||
etc/ = etc/*
|
||||
|
||||
@@ -38,6 +39,9 @@ console_scripts =
|
||||
watcher-decision-engine = watcher.cmd.decisionengine:main
|
||||
watcher-applier = watcher.cmd.applier:main
|
||||
|
||||
tempest.test_plugins =
|
||||
watcher_tests = watcher_tempest_plugin.plugin:WatcherTempestPlugin
|
||||
|
||||
watcher.database.migration_backend =
|
||||
sqlalchemy = watcher.db.sqlalchemy.migration
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
..
|
||||
Except where otherwise noted, this document is licensed under Creative
|
||||
Commons Attribution 3.0 License. You can view the license at:
|
||||
|
||||
https://creativecommons.org/licenses/by/3.0/
|
||||
|
||||
Tempest Field Guide to Infrastructure Optimization API tests
|
||||
============================================================
|
||||
|
||||
|
||||
What are these tests?
|
||||
---------------------
|
||||
|
||||
These tests stress the OpenStack Infrastructure Optimization API provided by
|
||||
Watcher.
|
||||
|
||||
|
||||
Why are these tests in tempest?
|
||||
------------------------------
|
||||
|
||||
The purpose of these tests is to exercise the various APIs provided by Watcher
|
||||
for optimizing the infrastructure.
|
||||
|
||||
|
||||
Scope of these tests
|
||||
--------------------
|
||||
|
||||
The Infrastructure Optimization API test perform basic CRUD operations on the Watcher node
|
||||
inventory. They do not actually perform placement or migration of virtual resources. It is important
|
||||
to note that all Watcher API actions are admin operations meant to be used
|
||||
either by cloud operators.
|
||||
@@ -1,130 +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 tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import exceptions as lib_exc
|
||||
|
||||
from tempest import clients_infra_optim as clients
|
||||
from tempest.common import credentials
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
# Resources must be deleted in a specific order, this list
|
||||
# defines the resource types to clean up, and the correct order.
|
||||
RESOURCE_TYPES = ['audit_template']
|
||||
# RESOURCE_TYPES = ['action', 'action_plan', 'audit', 'audit_template']
|
||||
|
||||
|
||||
def creates(resource):
|
||||
"""Decorator that adds resources to the appropriate cleanup list."""
|
||||
|
||||
def decorator(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper(cls, *args, **kwargs):
|
||||
resp, body = f(cls, *args, **kwargs)
|
||||
|
||||
if 'uuid' in body:
|
||||
cls.created_objects[resource].add(body['uuid'])
|
||||
|
||||
return resp, body
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
class BaseInfraOptimTest(test.BaseTestCase):
|
||||
"""Base class for Infrastructure Optimization API tests."""
|
||||
|
||||
@classmethod
|
||||
# def skip_checks(cls):
|
||||
# super(BaseInfraOptimTest, cls).skip_checks()
|
||||
# if not CONF.service_available.watcher:
|
||||
# skip_msg = \
|
||||
# ('%s skipped as Watcher is not available' % cls.__name__)
|
||||
# raise cls.skipException(skip_msg)
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
super(BaseInfraOptimTest, cls).setup_credentials()
|
||||
if (not hasattr(cls, 'isolated_creds') or
|
||||
not cls.isolated_creds.name == cls.__name__):
|
||||
cls.isolated_creds = credentials.get_isolated_credentials(
|
||||
name=cls.__name__, network_resources=cls.network_resources)
|
||||
cls.mgr = clients.Manager(cls.isolated_creds.get_admin_creds())
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseInfraOptimTest, cls).setup_clients()
|
||||
cls.client = cls.mgr.io_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseInfraOptimTest, cls).resource_setup()
|
||||
|
||||
cls.created_objects = {}
|
||||
for resource in RESOURCE_TYPES:
|
||||
cls.created_objects[resource] = set()
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
"""Ensure that all created objects get destroyed."""
|
||||
|
||||
try:
|
||||
for resource in RESOURCE_TYPES:
|
||||
uuids = cls.created_objects[resource]
|
||||
delete_method = getattr(cls.client, 'delete_%s' % resource)
|
||||
for u in uuids:
|
||||
delete_method(u, ignore_errors=lib_exc.NotFound)
|
||||
finally:
|
||||
super(BaseInfraOptimTest, cls).resource_cleanup()
|
||||
|
||||
@classmethod
|
||||
@creates('audit_template')
|
||||
def create_audit_template(cls, description=None, expect_errors=False):
|
||||
"""Wrapper utility for creating test audit_template.
|
||||
|
||||
:param description: A description of the audit template.
|
||||
if not supplied, a random value will be generated.
|
||||
:return: Created audit template.
|
||||
"""
|
||||
|
||||
description = description or data_utils.rand_name(
|
||||
'test-audit_template')
|
||||
resp, body = cls.client.create_audit_template(description=description)
|
||||
return resp, body
|
||||
|
||||
@classmethod
|
||||
def delete_audit_template(cls, audit_template_id):
|
||||
"""Deletes a audit_template having the specified UUID.
|
||||
|
||||
:param uuid: The unique identifier of the audit_template.
|
||||
:return: Server response.
|
||||
"""
|
||||
|
||||
resp, body = cls.client.delete_audit_template(audit_template_id)
|
||||
|
||||
if audit_template_id in cls.created_objects['audit_template']:
|
||||
cls.created_objects['audit_template'].remove(audit_template_id)
|
||||
|
||||
return resp
|
||||
|
||||
def validate_self_link(self, resource, uuid, link):
|
||||
"""Check whether the given self link formatted correctly."""
|
||||
expected_link = "{base}/{pref}/{res}/{uuid}".format(
|
||||
base=self.client.base_url,
|
||||
pref=self.client.uri_prefix,
|
||||
res=resource,
|
||||
uuid=uuid)
|
||||
self.assertEqual(expected_link, link)
|
||||
@@ -1,56 +0,0 @@
|
||||
..
|
||||
Except where otherwise noted, this document is licensed under Creative
|
||||
Commons Attribution 3.0 License. You can view the license at:
|
||||
|
||||
https://creativecommons.org/licenses/by/3.0/
|
||||
|
||||
.. _cli_field_guide:
|
||||
|
||||
Tempest Field Guide to CLI tests
|
||||
================================
|
||||
|
||||
|
||||
What are these tests?
|
||||
---------------------
|
||||
The cli tests test the various OpenStack command line interface tools
|
||||
to ensure that they minimally function. The current scope is read only
|
||||
operations on a cloud that are hard to test via unit tests.
|
||||
|
||||
|
||||
Why are these tests in tempest?
|
||||
-------------------------------
|
||||
These tests exist here because it is extremely difficult to build a
|
||||
functional enough environment in the python-\*client unit tests to
|
||||
provide this kind of testing. Because we already put up a cloud in the
|
||||
gate with devstack + tempest it was decided it was better to have
|
||||
these as a side tree in tempest instead of another QA effort which
|
||||
would split review time.
|
||||
|
||||
|
||||
Scope of these tests
|
||||
--------------------
|
||||
This should stay limited to the scope of testing the cli. Functional
|
||||
testing of the cloud should be elsewhere, this is about exercising the
|
||||
cli code.
|
||||
|
||||
|
||||
Example of a good test
|
||||
----------------------
|
||||
Tests should be isolated to a single command in one of the python
|
||||
clients.
|
||||
|
||||
Tests should not modify the cloud.
|
||||
|
||||
If a test is validating the cli for bad data, it should do it with
|
||||
assertRaises.
|
||||
|
||||
A reasonable example of an existing test is as follows::
|
||||
|
||||
def test_admin_list(self):
|
||||
self.nova('list')
|
||||
self.nova('list', params='--all-tenants 1')
|
||||
self.nova('list', params='--all-tenants 0')
|
||||
self.assertRaises(subprocess.CalledProcessError,
|
||||
self.nova,
|
||||
'list',
|
||||
params='--all-tenants bad')
|
||||
@@ -1,126 +0,0 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import functools
|
||||
|
||||
from tempest_lib.cli import base
|
||||
from tempest_lib.cli import output_parser
|
||||
import testtools
|
||||
|
||||
from tempest.common import credentials
|
||||
from tempest import config
|
||||
from tempest import exceptions
|
||||
from tempest.openstack.common import versionutils
|
||||
from tempest import test
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
def check_client_version(client, version):
|
||||
"""Checks if the client's version is compatible with the given version
|
||||
|
||||
@param client: The client to check.
|
||||
@param version: The version to compare against.
|
||||
@return: True if the client version is compatible with the given version
|
||||
parameter, False otherwise.
|
||||
"""
|
||||
current_version = base.execute(client, '', params='--version',
|
||||
merge_stderr=True, cli_dir=CONF.cli.cli_dir)
|
||||
|
||||
if not current_version.strip():
|
||||
raise exceptions.TempestException('"%s --version" output was empty' %
|
||||
client)
|
||||
|
||||
return versionutils.is_compatible(version, current_version,
|
||||
same_major=False)
|
||||
|
||||
|
||||
def min_client_version(*args, **kwargs):
|
||||
"""A decorator to skip tests if the client used isn't of the right version.
|
||||
|
||||
@param client: The client command to run. For python-novaclient, this is
|
||||
'nova', for python-cinderclient this is 'cinder', etc.
|
||||
@param version: The minimum version required to run the CLI test.
|
||||
"""
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*func_args, **func_kwargs):
|
||||
if not check_client_version(kwargs['client'], kwargs['version']):
|
||||
msg = "requires %s client version >= %s" % (kwargs['client'],
|
||||
kwargs['version'])
|
||||
raise testtools.TestCase.skipException(msg)
|
||||
return func(*func_args, **func_kwargs)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
class ClientTestBase(test.BaseTestCase):
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(ClientTestBase, cls).skip_checks()
|
||||
if not CONF.identity_feature_enabled.api_v2:
|
||||
raise cls.skipException("CLI clients rely on identity v2 API, "
|
||||
"which is configured as not available")
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
if not CONF.cli.enabled:
|
||||
msg = "cli testing disabled"
|
||||
raise cls.skipException(msg)
|
||||
super(ClientTestBase, cls).resource_setup()
|
||||
cls.isolated_creds = credentials.get_isolated_credentials(cls.__name__)
|
||||
cls.creds = cls.isolated_creds.get_admin_creds()
|
||||
|
||||
def _get_clients(self):
|
||||
clients = base.CLIClient(self.creds.username,
|
||||
self.creds.password,
|
||||
self.creds.tenant_name,
|
||||
CONF.identity.uri, CONF.cli.cli_dir)
|
||||
return clients
|
||||
|
||||
# TODO(mtreinish): The following code is basically copied from tempest-lib.
|
||||
# The base cli test class in tempest-lib 0.0.1 doesn't work as a mixin like
|
||||
# is needed here. The code below should be removed when tempest-lib
|
||||
# provides a way to provide this functionality
|
||||
def setUp(self):
|
||||
super(ClientTestBase, self).setUp()
|
||||
self.clients = self._get_clients()
|
||||
self.parser = output_parser
|
||||
|
||||
def assertTableStruct(self, items, field_names):
|
||||
"""Verify that all items has keys listed in field_names.
|
||||
|
||||
:param items: items to assert are field names in the output table
|
||||
:type items: list
|
||||
:param field_names: field names from the output table of the cmd
|
||||
:type field_names: list
|
||||
"""
|
||||
for item in items:
|
||||
for field in field_names:
|
||||
self.assertIn(field, item)
|
||||
|
||||
def assertFirstLineStartsWith(self, lines, beginning):
|
||||
"""Verify that the first line starts with a string
|
||||
|
||||
:param lines: strings for each line of output
|
||||
:type lines: list
|
||||
:param beginning: verify this is at the beginning of the first line
|
||||
:type beginning: string
|
||||
"""
|
||||
self.assertTrue(lines[0].startswith(beginning),
|
||||
msg=('Beginning of first line has invalid content: %s'
|
||||
% lines[:3]))
|
||||
@@ -1 +0,0 @@
|
||||
This directory consists of simple read only python client tests.
|
||||
@@ -1,220 +0,0 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from tempest_lib import exceptions
|
||||
import testtools
|
||||
|
||||
from tempest import cli
|
||||
from tempest import clients
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SimpleReadOnlyCinderClientTest(cli.ClientTestBase):
|
||||
"""Basic, read-only tests for Cinder CLI client.
|
||||
|
||||
Checks return values and output of read-only commands.
|
||||
These tests do not presume any content, nor do they create
|
||||
their own. They only verify the structure of output if present.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
# if not CONF.service_available.cinder:
|
||||
# msg = ("%s skipped as Cinder is not available" % cls.__name__)
|
||||
# raise cls.skipException(msg)
|
||||
super(SimpleReadOnlyCinderClientTest, cls).resource_setup()
|
||||
id_cl = clients.AdminManager().identity_client
|
||||
tenant = id_cl.get_tenant_by_name(CONF.identity.admin_tenant_name)
|
||||
cls.admin_tenant_id = tenant['id']
|
||||
|
||||
def cinder(self, *args, **kwargs):
|
||||
return self.clients.cinder(*args,
|
||||
endpoint_type=CONF.volume.endpoint_type,
|
||||
**kwargs)
|
||||
|
||||
@test.idempotent_id('229bc6dc-d804-4668-b753-b590caf63061')
|
||||
def test_cinder_fake_action(self):
|
||||
self.assertRaises(exceptions.CommandFailed,
|
||||
self.cinder,
|
||||
'this-does-not-exist')
|
||||
|
||||
@test.idempotent_id('77140216-14db-4fc5-a246-e2a587e9e99b')
|
||||
def test_cinder_absolute_limit_list(self):
|
||||
roles = self.parser.listing(self.cinder('absolute-limits'))
|
||||
self.assertTableStruct(roles, ['Name', 'Value'])
|
||||
|
||||
@test.idempotent_id('2206b9ce-1a36-4a0a-a129-e5afc7cee1dd')
|
||||
def test_cinder_backup_list(self):
|
||||
backup_list = self.parser.listing(self.cinder('backup-list'))
|
||||
self.assertTableStruct(backup_list, ['ID', 'Volume ID', 'Status',
|
||||
'Name', 'Size', 'Object Count',
|
||||
'Container'])
|
||||
|
||||
@test.idempotent_id('c7f50346-cd99-4e0b-953f-796ff5f47295')
|
||||
def test_cinder_extra_specs_list(self):
|
||||
extra_specs_list = self.parser.listing(self.cinder('extra-specs-list'))
|
||||
self.assertTableStruct(extra_specs_list, ['ID', 'Name', 'extra_specs'])
|
||||
|
||||
@test.idempotent_id('9de694cb-b40b-442c-a30c-5f9873e144f7')
|
||||
def test_cinder_volumes_list(self):
|
||||
list = self.parser.listing(self.cinder('list'))
|
||||
self.assertTableStruct(list, ['ID', 'Status', 'Name', 'Size',
|
||||
'Volume Type', 'Bootable',
|
||||
'Attached to'])
|
||||
self.cinder('list', params='--all-tenants 1')
|
||||
self.cinder('list', params='--all-tenants 0')
|
||||
self.assertRaises(exceptions.CommandFailed,
|
||||
self.cinder,
|
||||
'list',
|
||||
params='--all-tenants bad')
|
||||
|
||||
@test.idempotent_id('56f7c15c-ee82-4f23-bbe8-ce99b66da493')
|
||||
def test_cinder_quota_class_show(self):
|
||||
"""This CLI can accept and string as param."""
|
||||
roles = self.parser.listing(self.cinder('quota-class-show',
|
||||
params='abc'))
|
||||
self.assertTableStruct(roles, ['Property', 'Value'])
|
||||
|
||||
@test.idempotent_id('a919a811-b7f0-47a7-b4e5-f3eb674dd200')
|
||||
def test_cinder_quota_defaults(self):
|
||||
"""This CLI can accept and string as param."""
|
||||
roles = self.parser.listing(self.cinder('quota-defaults',
|
||||
params=self.admin_tenant_id))
|
||||
self.assertTableStruct(roles, ['Property', 'Value'])
|
||||
|
||||
@test.idempotent_id('18166673-ffa8-4df3-b60c-6375532288bc')
|
||||
def test_cinder_quota_show(self):
|
||||
"""This CLI can accept and string as param."""
|
||||
roles = self.parser.listing(self.cinder('quota-show',
|
||||
params=self.admin_tenant_id))
|
||||
self.assertTableStruct(roles, ['Property', 'Value'])
|
||||
|
||||
@test.idempotent_id('b2c66ed9-ca96-4dc4-94cc-8083e664e516')
|
||||
def test_cinder_rate_limits(self):
|
||||
rate_limits = self.parser.listing(self.cinder('rate-limits'))
|
||||
self.assertTableStruct(rate_limits, ['Verb', 'URI', 'Value', 'Remain',
|
||||
'Unit', 'Next_Available'])
|
||||
|
||||
@test.idempotent_id('7a19955b-807c-481a-a2ee-9d76733eac28')
|
||||
@testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
|
||||
'Volume snapshot not available.')
|
||||
def test_cinder_snapshot_list(self):
|
||||
snapshot_list = self.parser.listing(self.cinder('snapshot-list'))
|
||||
self.assertTableStruct(snapshot_list, ['ID', 'Volume ID', 'Status',
|
||||
'Name', 'Size'])
|
||||
|
||||
@test.idempotent_id('6e54ecd9-7ba9-490d-8e3b-294b67139e73')
|
||||
def test_cinder_type_list(self):
|
||||
type_list = self.parser.listing(self.cinder('type-list'))
|
||||
self.assertTableStruct(type_list, ['ID', 'Name'])
|
||||
|
||||
@test.idempotent_id('2c363583-24a0-4980-b9cb-b50c0d241e82')
|
||||
def test_cinder_list_extensions(self):
|
||||
roles = self.parser.listing(self.cinder('list-extensions'))
|
||||
self.assertTableStruct(roles, ['Name', 'Summary', 'Alias', 'Updated'])
|
||||
|
||||
@test.idempotent_id('691bd6df-30ad-4be7-927b-a02d62aaa38a')
|
||||
def test_cinder_credentials(self):
|
||||
credentials = self.parser.listing(self.cinder('credentials'))
|
||||
self.assertTableStruct(credentials, ['User Credentials', 'Value'])
|
||||
|
||||
@test.idempotent_id('5c6d71a3-4904-4a3a-aec9-7fd4aa830e95')
|
||||
def test_cinder_availability_zone_list(self):
|
||||
zone_list = self.parser.listing(self.cinder('availability-zone-list'))
|
||||
self.assertTableStruct(zone_list, ['Name', 'Status'])
|
||||
|
||||
@test.idempotent_id('9b0fd5a6-f955-42b9-a42f-6f542a80b9a3')
|
||||
def test_cinder_endpoints(self):
|
||||
out = self.cinder('endpoints')
|
||||
tables = self.parser.tables(out)
|
||||
for table in tables:
|
||||
headers = table['headers']
|
||||
self.assertTrue(2 >= len(headers))
|
||||
self.assertEqual('Value', headers[1])
|
||||
|
||||
@test.idempotent_id('301b5ae1-9591-4e9f-999c-d525a9bdf822')
|
||||
def test_cinder_service_list(self):
|
||||
service_list = self.parser.listing(self.cinder('service-list'))
|
||||
self.assertTableStruct(service_list, ['Binary', 'Host', 'Zone',
|
||||
'Status', 'State', 'Updated_at'])
|
||||
|
||||
@test.idempotent_id('7260ae52-b462-461e-9048-36d0bccf92c6')
|
||||
def test_cinder_transfer_list(self):
|
||||
transfer_list = self.parser.listing(self.cinder('transfer-list'))
|
||||
self.assertTableStruct(transfer_list, ['ID', 'Volume ID', 'Name'])
|
||||
|
||||
@test.idempotent_id('0976dea8-14f3-45a9-8495-3617fc4fbb13')
|
||||
def test_cinder_bash_completion(self):
|
||||
self.cinder('bash-completion')
|
||||
|
||||
@test.idempotent_id('b7c00361-be80-4512-8735-5f98fc54f2a9')
|
||||
def test_cinder_qos_list(self):
|
||||
qos_list = self.parser.listing(self.cinder('qos-list'))
|
||||
self.assertTableStruct(qos_list, ['ID', 'Name', 'Consumer', 'specs'])
|
||||
|
||||
@test.idempotent_id('2e92dc6e-22b5-4d94-abfc-b543b0c50a89')
|
||||
def test_cinder_encryption_type_list(self):
|
||||
encrypt_list = self.parser.listing(self.cinder('encryption-type-list'))
|
||||
self.assertTableStruct(encrypt_list, ['Volume Type ID', 'Provider',
|
||||
'Cipher', 'Key Size',
|
||||
'Control Location'])
|
||||
|
||||
@test.idempotent_id('0ee6cb4c-8de6-4811-a7be-7f4bb75b80cc')
|
||||
def test_admin_help(self):
|
||||
help_text = self.cinder('help')
|
||||
lines = help_text.split('\n')
|
||||
self.assertFirstLineStartsWith(lines, 'usage: cinder')
|
||||
|
||||
commands = []
|
||||
cmds_start = lines.index('Positional arguments:')
|
||||
cmds_end = lines.index('Optional arguments:')
|
||||
command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)')
|
||||
for line in lines[cmds_start:cmds_end]:
|
||||
match = command_pattern.match(line)
|
||||
if match:
|
||||
commands.append(match.group(1))
|
||||
commands = set(commands)
|
||||
wanted_commands = set(('absolute-limits', 'list', 'help',
|
||||
'quota-show', 'type-list', 'snapshot-list'))
|
||||
self.assertFalse(wanted_commands - commands)
|
||||
|
||||
# Optional arguments:
|
||||
|
||||
@test.idempotent_id('2fd6f530-183c-4bda-8918-1e59e36c26b9')
|
||||
def test_cinder_version(self):
|
||||
self.cinder('', flags='--version')
|
||||
|
||||
@test.idempotent_id('306bac51-c443-4426-a6cf-583a953fcd68')
|
||||
def test_cinder_debug_list(self):
|
||||
self.cinder('list', flags='--debug')
|
||||
|
||||
@test.idempotent_id('6d97fcd2-5dd1-429d-af70-030c949d86cd')
|
||||
def test_cinder_retries_list(self):
|
||||
self.cinder('list', flags='--retries 3')
|
||||
|
||||
@test.idempotent_id('95a2850c-35b4-4159-bb93-51647a5ad232')
|
||||
def test_cinder_region_list(self):
|
||||
region = CONF.volume.region
|
||||
if not region:
|
||||
region = CONF.identity.region
|
||||
self.cinder('list', flags='--os-region-name ' + region)
|
||||
@@ -1,42 +0,0 @@
|
||||
# Copyright 2014 Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tempest import clients
|
||||
from tempest.common import cred_provider
|
||||
from tempest import config
|
||||
from tempest.services.infra_optim.v1.json import infra_optim_client as ioc
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class Manager(clients.Manager):
|
||||
def __init__(self, credentials=None, service=None):
|
||||
super(Manager, self).__init__(credentials, service)
|
||||
self.io_client = ioc.InfraOptimClientJSON(self.auth_provider,
|
||||
'infra-optim',
|
||||
CONF.identity.region)
|
||||
|
||||
|
||||
class AltManager(Manager):
|
||||
def __init__(self, service=None):
|
||||
super(AltManager, self).__init__(
|
||||
cred_provider.get_configured_credentials('alt_user'), service)
|
||||
|
||||
|
||||
class AdminManager(Manager):
|
||||
def __init__(self, service=None):
|
||||
super(AdminManager, self).__init__(
|
||||
cred_provider.get_configured_credentials('identity_admin'),
|
||||
service)
|
||||
@@ -1,45 +0,0 @@
|
||||
# Copyright 2014 Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from tempest import config # noqa
|
||||
|
||||
service_available_group = cfg.OptGroup(name="service_available",
|
||||
title="Available OpenStack Services")
|
||||
|
||||
ServiceAvailableGroup = [
|
||||
cfg.BoolOpt("watcher",
|
||||
default=True,
|
||||
help="Whether or not watcher is expected to be available"),
|
||||
]
|
||||
|
||||
|
||||
class TempestConfigProxyWatcher(object):
|
||||
"""Wrapper over standard Tempest config that sets Watcher opts."""
|
||||
|
||||
def __init__(self):
|
||||
self._config = config.CONF
|
||||
config.register_opt_group(
|
||||
cfg.CONF, service_available_group, ServiceAvailableGroup)
|
||||
self._config.share = cfg.CONF.share
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self._config, attr)
|
||||
|
||||
|
||||
CONF = TempestConfigProxyWatcher()
|
||||
@@ -1,142 +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.services.infra_optim import base
|
||||
|
||||
|
||||
class InfraOptimClientJSON(base.InfraOptimClient):
|
||||
"""Base Tempest REST client for Watcher API v1."""
|
||||
version = '1'
|
||||
uri_prefix = 'v1'
|
||||
|
||||
# Audit Template
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_templates(self, **kwargs):
|
||||
"""List all existing audit templates."""
|
||||
return self._list_request('audit_templates', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_template_audits(self, audit_template_uuid):
|
||||
"""Lists all audits associated with a audit template."""
|
||||
return self._list_request(
|
||||
'/audit_templates/%s/audits' % audit_template_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_templates_detail(self, **kwargs):
|
||||
"""Lists details of all existing audit templates."""
|
||||
return self._list_request('/audit_templates/detail', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def show_audit_template(self, uuid):
|
||||
"""Gets a specific audit template.
|
||||
|
||||
:param uuid: Unique identifier of the audit template in UUID format.
|
||||
:return: Serialized audit template as a dictionary.
|
||||
"""
|
||||
|
||||
return self._show_request('audit_templates', uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def show_audit_template_by_host_agregate(self, host_agregate_id):
|
||||
"""Gets an audit template associated with given host agregate ID.
|
||||
|
||||
:param uuid: Unique identifier of the audit_template in UUID format.
|
||||
:return: Serialized audit_template as a dictionary.
|
||||
"""
|
||||
|
||||
uri = '/audit_templates/detail?host_agregate=%s' % host_agregate_id
|
||||
|
||||
return self._show_request('audit_templates', uuid=None, uri=uri)
|
||||
|
||||
@base.handle_errors
|
||||
def show_audit_template_by_goal(self, goal):
|
||||
"""Gets an audit template associated with given goal.
|
||||
|
||||
:param uuid: Unique identifier of the audit_template in UUID format.
|
||||
:return: Serialized audit_template as a dictionary.
|
||||
"""
|
||||
|
||||
uri = '/audit_templates/detail?goal=%s' % goal
|
||||
|
||||
return self._show_request('audit_templates', uuid=None, uri=uri)
|
||||
|
||||
@base.handle_errors
|
||||
def create_audit_template(self, **kwargs):
|
||||
"""Creates an audit template with the specified parameters.
|
||||
|
||||
:param name: The name of the audit template. Default: My Audit Template
|
||||
:param description: The description of the audit template.
|
||||
Default: AT Description
|
||||
:param goal: The goal associated within the audit template.
|
||||
Default: SERVERS_CONSOLIDATION
|
||||
:param host_aggregate: ID of the host aggregate targeted by
|
||||
this audit template. Default: 1
|
||||
:param extra: IMetadata associated to this audit template.
|
||||
Default: {}
|
||||
:return: A tuple with the server response and the created audit
|
||||
template.
|
||||
"""
|
||||
|
||||
audit_template = {
|
||||
'name': kwargs.get('name', 'My Audit Template'),
|
||||
'description': kwargs.get('description', 'AT Description'),
|
||||
'goal': kwargs.get('goal', 'SERVERS_CONSOLIDATION'),
|
||||
'host_aggregate': kwargs.get('host_aggregate', 1),
|
||||
'extra': kwargs.get('extra', {}),
|
||||
}
|
||||
|
||||
return self._create_request('audit_templates', audit_template)
|
||||
|
||||
# @base.handle_errors
|
||||
# def create_audit(self, audit_template_id=None, **kwargs):
|
||||
# """
|
||||
# Create a infra_optim audit with the specified parameters.
|
||||
|
||||
# :param cpu_arch: CPU architecture of the audit. Default: x86_64.
|
||||
# :param cpus: Number of CPUs. Default: 8.
|
||||
# :param local_gb: Disk size. Default: 1024.
|
||||
# :param memory_mb: Available RAM. Default: 4096.
|
||||
# :param driver: Driver name. Default: "fake"
|
||||
# :return: A tuple with the server response and the created audit.
|
||||
|
||||
# """
|
||||
# audit = {'audit_template_uuid': audit_template_id,
|
||||
# 'properties': {'cpu_arch': kwargs.get('cpu_arch', 'x86_64'),
|
||||
# 'cpus': kwargs.get('cpus', 8),
|
||||
# 'local_gb': kwargs.get('local_gb', 1024),
|
||||
# 'memory_mb': kwargs.get('memory_mb', 4096)},
|
||||
# 'driver': kwargs.get('driver', 'fake')}
|
||||
|
||||
# return self._create_request('audits', audit)
|
||||
|
||||
@base.handle_errors
|
||||
def delete_audit_template(self, uuid):
|
||||
"""Deletes an audit template having the specified UUID.
|
||||
|
||||
:param uuid: The unique identifier of the audit template.
|
||||
:return: A tuple with the server response and the response body.
|
||||
"""
|
||||
|
||||
return self._delete_request('audit_templates', uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def update_audit_template(self, uuid, patch):
|
||||
"""Update the specified audit template.
|
||||
|
||||
:param uuid: The unique identifier of the audit template.
|
||||
:param patch: List of dicts representing json patches.
|
||||
:return: A tuple with the server response and the updated audit
|
||||
template.
|
||||
"""
|
||||
|
||||
return self._patch_request('audit_templates', uuid, patch)
|
||||
133
watcher_tempest_plugin/README.rst
Normal file
133
watcher_tempest_plugin/README.rst
Normal file
@@ -0,0 +1,133 @@
|
||||
..
|
||||
Except where otherwise noted, this document is licensed under Creative
|
||||
Commons Attribution 3.0 License. You can view the license at:
|
||||
|
||||
https://creativecommons.org/licenses/by/3.0/
|
||||
|
||||
.. _tempest_integration:
|
||||
|
||||
=============
|
||||
Tempest tests
|
||||
=============
|
||||
|
||||
This directory contains Tempest tests to cover Watcher project.
|
||||
|
||||
The following procedure gets you started with Tempest testing but you can also
|
||||
refer to the `Tempest documentation`_ for more details.
|
||||
|
||||
.. _Tempest documentation: http://docs.openstack.org/developer/tempest/
|
||||
|
||||
|
||||
Tempest installation
|
||||
====================
|
||||
|
||||
To install Tempest you can issue the following commands::
|
||||
|
||||
$ git clone https://github.com/openstack/tempest/
|
||||
$ cd tempest/
|
||||
$ pip install .
|
||||
|
||||
The folder you are into now will be called ``<TEMPEST_DIR>`` from now onwards.
|
||||
|
||||
Please note that although it is fully working outside a virtual environment, it
|
||||
is recommended to install within a venv.
|
||||
|
||||
|
||||
Watcher Tempest testing setup
|
||||
=============================
|
||||
|
||||
You can now install Watcher alongside it in development mode by issuing the
|
||||
following command::
|
||||
|
||||
$ pip install -e <WATCHER_SRC_DIR>
|
||||
|
||||
Then setup a local working environment (here ``watcher-cloud``) for running
|
||||
Tempest for Watcher which shall contain the configuration for your OpenStack
|
||||
intergration platform.
|
||||
|
||||
In a virtual environment, you can do so by issuing the following command::
|
||||
|
||||
$ cd <TEMPEST_DIR>
|
||||
$ tempest init watcher-cloud
|
||||
|
||||
Otherwise, if you are not using a virtualenv::
|
||||
|
||||
$ cd <TEMPEST_DIR>
|
||||
$ tempest init --config-dir ./etc watcher-cloud
|
||||
|
||||
By default the configuration file is empty so before starting, you need to issue the following commands::
|
||||
|
||||
$ cd <TEMPEST_DIR>/watcher-cloud/etc
|
||||
$ cp tempest.conf.sample tempest.conf
|
||||
|
||||
At this point you need to edit the ``watcher-cloud/etc/tempest.conf``
|
||||
file as described in the `Tempest configuration guide`_.
|
||||
Shown below is a minimal configuration you need to set within your
|
||||
``tempest.conf`` configuration file which can get you started.
|
||||
|
||||
For Keystone V3::
|
||||
|
||||
uri_v3 = http://<KEYSTONE_PUBLIC_ENDPOINT_IP>:<KEYSTONE_PORT>/v3
|
||||
admin_tenant_name = <ADMIN_TENANT_NAME>
|
||||
admin_username = <ADMIN_USERNAME>
|
||||
admin_password = <ADMIN_PASSWORD>
|
||||
admin_domain_name = <ADMIN_DOMAIN_NAME>
|
||||
api_v2 = false
|
||||
api_v3 = true
|
||||
auth_version = v3
|
||||
|
||||
For Keystone V2::
|
||||
|
||||
uri = http://<KEYSTONE_PUBLIC_ENDPOINT_IP>:<KEYSTONE_PORT>/v2.0
|
||||
admin_tenant_name = <ADMIN_TENANT_NAME>
|
||||
admin_username = <ADMIN_USERNAME>
|
||||
admin_password = <ADMIN_PASSWORD>
|
||||
auth_version = v2
|
||||
|
||||
|
||||
For more information, please refer to:
|
||||
|
||||
- Keystone connection: http://docs.openstack.org/developer/tempest/configuration.html#keystone-connection-info
|
||||
- Dynamic Keystone Credentials: http://docs.openstack.org/developer/tempest/configuration.html#dynamic-credentials
|
||||
|
||||
.. _virtual environment: http://docs.python-guide.org/en/latest/dev/virtualenvs/
|
||||
.. _Tempest configuration guide: http://docs.openstack.org/developer/tempest/configuration.html
|
||||
|
||||
|
||||
Watcher Tempest tests execution
|
||||
===============================
|
||||
|
||||
To list all Watcher Tempest cases, you can issue the following commands::
|
||||
|
||||
$ cd <TEMPEST_DIR>
|
||||
$ testr list-tests watcher
|
||||
|
||||
To run only these tests in Tempest, you can then issue these commands::
|
||||
|
||||
$ ./run_tempest.sh --config watcher-cloud/etc/tempest.conf -N -- watcher
|
||||
|
||||
Or alternatively the following commands if you are::
|
||||
|
||||
$ cd <TEMPEST_DIR>/watcher-cloud
|
||||
$ ../run_tempest.sh -N -- watcher
|
||||
|
||||
To run a single test case, go to Tempest directory, then run with test case
|
||||
name, e.g.::
|
||||
|
||||
$ cd <TEMPEST_DIR>
|
||||
$ ./run_tempest.sh --config watcher-cloud/etc/tempest.conf -N \
|
||||
-- watcher_tempest_plugin.tests.api.admin.test_audit_template.TestCreateDeleteAuditTemplate.test_create_audit_template
|
||||
|
||||
Alternatively, you can also run the Watcher Tempest plugin tests using tox. But
|
||||
before you can do so, you need to follow the Tempest explanation on running
|
||||
`tox with plugins`_. Then, run::
|
||||
|
||||
$ export TEMPEST_CONFIG_DIR=<TEMPEST_DIR>/watcher-cloud/etc/
|
||||
$ tox -eall-plugin watcher
|
||||
|
||||
.. _tox with plugins: http://docs.openstack.org/developer/tempest/plugin.html#notes-for-using-plugins-with-virtualenvs
|
||||
|
||||
And, to run a specific test::
|
||||
|
||||
$ export TEMPEST_CONFIG_DIR=<TEMPEST_DIR>/watcher-cloud/etc/
|
||||
$ tox -eall-plugin watcher_tempest_plugin.tests.api.admin.test_audit_template.TestCreateDeleteAuditTemplate.test_create_audit_template
|
||||
27
watcher_tempest_plugin/config.py
Normal file
27
watcher_tempest_plugin/config.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
service_available_group = cfg.OptGroup(name="service_available",
|
||||
title="Available OpenStack Services")
|
||||
|
||||
ServiceAvailableGroup = [
|
||||
cfg.BoolOpt("watcher",
|
||||
default=True,
|
||||
help="Whether or not watcher is expected to be available"),
|
||||
]
|
||||
45
watcher_tempest_plugin/infra_optim_clients.py
Normal file
45
watcher_tempest_plugin/infra_optim_clients.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
from tempest import clients
|
||||
from tempest.common import credentials_factory as creds_factory
|
||||
from tempest import config
|
||||
|
||||
from watcher_tempest_plugin.services.infra_optim.v1.json import client as ioc
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseManager(clients.Manager):
|
||||
|
||||
def __init__(self, credentials, service=None, api_microversions=None):
|
||||
super(BaseManager, self).__init__(
|
||||
credentials, service, api_microversions)
|
||||
self.io_client = ioc.InfraOptimClientJSON(
|
||||
self.auth_provider, 'infra-optim', CONF.identity.region)
|
||||
|
||||
|
||||
class AdminManager(BaseManager):
|
||||
def __init__(self, service=None, api_microversions=None):
|
||||
super(AdminManager, self).__init__(
|
||||
creds_factory.get_configured_credentials('identity_admin'),
|
||||
service,
|
||||
api_microversions
|
||||
)
|
||||
36
watcher_tempest_plugin/plugin.py
Normal file
36
watcher_tempest_plugin/plugin.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# 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 import config
|
||||
from tempest.test_discover import plugins
|
||||
|
||||
from watcher_tempest_plugin import config as watcher_config
|
||||
|
||||
|
||||
class WatcherTempestPlugin(plugins.TempestPlugin):
|
||||
def load_tests(self):
|
||||
base_path = os.path.split(os.path.dirname(
|
||||
os.path.abspath(__file__)))[0]
|
||||
test_dir = "watcher_tempest_plugin/tests"
|
||||
full_test_dir = os.path.join(base_path, test_dir)
|
||||
return full_test_dir, base_path
|
||||
|
||||
def register_opts(self, conf):
|
||||
config.register_opt_group(
|
||||
conf, watcher_config.service_available_group,
|
||||
watcher_config.ServiceAvailableGroup)
|
||||
|
||||
def get_opt_lists(self):
|
||||
return []
|
||||
@@ -1,18 +1,23 @@
|
||||
# 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
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.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
|
||||
#
|
||||
# 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.
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import abc
|
||||
import functools
|
||||
import json
|
||||
|
||||
import six
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from tempest.common import service_client
|
||||
@@ -38,20 +43,21 @@ def handle_errors(f):
|
||||
return wrapper
|
||||
|
||||
|
||||
class InfraOptimClient(service_client.ServiceClient):
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseInfraOptimClient(service_client.ServiceClient):
|
||||
"""Base Tempest REST client for Watcher API."""
|
||||
|
||||
uri_prefix = ''
|
||||
URI_PREFIX = ''
|
||||
|
||||
@abc.abstractmethod
|
||||
def serialize(self, object_dict):
|
||||
"""Serialize an Watcher object."""
|
||||
raise NotImplementedError()
|
||||
|
||||
return json.dumps(object_dict)
|
||||
|
||||
@abc.abstractmethod
|
||||
def deserialize(self, object_str):
|
||||
"""Deserialize an Watcher object."""
|
||||
|
||||
return json.loads(object_str)
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_uri(self, resource_name, uuid=None, permanent=False):
|
||||
"""Get URI for a specific resource or object.
|
||||
@@ -61,7 +67,7 @@ class InfraOptimClient(service_client.ServiceClient):
|
||||
:return: Relative URI for the resource or object.
|
||||
"""
|
||||
|
||||
prefix = self.uri_prefix if not permanent else ''
|
||||
prefix = self.URI_PREFIX if not permanent else ''
|
||||
|
||||
return '{pref}/{res}{uuid}'.format(pref=prefix,
|
||||
res=resource_name,
|
||||
@@ -172,7 +178,7 @@ class InfraOptimClient(service_client.ServiceClient):
|
||||
"""
|
||||
|
||||
uri = self._get_uri(resource, uuid)
|
||||
patch_body = json.dumps(patch_object)
|
||||
patch_body = self.serialize(patch_object)
|
||||
|
||||
resp, body = self.patch(uri, body=patch_body)
|
||||
self.expected_success(200, resp['status'])
|
||||
@@ -198,7 +204,7 @@ class InfraOptimClient(service_client.ServiceClient):
|
||||
"""Update specified object with JSON-patch."""
|
||||
|
||||
uri = self._get_uri(resource)
|
||||
put_body = json.dumps(put_object)
|
||||
put_body = self.serialize(put_object)
|
||||
|
||||
resp, body = self.put(uri, body=put_body)
|
||||
self.expected_success(202, resp['status'])
|
||||
146
watcher_tempest_plugin/services/infra_optim/v1/json/client.py
Normal file
146
watcher_tempest_plugin/services/infra_optim/v1/json/client.py
Normal file
@@ -0,0 +1,146 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from watcher_tempest_plugin.services.infra_optim import base
|
||||
|
||||
|
||||
class InfraOptimClientJSON(base.BaseInfraOptimClient):
|
||||
"""Base Tempest REST client for Watcher API v1."""
|
||||
|
||||
URI_PREFIX = 'v1'
|
||||
|
||||
def serialize(self, object_dict):
|
||||
"""Serialize an Watcher object."""
|
||||
return json.dumps(object_dict)
|
||||
|
||||
def deserialize(self, object_str):
|
||||
"""Deserialize an Watcher object."""
|
||||
return json.loads(object_str.decode('utf-8'))
|
||||
|
||||
# ### AUDIT TEMPLATES ### #
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_templates(self, **kwargs):
|
||||
"""List all existing audit templates."""
|
||||
return self._list_request('audit_templates', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_template_audits(self, audit_template_uuid):
|
||||
"""Lists all audits associated with a audit template."""
|
||||
return self._list_request(
|
||||
'/audit_templates/%s/audits' % audit_template_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def list_audit_templates_detail(self, **kwargs):
|
||||
"""Lists details of all existing audit templates."""
|
||||
return self._list_request('/audit_templates/detail', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def show_audit_template(self, audit_template_uuid):
|
||||
"""Gets a specific audit template.
|
||||
|
||||
:param audit_template_uuid: Unique identifier of the audit template
|
||||
:return: Serialized audit template as a dictionary.
|
||||
"""
|
||||
return self._show_request('audit_templates', audit_template_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def filter_audit_template_by_host_aggregate(self, host_aggregate):
|
||||
"""Gets an audit template associated with given host agregate ID.
|
||||
|
||||
:param host_aggregate: Unique identifier of the host aggregate
|
||||
:return: Serialized audit template as a dictionary.
|
||||
"""
|
||||
return self._list_request('/audit_templates',
|
||||
host_aggregate=host_aggregate)
|
||||
|
||||
@base.handle_errors
|
||||
def filter_audit_template_by_goal(self, goal):
|
||||
"""Gets an audit template associated with given goal.
|
||||
|
||||
:param goal: goal identifier
|
||||
:return: Serialized audit template as a dictionary.
|
||||
"""
|
||||
return self._list_request('/audit_templates', goal=goal)
|
||||
|
||||
@base.handle_errors
|
||||
def create_audit_template(self, **kwargs):
|
||||
"""Creates an audit template with the specified parameters.
|
||||
|
||||
:param name: The name of the audit template. Default: My Audit Template
|
||||
:param description: The description of the audit template.
|
||||
Default: AT Description
|
||||
:param goal: The goal associated within the audit template.
|
||||
Default: DUMMY
|
||||
:param host_aggregate: ID of the host aggregate targeted by
|
||||
this audit template. Default: 1
|
||||
:param extra: IMetadata associated to this audit template.
|
||||
Default: {}
|
||||
:return: A tuple with the server response and the created audit
|
||||
template.
|
||||
"""
|
||||
|
||||
parameters = {k: v for k, v in kwargs.items() if v is not None}
|
||||
# This name is unique to avoid the DB unique constraint on names
|
||||
unique_name = 'Tempest Audit Template %s' % uuid.uuid4()
|
||||
|
||||
audit_template = {
|
||||
'name': parameters.get('name', unique_name),
|
||||
'description': parameters.get('description', ''),
|
||||
'goal': parameters.get('goal', 'DUMMY'),
|
||||
'host_aggregate': parameters.get('host_aggregate', 1),
|
||||
'extra': parameters.get('extra', {}),
|
||||
}
|
||||
|
||||
return self._create_request('audit_templates', audit_template)
|
||||
|
||||
@base.handle_errors
|
||||
def create_audit(self, audit_template_uuid, **kwargs):
|
||||
"""Create an audit with the specified parameters.
|
||||
|
||||
:param audit_template_uuid: Audit template ID used by the audit
|
||||
:return: A tuple with the server response and the created audit.
|
||||
"""
|
||||
audit = {'audit_template_uuid': audit_template_uuid}
|
||||
audit.update(kwargs)
|
||||
|
||||
return self._create_request('audits', audit)
|
||||
|
||||
@base.handle_errors
|
||||
def delete_audit_template(self, audit_template_uuid):
|
||||
"""Deletes an audit template having the specified UUID.
|
||||
|
||||
:param audit_template_uuid: The unique identifier of the audit template
|
||||
:return: A tuple with the server response and the response body.
|
||||
"""
|
||||
|
||||
return self._delete_request('audit_templates', audit_template_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def update_audit_template(self, audit_template_uuid, patch):
|
||||
"""Update the specified audit template.
|
||||
|
||||
:param audit_template_uuid: The unique identifier of the audit template
|
||||
:param patch: List of dicts representing json patches.
|
||||
:return: A tuple with the server response and the updated audit
|
||||
template.
|
||||
"""
|
||||
|
||||
return self._patch_request('audit_templates',
|
||||
audit_template_uuid, patch)
|
||||
139
watcher_tempest_plugin/tests/api/admin/base.py
Normal file
139
watcher_tempest_plugin/tests/api/admin/base.py
Normal file
@@ -0,0 +1,139 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import functools
|
||||
|
||||
from tempest import test
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import exceptions as lib_exc
|
||||
|
||||
from watcher_tempest_plugin import infra_optim_clients as clients
|
||||
|
||||
# Resources must be deleted in a specific order, this list
|
||||
# defines the resource types to clean up, and the correct order.
|
||||
RESOURCE_TYPES = ['audit_template']
|
||||
# RESOURCE_TYPES = ['action', 'action_plan', 'audit', 'audit_template']
|
||||
|
||||
|
||||
def creates(resource):
|
||||
"""Decorator that adds resources to the appropriate cleanup list."""
|
||||
|
||||
def decorator(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper(cls, *args, **kwargs):
|
||||
resp, body = f(cls, *args, **kwargs)
|
||||
|
||||
if 'uuid' in body:
|
||||
cls.created_objects[resource].add(body['uuid'])
|
||||
|
||||
return resp, body
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
class BaseInfraOptimTest(test.BaseTestCase):
|
||||
"""Base class for Infrastructure Optimization API tests."""
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
super(BaseInfraOptimTest, cls).setup_credentials()
|
||||
cls.mgr = clients.AdminManager()
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseInfraOptimTest, cls).setup_clients()
|
||||
cls.client = cls.mgr.io_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseInfraOptimTest, cls).resource_setup()
|
||||
|
||||
cls.created_objects = {}
|
||||
for resource in RESOURCE_TYPES:
|
||||
cls.created_objects[resource] = set()
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
"""Ensure that all created objects get destroyed."""
|
||||
|
||||
try:
|
||||
for resource in RESOURCE_TYPES:
|
||||
uuids = cls.created_objects[resource]
|
||||
delete_method = getattr(cls.client, 'delete_%s' % resource)
|
||||
for u in uuids:
|
||||
delete_method(u, ignore_errors=lib_exc.NotFound)
|
||||
finally:
|
||||
super(BaseInfraOptimTest, cls).resource_cleanup()
|
||||
|
||||
def validate_self_link(self, resource, uuid, link):
|
||||
"""Check whether the given self link formatted correctly."""
|
||||
expected_link = "{base}/{pref}/{res}/{uuid}".format(
|
||||
base=self.client.base_url,
|
||||
pref=self.client.URI_PREFIX,
|
||||
res=resource,
|
||||
uuid=uuid
|
||||
)
|
||||
self.assertEqual(expected_link, link)
|
||||
|
||||
def assert_expected(self, expected, actual,
|
||||
keys=('created_at', 'updated_at', 'deleted_at')):
|
||||
# Check if not expected keys/values exists in actual response body
|
||||
for key, value in expected.items():
|
||||
if key not in keys:
|
||||
self.assertIn(key, actual)
|
||||
self.assertEqual(value, actual[key])
|
||||
|
||||
# ### AUDIT TEMPLATES ### #
|
||||
|
||||
@classmethod
|
||||
@creates('audit_template')
|
||||
def create_audit_template(cls, name=None, description=None, goal=None,
|
||||
host_aggregate=None, extra=None):
|
||||
"""Wrapper utility for creating a test audit template
|
||||
|
||||
:param name: The name of the audit template. Default: My Audit Template
|
||||
:param description: The description of the audit template.
|
||||
Default: AT Description
|
||||
:param goal: The goal associated within the audit template.
|
||||
Default: DUMMY
|
||||
:param host_aggregate: ID of the host aggregate targeted by
|
||||
this audit template. Default: 1
|
||||
:param extra: IMetadata associated to this audit template.
|
||||
Default: {}
|
||||
:return: A tuple with The HTTP response and its body
|
||||
"""
|
||||
|
||||
description = description or data_utils.rand_name(
|
||||
'test-audit_template')
|
||||
resp, body = cls.client.create_audit_template(
|
||||
name=name, description=description, goal=goal,
|
||||
host_aggregate=host_aggregate, extra=extra)
|
||||
return resp, body
|
||||
|
||||
@classmethod
|
||||
def delete_audit_template(cls, uuid):
|
||||
"""Deletes a audit_template having the specified UUID
|
||||
|
||||
:param uuid: The unique identifier of the audit template
|
||||
:return: Server response
|
||||
"""
|
||||
|
||||
resp, body = cls.client.delete_audit_template(uuid)
|
||||
|
||||
if uuid in cls.created_objects['audit_template']:
|
||||
cls.created_objects['audit_template'].remove(uuid)
|
||||
|
||||
return resp
|
||||
@@ -1,18 +1,23 @@
|
||||
# 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
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.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
|
||||
#
|
||||
# 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.
|
||||
# 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.api.infra_optim.admin import base
|
||||
from tempest import test
|
||||
|
||||
from watcher_tempest_plugin.tests.api.admin import base
|
||||
|
||||
|
||||
class TestApiDiscovery(base.BaseInfraOptimTest):
|
||||
"""Tests for API discovery features."""
|
||||
@@ -1,21 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.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
|
||||
#
|
||||
# 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.
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import uuid
|
||||
|
||||
from tempest import test
|
||||
from tempest_lib import decorators
|
||||
from tempest_lib import exceptions as lib_exc
|
||||
|
||||
from tempest.api.infra_optim.admin import base
|
||||
from tempest import test
|
||||
from watcher_tempest_plugin.tests.api.admin import base
|
||||
|
||||
|
||||
class TestCreateDeleteAuditTemplate(base.BaseInfraOptimTest):
|
||||
"""Tests on audit templates"""
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_create_audit_template(self):
|
||||
params = {'name': 'my at name %s' % uuid.uuid4(),
|
||||
'description': 'my at description',
|
||||
'host_aggregate': 12,
|
||||
'goal': 'DUMMY',
|
||||
'extra': {'str': 'value', 'int': 123, 'float': 0.123,
|
||||
'bool': True, 'list': [1, 2, 3],
|
||||
'dict': {'foo': 'bar'}}}
|
||||
|
||||
_, body = self.create_audit_template(**params)
|
||||
self.assert_expected(params, body)
|
||||
|
||||
_, audit_template = self.client.show_audit_template(body['uuid'])
|
||||
self.assert_expected(audit_template, body)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_create_audit_template_unicode_description(self):
|
||||
# Use a unicode string for testing:
|
||||
params = {'name': 'my at name %s' % uuid.uuid4(),
|
||||
'description': 'my àt déscrïptïôn',
|
||||
'host_aggregate': 12,
|
||||
'goal': 'DUMMY',
|
||||
'extra': {'foo': 'bar'}}
|
||||
|
||||
_, body = self.create_audit_template(**params)
|
||||
self.assert_expected(params, body)
|
||||
|
||||
_, audit_template = self.client.show_audit_template(body['uuid'])
|
||||
self.assert_expected(audit_template, body)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_delete_audit_template(self):
|
||||
_, body = self.create_audit_template()
|
||||
audit_uuid = body['uuid']
|
||||
|
||||
self.delete_audit_template(audit_uuid)
|
||||
|
||||
self.assertRaises(lib_exc.NotFound, self.client.show_audit_template,
|
||||
audit_uuid)
|
||||
|
||||
|
||||
class TestAuditTemplate(base.BaseInfraOptimTest):
|
||||
@@ -26,63 +78,30 @@ class TestAuditTemplate(base.BaseInfraOptimTest):
|
||||
super(TestAuditTemplate, cls).resource_setup()
|
||||
_, cls.audit_template = cls.create_audit_template()
|
||||
|
||||
def _assertExpected(self, expected, actual):
|
||||
# Check if not expected keys/values exists in actual response body
|
||||
for key, value in expected.items():
|
||||
if key not in ('created_at', 'updated_at', 'deleted_at'):
|
||||
self.assertIn(key, actual)
|
||||
self.assertEqual(value, actual[key])
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_create_audit_template(self):
|
||||
params = {'name': 'my at name',
|
||||
'description': 'my at description',
|
||||
'host_aggregate': 12,
|
||||
'goal': 'A GOAL',
|
||||
'extra': {'str': 'value', 'int': 123, 'float': 0.123,
|
||||
'bool': True, 'list': [1, 2, 3],
|
||||
'dict': {'foo': 'bar'}}}
|
||||
|
||||
_, body = self.create_audit_template(**params)
|
||||
self._assertExpected(params, body['properties'])
|
||||
|
||||
_, audit_template = self.client.show_audit_template(body['uuid'])
|
||||
self._assertExpected(audit_template, body)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_create_audit_template_unicode_description(self):
|
||||
# Use a unicode string for testing:
|
||||
params = {'name': 'my at name',
|
||||
'description': 'my àt déscrïptïôn',
|
||||
'host_aggregate': 12,
|
||||
'goal': 'A GOAL',
|
||||
'extra': {'foo': 'bar'}}
|
||||
|
||||
_, body = self.create_audit_template(**params)
|
||||
self._assertExpected(params, body['properties'])
|
||||
|
||||
_, audit_template = self.client.show_audit_template(body['uuid'])
|
||||
self._assertExpected(audit_template, body)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_show_audit_template(self):
|
||||
_, audit_template = self.client.show_audit_template(
|
||||
self.audit_template['uuid'])
|
||||
self._assertExpected(self.audit_template, audit_template)
|
||||
|
||||
self.assert_expected(self.audit_template, audit_template)
|
||||
|
||||
@decorators.skip_because(bug="1510189")
|
||||
@test.attr(type='smoke')
|
||||
def test_show_audit_template_by_goal(self):
|
||||
def test_filter_audit_template_by_goal(self):
|
||||
_, audit_template = self.client.\
|
||||
show_audit_template_by_goal(self.audit_template['goal'])
|
||||
self._assertExpected(self.audit_template,
|
||||
filter_audit_template_by_goal(self.audit_template['goal'])
|
||||
|
||||
self.assert_expected(self.audit_template,
|
||||
audit_template['audit_templates'][0])
|
||||
|
||||
@decorators.skip_because(bug="1510189")
|
||||
@test.attr(type='smoke')
|
||||
def test_show_audit_template_by_host_aggregate(self):
|
||||
def test_filter_audit_template_by_host_aggregate(self):
|
||||
_, audit_template = self.client.\
|
||||
show_audit_template_by_host_aggregate(
|
||||
filter_audit_template_by_host_aggregate(
|
||||
self.audit_template['host_aggregate'])
|
||||
self._assertExpected(self.audit_template,
|
||||
|
||||
self.assert_expected(self.audit_template,
|
||||
audit_template['audit_templates'][0])
|
||||
|
||||
@test.attr(type='smoke')
|
||||
@@ -106,31 +125,27 @@ class TestAuditTemplate(base.BaseInfraOptimTest):
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_list_with_limit(self):
|
||||
# We create 3 extra audit templates to exceed the limit we fix
|
||||
for _ in range(3):
|
||||
self.create_audit_template()
|
||||
|
||||
_, body = self.client.list_audit_templates(limit=3)
|
||||
|
||||
next_marker = body['audit_templates'][-1]['uuid']
|
||||
self.assertEqual(len(body['audit_templates']), 3)
|
||||
self.assertIn(next_marker, body['next'])
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_delete_audit_template(self):
|
||||
_, body = self.create_audit_template()
|
||||
uuid = body['uuid']
|
||||
|
||||
self.delete_audit_template(uuid)
|
||||
self.assertRaises(lib_exc.NotFound, self.client.show_audit_template,
|
||||
uuid)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_update_audit_template_replace(self):
|
||||
params = {'name': 'my at name',
|
||||
params = {'name': 'my at name %s' % uuid.uuid4(),
|
||||
'description': 'my at description',
|
||||
'host_aggregate': 12,
|
||||
'goal': 'A GOAL',
|
||||
'goal': 'DUMMY',
|
||||
'extra': {'key1': 'value1', 'key2': 'value2'}}
|
||||
|
||||
_, body = self.create_audit_template(**params)
|
||||
|
||||
new_name = 'my at new name'
|
||||
new_name = 'my at new name %s' % uuid.uuid4()
|
||||
new_description = 'my new at description'
|
||||
new_host_aggregate = 10
|
||||
new_goal = 'A NEW GOAL'
|
||||
@@ -168,8 +183,8 @@ class TestAuditTemplate(base.BaseInfraOptimTest):
|
||||
def test_update_audit_template_remove(self):
|
||||
extra = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
|
||||
description = 'my at description'
|
||||
goal = 'A GOAL'
|
||||
name = 'my at name'
|
||||
goal = 'DUMMY'
|
||||
name = 'my at name %s' % uuid.uuid4()
|
||||
params = {'name': name,
|
||||
'description': description,
|
||||
'host_aggregate': 12,
|
||||
@@ -199,7 +214,7 @@ class TestAuditTemplate(base.BaseInfraOptimTest):
|
||||
audit_template['uuid'],
|
||||
[{'path': '/host_aggregate', 'op': 'remove'}])
|
||||
_, body = self.client.show_audit_template(audit_template['uuid'])
|
||||
self.assertEqual('', body['extra'])
|
||||
self.assertEqual({}, body['extra'])
|
||||
|
||||
# Assert nothing else was changed
|
||||
self.assertEqual(name, body['name'])
|
||||
@@ -208,10 +223,10 @@ class TestAuditTemplate(base.BaseInfraOptimTest):
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_update_audit_template_add(self):
|
||||
params = {'name': 'my at name',
|
||||
params = {'name': 'my at name %s' % uuid.uuid4(),
|
||||
'description': 'my at description',
|
||||
'host_aggregate': 12,
|
||||
'goal': 'A GOAL'}
|
||||
'goal': 'DUMMY'}
|
||||
|
||||
_, body = self.create_audit_template(**params)
|
||||
|
||||
@@ -228,10 +243,3 @@ class TestAuditTemplate(base.BaseInfraOptimTest):
|
||||
|
||||
_, body = self.client.show_audit_template(body['uuid'])
|
||||
self.assertEqual(extra, body['extra'])
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_audit_template_audit_list(self):
|
||||
_, audit = self.create_audit(self.audit_template['uuid'])
|
||||
_, body = self.client.list_audit_template_audits(
|
||||
self.audit_template['uuid'])
|
||||
self.assertIn(audit['uuid'], [n['uuid'] for n in body['audits']])
|
||||
Reference in New Issue
Block a user