Committing initial work
This is the initial work done on CloudKitty's tempest plugin. Change-Id: I3251a51271c2ce3ff4bb667d1f6e09e77896b8d7
This commit is contained in:
parent
568276b927
commit
b58453fa85
6
.coveragerc
Normal file
6
.coveragerc
Normal file
@ -0,0 +1,6 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = cloudkitty_tempest_plugin
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
59
.gitignore
vendored
Normal file
59
.gitignore
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg*
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
cover/
|
||||
.coverage*
|
||||
!.coveragerc
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
.stestr
|
||||
.venv
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Complexity
|
||||
output/*.html
|
||||
output/*/index.html
|
||||
|
||||
# Sphinx
|
||||
doc/build
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Editors
|
||||
*~
|
||||
.*.swp
|
||||
.*sw?
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
3
.mailmap
Normal file
3
.mailmap
Normal file
@ -0,0 +1,3 @@
|
||||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
3
.stestr.conf
Normal file
3
.stestr.conf
Normal file
@ -0,0 +1,3 @@
|
||||
[DEFAULT]
|
||||
test_path=./cloudkitty_tempest_plugin/tests
|
||||
top_dir=./
|
5
.testr.conf
Normal file
5
.testr.conf
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./cloudkitty_tempest_plugin/tests $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
||||
group_regex=gabbi\.(suitemaker|driver)\.(test_[^_]+_[^_]+)
|
17
CONTRIBUTING.rst
Normal file
17
CONTRIBUTING.rst
Normal file
@ -0,0 +1,17 @@
|
||||
If you would like to contribute to the development of OpenStack, you must
|
||||
follow the steps in this page:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
If you already have a good understanding of how the system works and your
|
||||
OpenStack accounts are set up, you can skip to the development workflow
|
||||
section of this documentation to learn how changes to OpenStack should be
|
||||
submitted for review via the Gerrit tool:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Storyboard, not GitHub:
|
||||
|
||||
https://storyboard.openstack.org/#!/project/890
|
4
HACKING.rst
Normal file
4
HACKING.rst
Normal file
@ -0,0 +1,4 @@
|
||||
cloudkitty-tempest-plugin Style Commandments
|
||||
===============================================
|
||||
|
||||
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
176
LICENSE
Normal file
176
LICENSE
Normal file
@ -0,0 +1,176 @@
|
||||
|
||||
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.
|
||||
|
25
README.rst
Normal file
25
README.rst
Normal file
@ -0,0 +1,25 @@
|
||||
=================================
|
||||
Tempest integration of CloudKitty
|
||||
=================================
|
||||
|
||||
This project defines a tempest plugin containing tests used to verify the
|
||||
functionality of a cloudkitty installation. The plugin will automatically load
|
||||
these tests into tempest.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
This plugin tests the CloudKitty API. This supposes that the 'rating' role
|
||||
exists in your OpenStack installation.
|
||||
|
||||
Developers
|
||||
----------
|
||||
For more information on cloudkitty, refer to:
|
||||
http://docs.openstack.org/developer/cloudkitty/
|
||||
|
||||
For more information on tempest plugins, refer to:
|
||||
http://docs.openstack.org/developer/tempest/plugin.html#using-plugins
|
||||
|
||||
Bugs
|
||||
----
|
||||
Please report bugs to: https://storyboard.openstack.org/#!/project/890
|
2
babel.cfg
Normal file
2
babel.cfg
Normal file
@ -0,0 +1,2 @@
|
||||
[python: **.py]
|
||||
|
6
cloudkitty_tempest_plugin/README.rst
Normal file
6
cloudkitty_tempest_plugin/README.rst
Normal file
@ -0,0 +1,6 @@
|
||||
===============================================
|
||||
Tempest Integration of CloudKitty
|
||||
===============================================
|
||||
|
||||
This directory contains Tempest tests to cover the CloudKitty project.
|
||||
|
0
cloudkitty_tempest_plugin/__init__.py
Normal file
0
cloudkitty_tempest_plugin/__init__.py
Normal file
33
cloudkitty_tempest_plugin/config.py
Normal file
33
cloudkitty_tempest_plugin/config.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright 2017 Objectif Libre
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
rating_group = cfg.OptGroup(name='rating_plugin',
|
||||
title='Rating Service Options')
|
||||
|
||||
RatingGroup = [
|
||||
cfg.StrOpt('service_name',
|
||||
default='rating',
|
||||
help="Service name of the Rating service."),
|
||||
cfg.StrOpt('user_name',
|
||||
default='cloudkitty',
|
||||
help="User name for the Rating service."),
|
||||
cfg.StrOpt('endpoint_type',
|
||||
default='publicURL',
|
||||
choices=['public', 'admin', 'internal',
|
||||
'publicURL', 'adminURL', 'internalURL'],
|
||||
help="The endpoint type to use for the rating service."),
|
||||
]
|
42
cloudkitty_tempest_plugin/plugin.py
Normal file
42
cloudkitty_tempest_plugin/plugin.py
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright 2017 Objectif Libre
|
||||
# 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 os
|
||||
|
||||
from tempest import config
|
||||
from tempest.test_discover import plugins
|
||||
|
||||
from cloudkitty_tempest_plugin import config as project_config
|
||||
|
||||
|
||||
class CloudkittyTempestPlugin(plugins.TempestPlugin):
|
||||
def load_tests(self):
|
||||
base_path = os.path.split(os.path.dirname(
|
||||
os.path.abspath(__file__)))[0]
|
||||
test_dir = "cloudkitty_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,
|
||||
project_config.rating_group,
|
||||
project_config.RatingGroup)
|
||||
|
||||
def get_opt_lists(self):
|
||||
return [
|
||||
(project_config.rating_group.name,
|
||||
project_config.RatingGroup),
|
||||
]
|
0
cloudkitty_tempest_plugin/services/__init__.py
Normal file
0
cloudkitty_tempest_plugin/services/__init__.py
Normal file
455
cloudkitty_tempest_plugin/services/client.py
Normal file
455
cloudkitty_tempest_plugin/services/client.py
Normal file
@ -0,0 +1,455 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Objectif Libre
|
||||
#
|
||||
# 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 six
|
||||
|
||||
from keystoneauth1.identity import v3
|
||||
from keystoneauth1 import session
|
||||
from keystoneclient import client
|
||||
from oslo_serialization import jsonutils as json
|
||||
from tempest import config
|
||||
from tempest.lib.common import rest_client
|
||||
from tempest import manager
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
CLOUDKITTY_API_VERSION = 'v1'
|
||||
|
||||
|
||||
class RatingClient(rest_client.RestClient):
|
||||
"""Implementation of cloudkittyclient for testing purposes"""
|
||||
api_version = 'v1'
|
||||
|
||||
@staticmethod
|
||||
def deserialize(body):
|
||||
return json.loads(body.replace("\n", ""))
|
||||
|
||||
@staticmethod
|
||||
def serialize(body):
|
||||
return json.dumps(body)
|
||||
|
||||
def _do_request(self, method, uri, body=None, expected_code=200):
|
||||
resp, body = self.request(method, uri, body=body)
|
||||
self.expected_success(expected_code, resp.status)
|
||||
body = self.deserialize(body) if body else dict()
|
||||
if not isinstance(body, dict) and not isinstance(body, list):
|
||||
body = dict()
|
||||
# ResponseBody inherits from dict, so lists must be converted
|
||||
body = dict(body=body) if isinstance(body, list) else body
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def get_collector_mappings(self, service=None):
|
||||
uri = '/collector/mappings/'
|
||||
if service:
|
||||
uri += service + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def create_collector_mapping(self, collector='gnocchi', service='compute'):
|
||||
uri = '/collector/mappings/'
|
||||
request_body = {
|
||||
'collector': collector,
|
||||
'service': service,
|
||||
}
|
||||
return self._do_request('POST', uri, body=self.serialize(request_body))
|
||||
|
||||
def delete_collector_mapping(self, service='compute'):
|
||||
uri = '/collector/mappings'
|
||||
request_body = {
|
||||
'service': service,
|
||||
}
|
||||
return self._do_request('DELETE', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=204)
|
||||
|
||||
def get_collector_state(self, collector="gnocchi"):
|
||||
uri = '/collector/states/'
|
||||
request_body = {
|
||||
'name': collector,
|
||||
}
|
||||
return self._do_request('GET', uri, body=self.serialize(request_body))
|
||||
|
||||
def set_collector_state(self, collector="gnocchi", enabled=True):
|
||||
uri = '/collector/states/'
|
||||
request_body = {
|
||||
'name': collector,
|
||||
'enabled': enabled,
|
||||
}
|
||||
return self._do_request('PUT', uri, body=self.serialize(request_body))
|
||||
|
||||
def get_config(self):
|
||||
uri = '/info/config/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def get_service(self, service_name=None):
|
||||
uri = '/info/services/'
|
||||
if service_name:
|
||||
uri += service_name + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def get_rating_module(self, module_name=None):
|
||||
uri = '/rating/modules/'
|
||||
if module_name:
|
||||
uri += module_name + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def update_rating_module(self, module_name, description='',
|
||||
enabled=False, hot_config=True, priority=1):
|
||||
uri = '/rating/modules/'
|
||||
request_body = {
|
||||
'module_id': module_name,
|
||||
'description': description,
|
||||
'enabled': enabled,
|
||||
'hot-config': hot_config,
|
||||
'priority': priority,
|
||||
}
|
||||
return self._do_request('PUT', uri, body=self.serialize(request_body),
|
||||
expected_code=302)
|
||||
|
||||
def reload_rating_modules(self):
|
||||
uri = '/rating/reload_modules'
|
||||
return self._do_request('GET', uri, expected_code=204)
|
||||
|
||||
def get_report_summary(self):
|
||||
uri = '/report/summary/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def get_rated_tenants(self):
|
||||
uri = '/report/tenants/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def get_report_total(self):
|
||||
uri = '/report/total/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def get_storage_dataframes(self):
|
||||
uri = '/storage/dataframes/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def get_hashmap_mapping_types(self):
|
||||
uri = '/rating/module_config/hashmap/types/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def get_hashmap_service(self, service_id=None):
|
||||
uri = '/rating/module_config/hashmap/services/'
|
||||
if service_id:
|
||||
uri += service_id + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def create_hashmap_service(self, name, service_id=None):
|
||||
uri = '/rating/module_config/hashmap/services/'
|
||||
request_body = {
|
||||
'name': name,
|
||||
}
|
||||
if service_id:
|
||||
request_body['service_id'] = service_id
|
||||
return self._do_request('POST', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=201)
|
||||
|
||||
def delete_hashmap_service(self, service_id):
|
||||
uri = '/rating/module_config/hashmap/services/'
|
||||
request_body = {
|
||||
'service_id': service_id,
|
||||
}
|
||||
return self._do_request('DELETE', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=204)
|
||||
|
||||
def get_hashmap_fields(self, service_id):
|
||||
uri = '/rating/module_config/hashmap/fields/'
|
||||
request_body = {
|
||||
'service_id': service_id,
|
||||
}
|
||||
return self._do_request('GET', uri,
|
||||
body=self.serialize(request_body))
|
||||
|
||||
def get_hashmap_field(self, field_id):
|
||||
uri = '/rating/module_config/hashmap/fields/' + field_id + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def create_hashmap_field(self, field_name, service_id, field_id=None):
|
||||
uri = '/rating/module_config/hashmap/fields/'
|
||||
request_body = {
|
||||
'name': field_name,
|
||||
'service_id': service_id,
|
||||
}
|
||||
if field_id:
|
||||
request_body['field_id'] = field_id
|
||||
return self._do_request('POST', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=201)
|
||||
|
||||
def delete_hashmap_field(self, field_id):
|
||||
uri = '/rating/module_config/hashmap/fields/'
|
||||
request_body = {
|
||||
'field_id': field_id,
|
||||
}
|
||||
return self._do_request('DELETE', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=204)
|
||||
|
||||
def get_hashmap_mappings(self, service_id=None, field_id=None,
|
||||
group_id=None, no_group=False, tenant_id=None,
|
||||
filter_tenant=False):
|
||||
args = locals()
|
||||
args.pop('self')
|
||||
uri = '/rating/module_config/hashmap/mappings/'
|
||||
request_body = dict((k, v)
|
||||
for k, v in six.iteritems(args) if v is not None)
|
||||
return self._do_request('GET', uri,
|
||||
body=self.serialize(request_body))
|
||||
|
||||
def get_hashmap_mapping(self, mapping_id):
|
||||
uri = '/rating/module_config/hashmap/mappings/' + mapping_id + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def create_hashmap_mapping(self, cost=0, field_id=None, group_id=None,
|
||||
map_type=None, mapping_id=None, service_id=None,
|
||||
tenant_id=None, value=None):
|
||||
args = locals()
|
||||
args.pop('self')
|
||||
uri = '/rating/module_config/hashmap/mappings/'
|
||||
request_body = dict((k, v)
|
||||
for k, v in six.iteritems(args) if v is not None)
|
||||
return self._do_request('POST', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=201)
|
||||
|
||||
def delete_hashmap_mapping(self, mapping_id):
|
||||
uri = '/rating/module_config/hashmap/mappings/'
|
||||
request_body = {
|
||||
'mapping_id': mapping_id,
|
||||
}
|
||||
return self._do_request('DELETE', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=204)
|
||||
|
||||
def update_hashmap_mapping(self, mapping_id, cost=0,
|
||||
field_id=None, group_id=None, map_type=None,
|
||||
service_id=None, tenant_id=None, value=None):
|
||||
args = locals()
|
||||
args.pop('self')
|
||||
uri = '/rating/module_config/hashmap/mappings/'
|
||||
request_body = dict((k, v)
|
||||
for k, v in six.iteritems(args) if v is not None)
|
||||
return self._do_request('PUT', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=302)
|
||||
|
||||
def get_hashmap_mapping_group(self, mapping_id):
|
||||
uri = '/rating/module_config/hashmap/mappings/group/'
|
||||
request_body = {
|
||||
'mapping_id': mapping_id,
|
||||
}
|
||||
return self._do_request('GET', uri,
|
||||
body=self.serialize(request_body))
|
||||
|
||||
def get_hashmap_group(self, group_id=None):
|
||||
uri = '/rating/module_config/hashmap/groups/'
|
||||
if group_id:
|
||||
uri += group_id + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def create_hashmap_group(self, group_name, group_id=None):
|
||||
uri = '/rating/module_config/hashmap/groups'
|
||||
request_body = {
|
||||
'name': group_name,
|
||||
}
|
||||
if group_id:
|
||||
request_body['group_id'] = group_id
|
||||
return self._do_request('POST', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=201)
|
||||
|
||||
def delete_hashmap_group(self, group_id):
|
||||
uri = '/rating/module_config/hashmap/groups'
|
||||
request_body = {
|
||||
'group_id': group_id,
|
||||
}
|
||||
return self._do_request('DELETE', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=204)
|
||||
|
||||
def get_hashmap_group_mappings(self, group_id):
|
||||
uri = '/rating/module_config/hashmap/groups/mappings/'
|
||||
request_body = {
|
||||
'group_id': group_id,
|
||||
}
|
||||
return self._do_request('GET', uri,
|
||||
body=self.serialize(request_body))
|
||||
|
||||
def get_hashmap_group_threshold(self, group_id):
|
||||
uri = '/rating/module_config/hashmap/groups/thresholds/'
|
||||
request_body = {
|
||||
'group_id': group_id,
|
||||
}
|
||||
return self._do_request('GET', uri,
|
||||
body=self.serialize(request_body))
|
||||
|
||||
def get_hashmap_threshold(self, threshold_id):
|
||||
uri = '/rating/module_config/hashmap/thresholds/' + threshold_id + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
def get_hashmap_thresholds(self, service_id=None,
|
||||
field_id=None, group_id=None):
|
||||
args = locals()
|
||||
args.pop('self')
|
||||
uri = '/rating/module_config/hashmap/thresholds/'
|
||||
request_body = dict((k, v)
|
||||
for k, v in six.iteritems(args) if v is not None)
|
||||
return self._do_request('GET', uri,
|
||||
body=self.serialize(request_body))
|
||||
|
||||
def create_hashmap_threshold(self, field_id=None, group_id=None,
|
||||
threshold_id=None, map_type=None, cost=None,
|
||||
service_id=None, tenant_id=None, level=None):
|
||||
args = locals()
|
||||
args.pop('self')
|
||||
uri = '/rating/module_config/hashmap/thresholds/'
|
||||
request_body = dict((k, v)
|
||||
for k, v in six.iteritems(args) if v is not None)
|
||||
return self._do_request('POST', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=201)
|
||||
|
||||
def update_hashmap_threshold(self, threshold_id, field_id=None,
|
||||
group_id=None, map_type=None, cost=None,
|
||||
service_id=None, tenant_id=None, level=None):
|
||||
args = locals()
|
||||
args.pop('self')
|
||||
uri = '/rating/module_config/hashmap/thresholds/'
|
||||
request_body = dict((k, v)
|
||||
for k, v in six.iteritems(args) if v is not None)
|
||||
return self._do_request('PUT', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=302)
|
||||
|
||||
def delete_hashmap_threshold(self, threshold_id):
|
||||
uri = '/rating/module_config/hashmap/thresholds/'
|
||||
request_body = {
|
||||
'threshold_id': threshold_id,
|
||||
}
|
||||
return self._do_request('DELETE', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=204)
|
||||
|
||||
def get_pyscripts(self, no_data=False):
|
||||
uri = '/rating/module_config/pyscripts/scripts/'
|
||||
request_body = {
|
||||
'no_data': no_data,
|
||||
}
|
||||
return self._do_request('GET', uri, body=self.serialize(request_body))
|
||||
|
||||
def get_pyscript(self, script_id):
|
||||
uri = '/rating/module_config/pyscripts/scripts/' + script_id + '/'
|
||||
return self._do_request('GET', uri)
|
||||
|
||||
@staticmethod
|
||||
def _get_pyscript_request_body(name, data, checksum, script_id):
|
||||
args = locals()
|
||||
request_body = dict((k, v)
|
||||
for k, v in six.iteritems(args) if v is not None)
|
||||
return request_body
|
||||
|
||||
def create_pyscript(self, name, data, checksum=None, script_id=None):
|
||||
uri = '/rating/module_config/pyscripts/scripts/'
|
||||
request_body = self._get_pyscript_request_body(name, data,
|
||||
checksum, script_id)
|
||||
return self._do_request('POST', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=201)
|
||||
|
||||
def update_pyscript(self, script_id, name=None, data=None, checksum=None):
|
||||
uri = '/rating/module_config/pyscripts/scripts/'
|
||||
request_body = self._get_pyscript_request_body(name, data,
|
||||
checksum, script_id)
|
||||
return self._do_request('PUT', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=201)
|
||||
|
||||
def delete_pyscript(self, script_id):
|
||||
uri = '/rating/module_config/pyscripts/scripts/'
|
||||
request_body = {
|
||||
'script_id': script_id,
|
||||
}
|
||||
return self._do_request('DELETE', uri,
|
||||
body=self.serialize(request_body),
|
||||
expected_code=204)
|
||||
|
||||
|
||||
class Manager(manager.Manager):
|
||||
|
||||
rating_params = {
|
||||
'service': CONF.rating_plugin.service_name,
|
||||
'region': CONF.identity.region,
|
||||
'endpoint_type': CONF.rating_plugin.endpoint_type,
|
||||
}
|
||||
|
||||
def __init__(self, credentials=None, service=None):
|
||||
super(Manager, self).__init__(credentials)
|
||||
self.set_rating_client()
|
||||
|
||||
def set_rating_client(self):
|
||||
self.rating_client = RatingClient(self.auth_provider,
|
||||
**self.rating_params)
|
||||
|
||||
|
||||
class CustomIdentityClient(object):
|
||||
"""Custom Keystone client
|
||||
|
||||
Class used by the CK tempest plugin to add the 'rating' role to
|
||||
the dynamically allocated test tenant
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.admin_auth = v3.Password(
|
||||
auth_url=CONF.identity.uri_v3,
|
||||
username=CONF.auth.admin_username,
|
||||
password=CONF.auth.admin_password,
|
||||
project_name=CONF.auth.admin_project_name,
|
||||
project_domain_name=CONF.auth.admin_domain_name,
|
||||
user_domain_name=CONF.auth.admin_domain_name,
|
||||
)
|
||||
self.admin_session = session.Session(auth=self.admin_auth)
|
||||
self.admin_client = client.Client(session=self.admin_session)
|
||||
self.ck_user_id = self._get_ck_user_id()
|
||||
self.rating_role_id = self._get_rating_role_id()
|
||||
|
||||
def enable_rating(self, project_id):
|
||||
"""Assigns the 'rating' role to ck user on the given project"""
|
||||
self.admin_client.roles.grant(self.rating_role_id,
|
||||
user=self.ck_user_id,
|
||||
project=project_id)
|
||||
|
||||
@staticmethod
|
||||
def _find_item(iterable, key, value):
|
||||
item = None
|
||||
for elem in iterable:
|
||||
if getattr(elem, key, None) == value:
|
||||
item = elem
|
||||
return item
|
||||
|
||||
def _get_ck_user_id(self):
|
||||
users = self.admin_client.users.list()
|
||||
return getattr(
|
||||
self._find_item(users, 'name', CONF.rating_plugin.user_name),
|
||||
'id', None,
|
||||
)
|
||||
|
||||
def _get_rating_role_id(self):
|
||||
roles = self.admin_client.roles.list()
|
||||
return getattr(
|
||||
self._find_item(roles, 'name', 'rating'), 'id', None,
|
||||
)
|
0
cloudkitty_tempest_plugin/tests/__init__.py
Normal file
0
cloudkitty_tempest_plugin/tests/__init__.py
Normal file
0
cloudkitty_tempest_plugin/tests/api/__init__.py
Normal file
0
cloudkitty_tempest_plugin/tests/api/__init__.py
Normal file
84
cloudkitty_tempest_plugin/tests/api/base.py
Normal file
84
cloudkitty_tempest_plugin/tests/api/base.py
Normal file
@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Objectif Libre
|
||||
#
|
||||
# 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 six
|
||||
|
||||
from tempest import config
|
||||
from tempest.lib import exceptions
|
||||
import tempest.test
|
||||
|
||||
from cloudkitty_tempest_plugin.services import client
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
def skipIf(flag, reason):
|
||||
def decorator(f):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if getattr(self, flag):
|
||||
self.skipTest(reason)
|
||||
else:
|
||||
f(self, *args, **kwargs)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
class BaseRatingTest(tempest.test.BaseTestCase):
|
||||
"""Base test class for all Rating API tests."""
|
||||
|
||||
client_manager = client.Manager
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseRatingTest, cls).setup_clients()
|
||||
os_var = 'os_{}'.format(cls.credentials[0])
|
||||
cls.rating_client = getattr(cls, os_var).rating_client
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
super(BaseRatingTest, cls).setup_credentials()
|
||||
os_var = 'os_{}'.format(cls.credentials[0])
|
||||
project_id = getattr(cls, os_var).credentials.project_id
|
||||
cls.skip_rating_tests = False
|
||||
try:
|
||||
cls.custom_identity_client = client.CustomIdentityClient()
|
||||
cls.custom_identity_client.enable_rating(project_id)
|
||||
except Exception:
|
||||
cls.skip_rating_tests = True
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseRatingTest, cls).resource_setup()
|
||||
cls._created_resources = {
|
||||
'collector_mapping': list(),
|
||||
'hashmap_service': list(),
|
||||
'hashmap_field': list(),
|
||||
'hashmap_mapping': list(),
|
||||
'hashmap_group': list(),
|
||||
'hashmap_threshold': list(),
|
||||
'pyscript': list(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(BaseRatingTest, cls).resource_cleanup()
|
||||
for method, item_ids in six.iteritems(cls._created_resources):
|
||||
delete_method = 'delete_' + method
|
||||
delete_method = getattr(cls.rating_client, delete_method)
|
||||
for item_id in item_ids:
|
||||
try:
|
||||
delete_method(item_id)
|
||||
except exceptions.NotFound:
|
||||
pass
|
155
cloudkitty_tempest_plugin/tests/api/test_cloudkitty_api.py
Normal file
155
cloudkitty_tempest_plugin/tests/api/test_cloudkitty_api.py
Normal file
@ -0,0 +1,155 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Objectif Libre
|
||||
#
|
||||
# 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.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
from cloudkitty_tempest_plugin.tests.api import base
|
||||
|
||||
|
||||
class CloudkittyAdminAPITest(base.BaseRatingTest):
|
||||
|
||||
credentials = ['admin']
|
||||
|
||||
@decorators.idempotent_id('9c1d4c27-6e7c-42d7-b663-d88f097b7131')
|
||||
def test_get_collector_mappings(self):
|
||||
self.rating_client.get_collector_mappings()
|
||||
|
||||
def _find_item(self, haystack, needle, key, assert_method):
|
||||
found = False
|
||||
for item in haystack:
|
||||
try:
|
||||
if item[key] == needle:
|
||||
found = True
|
||||
except KeyError:
|
||||
continue
|
||||
assert_method(found)
|
||||
|
||||
@decorators.idempotent_id('af902c86-6022-4b94-a716-ec7932d5ae78')
|
||||
def test_create_get_delete_collector_mapping(self):
|
||||
mapping = self.rating_client.create_collector_mapping(
|
||||
collector=data_utils.rand_name('gnocchi'),
|
||||
service=data_utils.rand_name('compute'),
|
||||
)
|
||||
self._created_resources['collector_mapping'].append(
|
||||
mapping['service'],
|
||||
)
|
||||
mappings = self.rating_client.get_collector_mappings()
|
||||
self._find_item(mappings['mappings'],
|
||||
mapping['service'],
|
||||
'service',
|
||||
self.assertTrue)
|
||||
self.rating_client.delete_collector_mapping(mapping['service'])
|
||||
mappings = self.rating_client.get_collector_mappings()
|
||||
self._find_item(mappings['mappings'],
|
||||
mapping['service'],
|
||||
'service',
|
||||
self.assertFalse)
|
||||
|
||||
@decorators.idempotent_id('3fd83647-3058-4450-9588-a528557585c5')
|
||||
def test_get_collector_state(self):
|
||||
collector = self.rating_client.get_collector_state(
|
||||
collector=data_utils.rand_name('gnocchi'),
|
||||
)
|
||||
self.assertFalse(collector['enabled'])
|
||||
|
||||
@decorators.idempotent_id('71131104-fdae-43ec-9bed-c8d1d5ba7eb0')
|
||||
def test_set_collector_state(self):
|
||||
collector_name = data_utils.rand_name('gnocchi')
|
||||
self.rating_client.set_collector_state(
|
||||
collector=collector_name,
|
||||
enabled=True,
|
||||
)
|
||||
collector = self.rating_client.get_collector_state(collector_name)
|
||||
self.assertTrue(collector['enabled'])
|
||||
self.rating_client.set_collector_state(
|
||||
collector=collector_name,
|
||||
enabled=False,
|
||||
)
|
||||
collector = self.rating_client.get_collector_state(collector_name)
|
||||
self.assertFalse(collector['enabled'])
|
||||
|
||||
@decorators.idempotent_id('fba44b6a-6ca4-4155-b5c6-c4eb2465e4fb')
|
||||
def test_get_rating_modules(self):
|
||||
modules = self.rating_client.get_rating_module()
|
||||
self._find_item(modules['modules'],
|
||||
'hashmap',
|
||||
'module_id',
|
||||
self.assertTrue)
|
||||
self._find_item(modules['modules'],
|
||||
'pyscripts',
|
||||
'module_id',
|
||||
self.assertTrue)
|
||||
self._find_item(modules['modules'],
|
||||
'noop',
|
||||
'module_id',
|
||||
self.assertTrue)
|
||||
self.assertEqual(
|
||||
'hashmap',
|
||||
self.rating_client.get_rating_module('hashmap')['module_id'],
|
||||
)
|
||||
self.assertEqual(
|
||||
'pyscripts',
|
||||
self.rating_client.get_rating_module('pyscripts')['module_id'],
|
||||
)
|
||||
|
||||
@decorators.idempotent_id('7fc9e020-9547-4a66-a691-94cab7181358')
|
||||
def test_update_rating_module(self):
|
||||
self.rating_client.update_rating_module('hashmap', enabled=True)
|
||||
module = self.rating_client.get_rating_module('hashmap')
|
||||
self.assertTrue(module['enabled'])
|
||||
self.rating_client.update_rating_module('hashmap', enabled=False)
|
||||
module = self.rating_client.get_rating_module('hashmap')
|
||||
self.assertFalse(module['enabled'])
|
||||
|
||||
@decorators.idempotent_id('daeef22b-d52d-4e89-abb0-ae492e4648d4')
|
||||
def test_reload_rating_modules(self):
|
||||
self.rating_client.reload_rating_modules()
|
||||
|
||||
@base.skipIf('skip_rating_tests',
|
||||
'Rating role was not given to CloudKitty')
|
||||
@decorators.idempotent_id('e439019e-9e8a-4bcd-aa83-95bdba6e6115')
|
||||
def test_get_rated_tenants(self):
|
||||
rated_tenants = self.rating_client.get_rated_tenants()['body']
|
||||
self.assertGreater(len(rated_tenants), 0)
|
||||
|
||||
|
||||
class CloudkittyPrimaryAPITest(base.BaseRatingTest):
|
||||
|
||||
credentials = ['primary']
|
||||
|
||||
@decorators.idempotent_id('3285bccf-d043-4ad1-b64f-af4db8317cf9')
|
||||
def test_get_config(self):
|
||||
self.rating_client.get_config()
|
||||
|
||||
@decorators.idempotent_id('43b03099-0493-4291-9749-85cd8d512811')
|
||||
def test_get_services(self):
|
||||
self.rating_client.get_service()
|
||||
|
||||
@decorators.idempotent_id('64ecae87-0138-41bd-829f-91302dae7802')
|
||||
def test_get_service(self):
|
||||
self.rating_client.get_service('compute')
|
||||
|
||||
@decorators.idempotent_id('cccbff8a-24b2-4251-8f7b-ea941d048b9d')
|
||||
def test_report_summary(self):
|
||||
self.rating_client.get_report_summary()
|
||||
|
||||
@decorators.idempotent_id('2492dfd7-0688-4957-93ac-8c91933c28f5')
|
||||
def test_report_total(self):
|
||||
self.rating_client.get_report_total()
|
||||
|
||||
@decorators.idempotent_id('e233139b-3c75-4b70-b1f5-0776ef32c916')
|
||||
def test_get_storage_dataframes(self):
|
||||
self.rating_client.get_storage_dataframes()
|
328
cloudkitty_tempest_plugin/tests/api/test_hashmap_api.py
Normal file
328
cloudkitty_tempest_plugin/tests/api/test_hashmap_api.py
Normal file
@ -0,0 +1,328 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Objectif Libre
|
||||
#
|
||||
# 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.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
from cloudkitty_tempest_plugin.tests.api import base
|
||||
|
||||
|
||||
class CloudkittyHashmapAPITest(base.BaseRatingTest):
|
||||
|
||||
credentials = ['admin']
|
||||
|
||||
@decorators.idempotent_id('7037a3f8-b462-4243-a0bc-ffa3b4700397')
|
||||
def test_get_hashmap_rating_types(self):
|
||||
self.rating_client.get_hashmap_mapping_types()
|
||||
|
||||
def _setup_dummy_service(self):
|
||||
service = self.rating_client.create_hashmap_service(
|
||||
data_utils.rand_name('service'),
|
||||
)
|
||||
self._created_resources['hashmap_service'].append(
|
||||
service['service_id'])
|
||||
return service['service_id']
|
||||
|
||||
@decorators.idempotent_id('9e968284-7209-46e1-9742-4882b6e2cf2f')
|
||||
def test_create_delete_hashmap_service(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
self.rating_client.delete_hashmap_service(service_id)
|
||||
|
||||
@decorators.idempotent_id('9e9a67d1-e53d-46cf-8e13-8a332c40c32f')
|
||||
def test_get_hashmap_services(self):
|
||||
self.rating_client.get_hashmap_service()
|
||||
|
||||
@decorators.idempotent_id('6c4260c0-8701-4959-b00e-4789d31715a7')
|
||||
def test_get_hashmap_service(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
self.rating_client.get_hashmap_service(service_id=service_id)
|
||||
|
||||
def _setup_dummy_fields(self, service_id):
|
||||
field_ids = list()
|
||||
for i in range(3):
|
||||
field = self.rating_client.create_hashmap_field(
|
||||
data_utils.rand_name('hashmap_field'),
|
||||
service_id,
|
||||
)
|
||||
field_ids.append(field['field_id'])
|
||||
self._created_resources['hashmap_field'].append(field['field_id'])
|
||||
return field_ids
|
||||
|
||||
@decorators.idempotent_id('974669f9-392e-4aec-8e15-d24db23f08d4')
|
||||
def test_create_delete_hashmap_field(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
field_ids = self._setup_dummy_fields(service_id)
|
||||
for field_id in field_ids:
|
||||
self.rating_client.delete_hashmap_field(field_id)
|
||||
self.rating_client.delete_hashmap_service(service_id)
|
||||
|
||||
@decorators.idempotent_id('9ae98590-ff55-47f9-885e-baa4da1957d1')
|
||||
def test_get_hashmap_field(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
self._setup_dummy_fields(service_id)
|
||||
fields = self.rating_client.get_hashmap_fields(service_id)
|
||||
for field in fields['fields']:
|
||||
field_info = self.rating_client.get_hashmap_field(
|
||||
field['field_id'],
|
||||
)
|
||||
self.assertEqual(field_info['field_id'], field['field_id'])
|
||||
self.assertEqual(field_info['service_id'], field['service_id'])
|
||||
self.assertEqual(field_info['name'], field['name'])
|
||||
|
||||
def _find_item(self, haystack, needle, key):
|
||||
found = False
|
||||
for item in haystack:
|
||||
try:
|
||||
if item[key] == needle:
|
||||
found = True
|
||||
except KeyError:
|
||||
continue
|
||||
self.assertTrue(found)
|
||||
|
||||
@decorators.idempotent_id('4deb6914-7ba0-4219-a119-61b39bd58807')
|
||||
def test_get_hashmap_mappings(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
field_ids = self._setup_dummy_fields(service_id)
|
||||
field_mapping = self.rating_client.create_hashmap_mapping(
|
||||
field_id=field_ids[0],
|
||||
value='dummy mapping',
|
||||
)
|
||||
service_mapping = self.rating_client.create_hashmap_mapping(
|
||||
service_id=service_id,
|
||||
)
|
||||
self._created_resources['hashmap_mapping'].append(
|
||||
field_mapping['mapping_id'],
|
||||
)
|
||||
self._created_resources['hashmap_mapping'].append(
|
||||
service_mapping['mapping_id'],
|
||||
)
|
||||
|
||||
service_filtered_mappings = self.rating_client.get_hashmap_mappings(
|
||||
service_id=service_id,
|
||||
)
|
||||
field_filtered_mappings = self.rating_client.get_hashmap_mappings(
|
||||
field_id=field_ids[0],
|
||||
)
|
||||
self._find_item(service_filtered_mappings['mappings'],
|
||||
service_mapping['mapping_id'],
|
||||
'mapping_id')
|
||||
self._find_item(field_filtered_mappings['mappings'],
|
||||
field_mapping['mapping_id'],
|
||||
'mapping_id')
|
||||
|
||||
@decorators.idempotent_id('b7ad24e5-c72c-469a-886b-db43aab8f328')
|
||||
def test_create_delete_hashmap_mapping(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
field_ids = self._setup_dummy_fields(service_id)
|
||||
mapping_ids = list()
|
||||
for field_id in field_ids:
|
||||
mapping = self.rating_client.create_hashmap_mapping(
|
||||
field_id=field_id,
|
||||
value='dummy mapping',
|
||||
)
|
||||
self.assertEqual('dummy mapping', mapping['value'])
|
||||
mapping_ids.append(mapping['mapping_id'])
|
||||
mapping = self.rating_client.create_hashmap_mapping(
|
||||
service_id=service_id,
|
||||
)
|
||||
mapping_ids.append(mapping['mapping_id'])
|
||||
self._created_resources['hashmap_mapping'] += mapping_ids
|
||||
for mapping_id in mapping_ids:
|
||||
self.rating_client.delete_hashmap_mapping(mapping_id)
|
||||
|
||||
@decorators.idempotent_id('6a04634d-2d3a-406e-980a-e7c7c9cc081b')
|
||||
def test_update_hashmap_mapping(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
field_ids = self._setup_dummy_fields(service_id)
|
||||
mapping = self.rating_client.create_hashmap_mapping(
|
||||
field_id=field_ids[0],
|
||||
value='dummy field',
|
||||
)
|
||||
self._created_resources['hashmap_mapping'].append(
|
||||
mapping['mapping_id'],
|
||||
)
|
||||
self.assertEqual('dummy field', mapping['value'])
|
||||
self.rating_client.update_hashmap_mapping(
|
||||
mapping['mapping_id'],
|
||||
value='new value',
|
||||
)
|
||||
mapping = self.rating_client.get_hashmap_mapping(
|
||||
mapping['mapping_id']
|
||||
)
|
||||
self.assertEqual('new value', mapping['value'])
|
||||
|
||||
@decorators.idempotent_id('0f9200ab-146b-4349-a579-ce12062f465b')
|
||||
def test_create_delete_hashmap_group(self):
|
||||
group = self.rating_client.create_hashmap_group(
|
||||
data_utils.rand_name('dummy_group'),
|
||||
)
|
||||
self._created_resources['hashmap_group'].append(group['group_id'])
|
||||
self.rating_client.delete_hashmap_group(group['group_id'])
|
||||
|
||||
@decorators.idempotent_id('858a019a-fb64-4656-b7a6-c92917f641ab')
|
||||
def test_get_hashmap_group(self):
|
||||
group = self.rating_client.create_hashmap_group(
|
||||
data_utils.rand_name('dummy_group'),
|
||||
)
|
||||
self._created_resources['hashmap_group'].append(group['group_id'])
|
||||
group_name = group['name']
|
||||
groups = self.rating_client.get_hashmap_group()
|
||||
self._find_item(groups['groups'], group_name, 'name')
|
||||
group = self.rating_client.get_hashmap_group(group['group_id'])
|
||||
self.assertEqual(group['name'], group_name)
|
||||
|
||||
@decorators.idempotent_id('98ca42dd-a9e2-477a-8e42-16389aed1f44')
|
||||
def test_get_hashmap_mapping_group(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
field_ids = self._setup_dummy_fields(service_id)
|
||||
group = self.rating_client.create_hashmap_group(
|
||||
data_utils.rand_name('dummy_group'),
|
||||
)
|
||||
group_name = group['name']
|
||||
self._created_resources['hashmap_group'].append(group['group_id'])
|
||||
mapping = self.rating_client.create_hashmap_mapping(
|
||||
field_id=field_ids[0],
|
||||
group_id=group['group_id'],
|
||||
value='dummy mapping',
|
||||
)
|
||||
self._created_resources['hashmap_mapping'].append(
|
||||
mapping['mapping_id'],
|
||||
)
|
||||
group = self.rating_client.get_hashmap_mapping_group(
|
||||
mapping['mapping_id'],
|
||||
)
|
||||
self.assertEqual(group['name'], group_name)
|
||||
|
||||
@decorators.idempotent_id('92860fc8-596a-42fd-b0d5-97e0f5a7bd2c')
|
||||
def test_get_hashmap_group_mappings(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
field_ids = self._setup_dummy_fields(service_id)
|
||||
group = self.rating_client.create_hashmap_group(
|
||||
data_utils.rand_name('dummy_group'),
|
||||
)
|
||||
self._created_resources['hashmap_group'].append(group['group_id'])
|
||||
for i in range(3):
|
||||
mapping = self.rating_client.create_hashmap_mapping(
|
||||
field_id=field_ids[i],
|
||||
group_id=group['group_id'],
|
||||
value='dummy mapping {}'.format(i),
|
||||
)
|
||||
self._created_resources['hashmap_mapping'].append(
|
||||
mapping['mapping_id']
|
||||
)
|
||||
mappings = self.rating_client.get_hashmap_group_mappings(
|
||||
group['group_id'],
|
||||
)
|
||||
for i in range(3):
|
||||
self._find_item(mappings['mappings'],
|
||||
'dummy mapping {}'.format(i),
|
||||
'value')
|
||||
|
||||
@decorators.idempotent_id('d2b3dba3-91df-4aa7-9ae2-96d971df2dbf')
|
||||
def test_create_delete_update_hashmap_threshold(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
field_ids = self._setup_dummy_fields(service_id)
|
||||
thresholds = list()
|
||||
thresholds.append(self.rating_client.create_hashmap_threshold(
|
||||
service_id=service_id,
|
||||
level=0.95,
|
||||
cost=12,
|
||||
))
|
||||
self._created_resources['hashmap_threshold'].append(
|
||||
thresholds[-1]['threshold_id']
|
||||
)
|
||||
for idx, field_id in enumerate(field_ids):
|
||||
thresholds.append(self.rating_client.create_hashmap_threshold(
|
||||
field_id=field_id,
|
||||
level=0.95 * (idx + 1),
|
||||
cost=12 * (idx + 1),
|
||||
))
|
||||
self._created_resources['hashmap_threshold'].append(
|
||||
thresholds[-1]['threshold_id']
|
||||
)
|
||||
for threshold in thresholds:
|
||||
self.rating_client.update_hashmap_threshold(
|
||||
threshold['threshold_id'],
|
||||
cost=42,
|
||||
level=1.23,
|
||||
)
|
||||
for threshold in thresholds:
|
||||
self.rating_client.delete_hashmap_threshold(
|
||||
threshold['threshold_id'],
|
||||
)
|
||||
|
||||
@decorators.idempotent_id('dc463432-3b92-44ac-8caf-b789857f9db7')
|
||||
def test_get_hashmap_threshold(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
self._setup_dummy_fields(service_id)
|
||||
created_threshold = self.rating_client.create_hashmap_threshold(
|
||||
service_id=service_id,
|
||||
level=1.95,
|
||||
cost=42,
|
||||
)
|
||||
self._created_resources['hashmap_threshold'].append(
|
||||
created_threshold['threshold_id'],
|
||||
)
|
||||
threshold = self.rating_client.get_hashmap_threshold(
|
||||
created_threshold['threshold_id'],
|
||||
)
|
||||
self.assertEqual(threshold['level'], created_threshold['level'])
|
||||
self.rating_client.delete_hashmap_threshold(
|
||||
created_threshold['threshold_id'],
|
||||
)
|
||||
|
||||
@decorators.idempotent_id('d04caad5-eb18-40ce-817e-13c257633cca')
|
||||
def test_get_hashmap_thresholds(self):
|
||||
service_id = self._setup_dummy_service()
|
||||
field_ids = self._setup_dummy_fields(service_id)
|
||||
group = self.rating_client.create_hashmap_group(
|
||||
data_utils.rand_name('dummy_group'),
|
||||
)
|
||||
self._created_resources['hashmap_group'].append(group['group_id'])
|
||||
field_threshold = self.rating_client.create_hashmap_threshold(
|
||||
group_id=group['group_id'],
|
||||
field_id=field_ids[0],
|
||||
level=1.95,
|
||||
cost=42,
|
||||
)
|
||||
self._created_resources['hashmap_threshold'].append(
|
||||
field_threshold['threshold_id'],
|
||||
)
|
||||
service_threshold = self.rating_client.create_hashmap_threshold(
|
||||
service_id=service_id,
|
||||
group_id=group['group_id'],
|
||||
level=1.95,
|
||||
cost=42,
|
||||
)
|
||||
self._created_resources['hashmap_threshold'].append(
|
||||
service_threshold['threshold_id'],
|
||||
)
|
||||
thresholds = self.rating_client.get_hashmap_thresholds(
|
||||
service_id=service_id,
|
||||
)
|
||||
self._find_item(thresholds['thresholds'],
|
||||
group['group_id'],
|
||||
'group_id')
|
||||
thresholds = self.rating_client.get_hashmap_thresholds(
|
||||
field_id=field_ids[0],
|
||||
)
|
||||
self._find_item(thresholds['thresholds'],
|
||||
group['group_id'],
|
||||
'group_id')
|
||||
thresholds = self.rating_client.get_hashmap_thresholds(
|
||||
group_id=group['group_id']
|
||||
)
|
||||
self._find_item(thresholds['thresholds'], service_id, 'service_id')
|
||||
self._find_item(thresholds['thresholds'], field_ids[0], 'field_id')
|
86
cloudkitty_tempest_plugin/tests/api/test_pyscript_api.py
Normal file
86
cloudkitty_tempest_plugin/tests/api/test_pyscript_api.py
Normal file
@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Objectif Libre
|
||||
#
|
||||
# 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.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
from cloudkitty_tempest_plugin.tests.api import base
|
||||
|
||||
SCRIPT_DATA_ONE = """
|
||||
def dumbfunc():
|
||||
return 0
|
||||
|
||||
data = dumbfunc()
|
||||
"""
|
||||
|
||||
SCRIPT_DATA_TWO = """
|
||||
def dumbfunc():
|
||||
return 0
|
||||
|
||||
data = dumbfunc() + 2
|
||||
"""
|
||||
|
||||
|
||||
class CloudkittyPyscriptAPITest(base.BaseRatingTest):
|
||||
|
||||
credentials = ['admin']
|
||||
|
||||
@decorators.idempotent_id('2015c966-b707-40f7-b84d-9aa6550b9e41')
|
||||
def test_get_pyscripts(self):
|
||||
self.rating_client.get_pyscripts()
|
||||
|
||||
@decorators.idempotent_id('9e78cccc-ca85-42ce-8648-4cd9682375df')
|
||||
def test_create_update_delete_pyscript(self):
|
||||
pyscript = self.rating_client.create_pyscript(
|
||||
data_utils.rand_name('dummy_script'),
|
||||
SCRIPT_DATA_ONE,
|
||||
)
|
||||
self._created_resources['pyscript'].append(pyscript['script_id'])
|
||||
self.assertEqual(pyscript['data'], SCRIPT_DATA_ONE)
|
||||
self.rating_client.update_pyscript(pyscript['script_id'],
|
||||
data=SCRIPT_DATA_TWO,
|
||||
name=pyscript['name'])
|
||||
pyscript = self.rating_client.get_pyscript(pyscript['script_id'])
|
||||
self.assertEqual(pyscript['data'], SCRIPT_DATA_TWO)
|
||||
self.rating_client.delete_pyscript(pyscript['script_id'])
|
||||
|
||||
@decorators.idempotent_id('3fbaf8b4-c472-4509-8d73-55dc4a87a442')
|
||||
def test_get_pyscript(self):
|
||||
pyscript = self.rating_client.create_pyscript(
|
||||
data_utils.rand_name('dummy_script'),
|
||||
SCRIPT_DATA_ONE,
|
||||
)
|
||||
self.assertEqual(pyscript['data'], SCRIPT_DATA_ONE)
|
||||
self._created_resources['pyscript'].append(pyscript['script_id'])
|
||||
pyscript = self.rating_client.get_pyscript(pyscript['script_id'])
|
||||
|
||||
|
||||
class CloudkittyPyscriptAPITestNegative(base.BaseRatingTest):
|
||||
|
||||
credentials = ['admin']
|
||||
|
||||
@decorators.idempotent_id('999c97cc-1d71-43b8-988f-d89b8fac4040')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_update_script_negative(self):
|
||||
pyscript = self.rating_client.create_pyscript(
|
||||
data_utils.rand_name('dummy_script'),
|
||||
SCRIPT_DATA_ONE,
|
||||
)
|
||||
self._created_resources['pyscript'].append(pyscript['script_id'])
|
||||
fake_id = data_utils.rand_uuid()
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.rating_client.get_pyscript,
|
||||
fake_id)
|
0
cloudkitty_tempest_plugin/tests/scenario/__init__.py
Normal file
0
cloudkitty_tempest_plugin/tests/scenario/__init__.py
Normal file
11
requirements.txt
Normal file
11
requirements.txt
Normal file
@ -0,0 +1,11 @@
|
||||
# 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.
|
||||
|
||||
keystoneauth1>=2.18.0 # Apache-2.0
|
||||
oslo.config>=3.18.0 # Apache-2.0
|
||||
oslo.serialization>=2.14.0 # Apache-2.0
|
||||
pbr>=2.0 # Apache-2.0
|
||||
python-keystoneclient>=3.6.0 # Apache-2.0
|
||||
six>=1.9.0 # MIT
|
||||
tempest>=15.0.0 # Apache-2.0
|
28
setup.cfg
Normal file
28
setup.cfg
Normal file
@ -0,0 +1,28 @@
|
||||
[metadata]
|
||||
name = cloudkitty_tempest_plugin
|
||||
summary = Tempest plugin for CloudKitty
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.3
|
||||
Programming Language :: Python :: 3.4
|
||||
|
||||
[files]
|
||||
packages =
|
||||
cloudkitty_tempest_plugin
|
||||
|
||||
[entry_points]
|
||||
tempest.test_plugins =
|
||||
cloudkitty_tests = cloudkitty_tempest_plugin.plugin:CloudkittyTempestPlugin
|
29
setup.py
Normal file
29
setup.py
Normal file
@ -0,0 +1,29 @@
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True)
|
15
test-requirements.txt
Normal file
15
test-requirements.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# 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.
|
||||
|
||||
hacking>=0.12.0,<0.13 # Apache-2.0
|
||||
|
||||
coverage>=4.0,!=4.4 # Apache-2.0
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
sphinx>=1.6.2 # BSD
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
stestr>=1.0.0 # Apache-2.0
|
||||
testtools>=1.4.0 # MIT
|
||||
openstackdocstheme>=1.11.0 # Apache-2.0
|
||||
# releasenotes
|
||||
reno>=1.8.0 # Apache-2.0
|
43
tox.ini
Normal file
43
tox.ini
Normal file
@ -0,0 +1,43 @@
|
||||
[tox]
|
||||
minversion = 2.0
|
||||
envlist = py34,py27,pypy,pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
PYTHONWARNINGS=default::DeprecationWarning
|
||||
OS_STDOUT_CAPTURE=1
|
||||
OS_STDERR_CAPTURE=1
|
||||
OS_TEST_TIMEOUT=60
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8 {posargs}
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
PYTHON=coverage run --source cloudkitty_tempest_plugin --parallel-mode
|
||||
commands =
|
||||
stestr run {posargs}
|
||||
coverage combine
|
||||
coverage html -d cover
|
||||
coverage xml -o cover/coverage.xml
|
||||
|
||||
[testenv:debug]
|
||||
commands = oslo_debug_helper {posargs}
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
Loading…
x
Reference in New Issue
Block a user