Added gabbi API tests
Added tests on all the core API. Change-Id: I6610ddbe5071ce1231c80319ab1b43c9cd626de4 Depends-On: I2231521708f163d80538fe2e7e5559d4c06cf0c3 Depends-On: Ib05d533eecd32159c1bc7a639db1a49df678f726 Depends-On: I3302617c266111ba1d705d04f1acf20b9348614d Depends-On: I0ab0f1b2a75d39b8d371c54a264856db69adc2fd
This commit is contained in:
parent
23458597ab
commit
04a9f0cd43
@ -2,3 +2,4 @@
|
||||
test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./cloudkitty/tests $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
||||
group_regex=gabbi\.driver\.(test_[^_]+_[^_]+)
|
||||
|
@ -48,12 +48,13 @@ auth_opts = [
|
||||
api_opts = [
|
||||
cfg.StrOpt('host_ip',
|
||||
default="0.0.0.0",
|
||||
help="Host serving the API."
|
||||
),
|
||||
help="Host serving the API."),
|
||||
cfg.IntOpt('port',
|
||||
default=8888,
|
||||
help="Host port serving the API."
|
||||
),
|
||||
help="Host port serving the API."),
|
||||
cfg.BoolOpt('pecan_debug',
|
||||
default=False,
|
||||
help='Toggle Pecan Debug Middleware.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -87,7 +88,7 @@ def setup_app(pecan_config=None, extra_hooks=None):
|
||||
app_conf.app.root,
|
||||
static_root=app_conf.app.static_root,
|
||||
template_path=app_conf.app.template_path,
|
||||
debug=CONF.debug,
|
||||
debug=CONF.api.pecan_debug,
|
||||
force_canonical=getattr(app_conf.app, 'force_canonical', True),
|
||||
hooks=app_hooks,
|
||||
guess_content_type_from_ext=False
|
||||
|
@ -102,7 +102,7 @@ class ModulesController(rest.RestController, RatingModulesMixin):
|
||||
with lock:
|
||||
module = self.extensions[module_id]
|
||||
except KeyError:
|
||||
pecan.abort(404, 'Module not found.')
|
||||
pecan.abort(400, 'Module not found.')
|
||||
infos = module.obj.module_info.copy()
|
||||
infos['module_id'] = infos.pop('name')
|
||||
return rating_models.CloudkittyModule(**infos)
|
||||
@ -218,3 +218,4 @@ class RatingController(rest.RestController):
|
||||
policy.enforce(pecan.request.context, 'rating:module_config', {})
|
||||
self.modules.reload_extensions()
|
||||
self.module_config.reload_extensions()
|
||||
self.module_config.expose_modules()
|
||||
|
@ -54,25 +54,28 @@ class DataFramesController(rest.RestController):
|
||||
backend = pecan.request.storage_backend
|
||||
dataframes = []
|
||||
try:
|
||||
frames = backend.get_time_frame(begin_ts, end_ts,
|
||||
frames = backend.get_time_frame(begin_ts,
|
||||
end_ts,
|
||||
tenant_id=tenant_id,
|
||||
res_type=resource_type)
|
||||
for frame in frames:
|
||||
for service, data_list in frame['usage'].items():
|
||||
frame_tenant = None
|
||||
resources = []
|
||||
for data in data_list:
|
||||
desc = data['desc'] if data['desc'] else {}
|
||||
price = decimal.Decimal(data['rating']['price'])
|
||||
price = decimal.Decimal(str(data['rating']['price']))
|
||||
resource = storage_models.RatedResource(
|
||||
service=service,
|
||||
desc=desc,
|
||||
volume=data['vol']['qty'],
|
||||
rating=price)
|
||||
frame_tenant = data['tenant_id']
|
||||
resources.append(resource)
|
||||
dataframe = storage_models.DataFrame(
|
||||
begin=ck_utils.iso2dt(frame['period']['begin']),
|
||||
end=ck_utils.iso2dt(frame['period']['end']),
|
||||
tenant_id=tenant_id, # FIXME
|
||||
tenant_id=frame_tenant,
|
||||
resources=resources)
|
||||
dataframes.append(dataframe)
|
||||
except ck_storage.NoTimeFrame:
|
||||
|
@ -60,7 +60,7 @@ class RatedDataFrame(Base, models.ModelBase):
|
||||
|
||||
# Volume informations
|
||||
vol_dict = {}
|
||||
vol_dict['qty'] = self.qty
|
||||
vol_dict['qty'] = self.qty.normalize()
|
||||
vol_dict['unit'] = self.unit
|
||||
res_dict = {}
|
||||
|
||||
|
@ -19,11 +19,20 @@ from oslo_config import fixture as config_fixture
|
||||
from oslotest import base
|
||||
import testscenarios
|
||||
|
||||
from cloudkitty import collector
|
||||
from cloudkitty import db
|
||||
from cloudkitty.db import api as ck_db_api
|
||||
from cloudkitty import rating
|
||||
|
||||
|
||||
class FakeCollectorModule(collector.BaseCollector):
|
||||
collector_name = 'test_fake'
|
||||
dependencies = tuple()
|
||||
|
||||
def __init__(self):
|
||||
super(FakeCollectorModule, self).__init__([], period=3600)
|
||||
|
||||
|
||||
class FakeRatingModule(rating.RatingProcessorBase):
|
||||
module_name = 'fake'
|
||||
description = 'fake rating module'
|
||||
|
0
cloudkitty/tests/gabbi/__init__.py
Normal file
0
cloudkitty/tests/gabbi/__init__.py
Normal file
311
cloudkitty/tests/gabbi/fixtures.py
Normal file
311
cloudkitty/tests/gabbi/fixtures.py
Normal file
@ -0,0 +1,311 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
#
|
||||
# @author: Stéphane Albert
|
||||
#
|
||||
import abc
|
||||
import decimal
|
||||
import os
|
||||
|
||||
from gabbi import fixture
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as conf_fixture
|
||||
from oslo_db.sqlalchemy import utils
|
||||
import oslo_messaging as messaging
|
||||
from oslo_messaging import conffixture
|
||||
from oslo_policy import opts as policy_opts
|
||||
import six
|
||||
from stevedore import driver
|
||||
from stevedore import extension
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from cloudkitty.api import app
|
||||
from cloudkitty.common import rpc
|
||||
from cloudkitty import db
|
||||
from cloudkitty.db import api as ck_db_api
|
||||
from cloudkitty import rating
|
||||
from cloudkitty import storage
|
||||
from cloudkitty.storage.sqlalchemy import models
|
||||
from cloudkitty import tests
|
||||
from cloudkitty import utils as ck_utils
|
||||
|
||||
INITIAL_TIMESTAMP = 1420070400
|
||||
|
||||
|
||||
class UUIDFixture(fixture.GabbiFixture):
|
||||
def start_fixture(self):
|
||||
FAKE_UUID = '6c1b8a30-797f-4b7e-ad66-9879b79059fb'
|
||||
patcher = mock.patch(
|
||||
'oslo_utils.uuidutils.generate_uuid',
|
||||
return_value=FAKE_UUID)
|
||||
patcher.start()
|
||||
self.patcher = patcher
|
||||
|
||||
def stop_fixture(self):
|
||||
self.patcher.stop()
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseExtensionFixture(fixture.GabbiFixture):
|
||||
klass = None
|
||||
namespace = None
|
||||
stevedore_mgr = None
|
||||
assert_args = {}
|
||||
|
||||
@abc.abstractmethod
|
||||
def setup_fake_modules(self):
|
||||
pass
|
||||
|
||||
def start_fixture(self):
|
||||
fake_extensions = self.setup_fake_modules()
|
||||
self.mock = mock.patch(self.klass)
|
||||
fake_mgr = self.stevedore_mgr.make_test_instance(
|
||||
fake_extensions,
|
||||
self.namespace)
|
||||
self.patch = self.mock.start()
|
||||
self.patch.return_value = fake_mgr
|
||||
|
||||
def stop_fixture(self):
|
||||
self.patch.assert_called_with(
|
||||
self.namespace,
|
||||
**self.assert_args)
|
||||
self.mock.stop()
|
||||
|
||||
|
||||
class CollectorExtensionsFixture(BaseExtensionFixture):
|
||||
klass = 'stevedore.driver.DriverManager'
|
||||
namespace = 'cloudkitty.collector.backends'
|
||||
stevedore_mgr = driver.DriverManager
|
||||
assert_args = {
|
||||
'invoke_kwds': {'period': 3600},
|
||||
'invoke_on_load': True}
|
||||
|
||||
def setup_fake_modules(self):
|
||||
def fake_metric(start,
|
||||
end=None,
|
||||
project_id=None,
|
||||
q_filter=None):
|
||||
return None
|
||||
|
||||
fake_module1 = tests.FakeCollectorModule()
|
||||
fake_module1.collector_name = 'fake1'
|
||||
fake_module1.get_compute = fake_metric
|
||||
fake_module2 = tests.FakeCollectorModule()
|
||||
fake_module2.collector_name = 'fake2'
|
||||
fake_module2.get_volume = fake_metric
|
||||
fake_module3 = tests.FakeCollectorModule()
|
||||
fake_module3.collector_name = 'fake3'
|
||||
fake_module3.get_compute = fake_metric
|
||||
fake_extensions = [
|
||||
extension.Extension(
|
||||
'fake1',
|
||||
'cloudkitty.tests.FakeCollectorModule1',
|
||||
None,
|
||||
fake_module1),
|
||||
extension.Extension(
|
||||
'fake2',
|
||||
'cloudkitty.tests.FakeCollectorModule2',
|
||||
None,
|
||||
fake_module2),
|
||||
extension.Extension(
|
||||
'fake3',
|
||||
'cloudkitty.tests.FakeCollectorModule3',
|
||||
None,
|
||||
fake_module3)]
|
||||
return fake_extensions[0]
|
||||
|
||||
|
||||
class RatingModulesFixture(BaseExtensionFixture):
|
||||
klass = 'stevedore.extension.ExtensionManager'
|
||||
namespace = 'cloudkitty.rating.processors'
|
||||
stevedore_mgr = extension.ExtensionManager
|
||||
assert_args = {
|
||||
'invoke_on_load': True}
|
||||
|
||||
def setup_fake_modules(self):
|
||||
class FakeConfigController(rating.RatingRestControllerBase):
|
||||
_custom_actions = {
|
||||
'test': ['GET']
|
||||
}
|
||||
|
||||
@wsme_pecan.wsexpose(wtypes.text)
|
||||
def get_test(self):
|
||||
"""Return the list of every mapping type available.
|
||||
|
||||
"""
|
||||
return 'OK'
|
||||
|
||||
fake_module1 = tests.FakeRatingModule()
|
||||
fake_module1.module_name = 'fake1'
|
||||
fake_module1.set_priority(3)
|
||||
fake_module2 = tests.FakeRatingModule()
|
||||
fake_module2.module_name = 'fake2'
|
||||
fake_module2.config_controller = FakeConfigController
|
||||
fake_module2.set_priority(1)
|
||||
fake_module3 = tests.FakeRatingModule()
|
||||
fake_module3.module_name = 'fake3'
|
||||
fake_module3.set_priority(2)
|
||||
fake_extensions = [
|
||||
extension.Extension(
|
||||
'fake1',
|
||||
'cloudkitty.tests.FakeRatingModule1',
|
||||
None,
|
||||
fake_module1),
|
||||
extension.Extension(
|
||||
'fake2',
|
||||
'cloudkitty.tests.FakeRatingModule2',
|
||||
None,
|
||||
fake_module2),
|
||||
extension.Extension(
|
||||
'fake3',
|
||||
'cloudkitty.tests.FakeRatingModule3',
|
||||
None,
|
||||
fake_module3)]
|
||||
return fake_extensions
|
||||
|
||||
|
||||
class ConfigFixture(fixture.GabbiFixture):
|
||||
def start_fixture(self):
|
||||
self.conf = None
|
||||
conf = conf_fixture.Config().conf
|
||||
policy_opts.set_defaults(conf)
|
||||
msg_conf = conffixture.ConfFixture(conf)
|
||||
msg_conf.transport_driver = 'fake'
|
||||
conf.import_group('api', 'cloudkitty.api.app')
|
||||
conf.set_override('auth_strategy', 'noauth')
|
||||
conf.set_override('connection', 'sqlite:///', 'database')
|
||||
conf.set_override('policy_file',
|
||||
os.path.abspath('etc/cloudkitty/policy.json'),
|
||||
group='oslo_policy')
|
||||
conf.import_group('storage', 'cloudkitty.storage')
|
||||
conf.set_override('backend', 'sqlalchemy', 'storage')
|
||||
self.conf = conf
|
||||
self.conn = ck_db_api.get_instance()
|
||||
migration = self.conn.get_migration()
|
||||
migration.upgrade('head')
|
||||
|
||||
def stop_fixture(self):
|
||||
if self.conf:
|
||||
self.conf.reset()
|
||||
db.get_engine().dispose()
|
||||
|
||||
|
||||
class BaseFakeRPC(fixture.GabbiFixture):
|
||||
endpoint = None
|
||||
|
||||
def start_fixture(self):
|
||||
rpc.init()
|
||||
target = messaging.Target(topic='cloudkitty',
|
||||
server=cfg.CONF.host,
|
||||
version='1.0')
|
||||
endpoints = [
|
||||
self.endpoint()
|
||||
]
|
||||
self.server = rpc.get_server(target, endpoints)
|
||||
self.server.start()
|
||||
|
||||
def stop_fixture(self):
|
||||
self.server.stop()
|
||||
|
||||
|
||||
class QuoteFakeRPC(BaseFakeRPC):
|
||||
class FakeRPCEndpoint(object):
|
||||
target = messaging.Target(namespace='rating',
|
||||
version='1.0')
|
||||
|
||||
def quote(self, ctxt, res_data):
|
||||
return str(1.0)
|
||||
|
||||
endpoint = FakeRPCEndpoint
|
||||
|
||||
|
||||
class BaseStorageDataFixture(fixture.GabbiFixture):
|
||||
def create_fake_data(self, begin, end):
|
||||
data = [{
|
||||
"period": {
|
||||
"begin": begin,
|
||||
"end": end},
|
||||
"usage": {
|
||||
"compute": [
|
||||
{
|
||||
"desc": {
|
||||
"dummy": True,
|
||||
"fake_meta": 1.0},
|
||||
"vol": {
|
||||
"qty": 1,
|
||||
"unit": "nothing"},
|
||||
"rating": {
|
||||
"price": decimal.Decimal('1.337')}}]}}]
|
||||
return data
|
||||
|
||||
def start_fixture(self):
|
||||
self.storage = storage.get_storage()
|
||||
self.storage.init()
|
||||
self.initialize_data()
|
||||
|
||||
def stop_fixture(self):
|
||||
model = models.RatedDataFrame
|
||||
session = db.get_session()
|
||||
q = utils.model_query(
|
||||
model,
|
||||
session)
|
||||
q.delete()
|
||||
|
||||
|
||||
class StorageDataFixture(BaseStorageDataFixture):
|
||||
def initialize_data(self):
|
||||
nodata_duration = (24 * 3 + 12) * 3600
|
||||
tenant_list = ['8f82cc70-e50c-466e-8624-24bdea811375',
|
||||
'7606a24a-b8ad-4ae0-be6c-3d7a41334a2e']
|
||||
for tenant in tenant_list:
|
||||
for i in range(INITIAL_TIMESTAMP,
|
||||
INITIAL_TIMESTAMP + nodata_duration,
|
||||
3600):
|
||||
self.storage.nodata(i, i + 3600, tenant)
|
||||
data_ts = INITIAL_TIMESTAMP + nodata_duration + 3600
|
||||
data_duration = (24 * 2 + 8) * 3600
|
||||
for i in range(data_ts,
|
||||
data_ts + data_duration,
|
||||
3600):
|
||||
data = self.create_fake_data(i, i + 3600)
|
||||
self.storage.append(data, tenant_list[0])
|
||||
half_duration = int(data_duration / 2)
|
||||
for i in range(data_ts,
|
||||
data_ts + half_duration,
|
||||
3600):
|
||||
data = self.create_fake_data(i, i + 3600)
|
||||
self.storage.append(data, tenant_list[1])
|
||||
for i in range(data_ts + half_duration + 3600,
|
||||
data_ts + data_duration,
|
||||
3600):
|
||||
self.storage.nodata(i, i + 3600, tenant_list[1])
|
||||
|
||||
|
||||
class NowStorageDataFixture(BaseStorageDataFixture):
|
||||
def initialize_data(self):
|
||||
begin = ck_utils.get_month_start_timestamp()
|
||||
for i in range(begin,
|
||||
begin + 3600 * 12,
|
||||
3600):
|
||||
data = self.create_fake_data(i, i + 3600)
|
||||
self.storage.append(data,
|
||||
'3d9a1b33-482f-42fd-aef9-b575a3da9369')
|
||||
|
||||
|
||||
def setup_app():
|
||||
rpc.init()
|
||||
return app.setup_app()
|
158
cloudkitty/tests/gabbi/gabbits/v1_billing.yaml
Normal file
158
cloudkitty/tests/gabbi/gabbits/v1_billing.yaml
Normal file
@ -0,0 +1,158 @@
|
||||
fixtures:
|
||||
- ConfigFixture
|
||||
- RatingModulesFixture
|
||||
- QuoteFakeRPC
|
||||
|
||||
tests:
|
||||
- name: reload list of modules available
|
||||
url: /v1/billing/reload_modules
|
||||
status: 204
|
||||
|
||||
- name: list all modules available
|
||||
url: /v1/billing/modules
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.modules.`len`: 3
|
||||
$.modules[0].priority: 3
|
||||
$.modules[0].module_id: "fake1"
|
||||
$.modules[0].enabled: false
|
||||
$.modules[0].description: "fake rating module"
|
||||
$.modules[0].hot-config: false
|
||||
$.modules[1].priority: 1
|
||||
$.modules[1].module_id: "fake2"
|
||||
$.modules[1].enabled: false
|
||||
$.modules[1].description: "fake rating module"
|
||||
$.modules[1].hot-config: false
|
||||
$.modules[2].priority: 2
|
||||
$.modules[2].module_id: "fake3"
|
||||
$.modules[2].enabled: false
|
||||
$.modules[2].description: "fake rating module"
|
||||
$.modules[2].hot-config: false
|
||||
|
||||
- name: get information of one module
|
||||
url: /v1/billing/modules/fake2
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.priority: 1
|
||||
$.module_id: "fake2"
|
||||
$.enabled: false
|
||||
$.description: "fake rating module"
|
||||
$.hot-config: false
|
||||
|
||||
- name: get information of a unknown module
|
||||
url: /v1/billing/modules/fakb
|
||||
status: 400
|
||||
response_strings:
|
||||
- "Module not found."
|
||||
|
||||
- name: change priority of a module
|
||||
url: /v1/billing/modules/fake3
|
||||
method: PUT
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
module_id: "fake3"
|
||||
priority: 5
|
||||
status: 302
|
||||
response_headers:
|
||||
location: "$SCHEME://$NETLOC/v1/billing/modules/fake3"
|
||||
|
||||
- name: get information of the modified module (priority)
|
||||
url: $LOCATION
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.priority: 5
|
||||
$.module_id: "fake3"
|
||||
$.enabled: false
|
||||
$.description: "fake rating module"
|
||||
$.hot-config: false
|
||||
|
||||
- name: change enabled status of a module
|
||||
url: /v1/billing/modules/fake3
|
||||
method: PUT
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
module_id: "fake3"
|
||||
enabled: true
|
||||
status: 302
|
||||
response_headers:
|
||||
location: "$SCHEME://$NETLOC/v1/billing/modules/fake3"
|
||||
|
||||
- name: get information of the modified module (status)
|
||||
url: $LOCATION
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.priority: 5
|
||||
$.module_id: "fake3"
|
||||
$.enabled: true
|
||||
$.description: "fake rating module"
|
||||
$.hot-config: false
|
||||
|
||||
- name: change status and priority of a module
|
||||
url: /v1/billing/modules/fake3
|
||||
method: PUT
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
module_id: "fake3"
|
||||
priority: 3
|
||||
enabled: false
|
||||
status: 302
|
||||
response_headers:
|
||||
location: "$SCHEME://$NETLOC/v1/billing/modules/fake3"
|
||||
|
||||
- name: get information of the modified module (both)
|
||||
url: $LOCATION
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.priority: 3
|
||||
$.module_id: "fake3"
|
||||
$.enabled: false
|
||||
$.description: "fake rating module"
|
||||
$.hot-config: false
|
||||
|
||||
- name: get a quote for a resource description
|
||||
url: /v1/billing/quote
|
||||
method: POST
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
resources:
|
||||
- service: "compute"
|
||||
volume: "1.0"
|
||||
desc:
|
||||
test: 1
|
||||
status: 200
|
||||
response_strings:
|
||||
- "1.0"
|
||||
|
||||
- name: module without custom API should use notconfigurable controller (GET)
|
||||
url: /v1/billing/module_config/fake1
|
||||
status: 409
|
||||
response_strings:
|
||||
- "Module is not configurable"
|
||||
|
||||
- name: module without custom API should use notconfigurable controller (POST)
|
||||
url: /v1/billing/module_config/fake1
|
||||
method: POST
|
||||
status: 409
|
||||
response_strings:
|
||||
- "Module is not configurable"
|
||||
|
||||
- name: module without custom API should use notconfigurable controller (PUT)
|
||||
url: /v1/billing/module_config/fake1
|
||||
method: PUT
|
||||
status: 409
|
||||
response_strings:
|
||||
- "Module is not configurable"
|
||||
|
||||
- name: verify module exposes its custom API
|
||||
url: /v1/billing/module_config/fake2/test
|
||||
status: 200
|
||||
response_strings:
|
||||
- "OK"
|
138
cloudkitty/tests/gabbi/gabbits/v1_collector.yaml
Normal file
138
cloudkitty/tests/gabbi/gabbits/v1_collector.yaml
Normal file
@ -0,0 +1,138 @@
|
||||
fixtures:
|
||||
- ConfigFixture
|
||||
|
||||
tests:
|
||||
|
||||
# States
|
||||
- name: check collector is disabled by default
|
||||
url: /v1/collector/fake1/states
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.enabled: false
|
||||
$.name: "fake1"
|
||||
|
||||
- name: enable collector
|
||||
url: /v1/collector/fake1/states
|
||||
method: PUT
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
name: "fake1"
|
||||
enabled: true
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.enabled: true
|
||||
$.name: "fake1"
|
||||
|
||||
- name: check collector state isolation
|
||||
url: /v1/collector/fake2/states
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.enabled: false
|
||||
$.name: "fake2"
|
||||
|
||||
- name: disable collector
|
||||
url: /v1/collector/fake1/states
|
||||
method: PUT
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
name: "fake1"
|
||||
enabled: false
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.enabled: false
|
||||
$.name: "fake1"
|
||||
|
||||
# Mappings
|
||||
- name: get all mappings (empty)
|
||||
url: /v1/collector/mappings
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.mappings: []
|
||||
|
||||
- name: try to get an unknown mapping
|
||||
url: /v1/collector/mappings/notfound
|
||||
status: 400
|
||||
response_strings:
|
||||
- "No mapping for service: notfound"
|
||||
|
||||
- name: try to delete an unknown mapping
|
||||
url: /v1/collector/mappings/notfound
|
||||
method: DELETE
|
||||
status: 400
|
||||
response_strings:
|
||||
- "No mapping for service: notfound"
|
||||
|
||||
- name: create mapping
|
||||
url: /v1/collector/mappings/fake1/metric1
|
||||
method: POST
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.collector: "fake1"
|
||||
$.service: "metric1"
|
||||
|
||||
- name: get all mappings
|
||||
url: /v1/collector/mappings
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.mappings[0].collector: "fake1"
|
||||
$.mappings[0].service: "metric1"
|
||||
|
||||
- name: create second mapping
|
||||
url: /v1/collector/mappings/fake2/metric8
|
||||
method: POST
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.collector: "fake2"
|
||||
$.service: "metric8"
|
||||
|
||||
- name: get all mappings filtering on collector fake1
|
||||
url: /v1/collector/mappings?collector=fake1
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.mappings[0].collector: "fake1"
|
||||
$.mappings[0].service: "metric1"
|
||||
|
||||
- name: get all mappings filtering on collector fake2
|
||||
url: /v1/collector/mappings?collector=fake2
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.mappings[0].collector: "fake2"
|
||||
$.mappings[0].service: "metric8"
|
||||
|
||||
- name: get all mappings with no filtering
|
||||
url: /v1/collector/mappings
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.mappings.`len`: 2
|
||||
$.mappings[0].collector: "fake1"
|
||||
$.mappings[0].service: "metric1"
|
||||
$.mappings[1].collector: "fake2"
|
||||
$.mappings[1].service: "metric8"
|
||||
|
||||
- name: get a mapping filtering on service metric8
|
||||
url: /v1/collector/mappings/metric8
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.collector: "fake2"
|
||||
$.service: "metric8"
|
||||
|
||||
- name: delete a mapping
|
||||
url: /v1/collector/mappings/metric1
|
||||
method: DELETE
|
||||
status: 204
|
||||
|
||||
- name: check the mapping got deleted
|
||||
url: /v1/collector/mappings/metric1
|
||||
status: 400
|
||||
response_strings:
|
||||
- "No mapping for service: metric1"
|
158
cloudkitty/tests/gabbi/gabbits/v1_rating.yaml
Normal file
158
cloudkitty/tests/gabbi/gabbits/v1_rating.yaml
Normal file
@ -0,0 +1,158 @@
|
||||
fixtures:
|
||||
- ConfigFixture
|
||||
- RatingModulesFixture
|
||||
- QuoteFakeRPC
|
||||
|
||||
tests:
|
||||
- name: reload list of modules available
|
||||
url: /v1/rating/reload_modules
|
||||
status: 204
|
||||
|
||||
- name: list all modules available
|
||||
url: /v1/rating/modules
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.modules.`len`: 3
|
||||
$.modules[0].priority: 3
|
||||
$.modules[0].module_id: "fake1"
|
||||
$.modules[0].enabled: false
|
||||
$.modules[0].description: "fake rating module"
|
||||
$.modules[0].hot-config: false
|
||||
$.modules[1].priority: 1
|
||||
$.modules[1].module_id: "fake2"
|
||||
$.modules[1].enabled: false
|
||||
$.modules[1].description: "fake rating module"
|
||||
$.modules[1].hot-config: false
|
||||
$.modules[2].priority: 2
|
||||
$.modules[2].module_id: "fake3"
|
||||
$.modules[2].enabled: false
|
||||
$.modules[2].description: "fake rating module"
|
||||
$.modules[2].hot-config: false
|
||||
|
||||
- name: get information of one module
|
||||
url: /v1/rating/modules/fake2
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.priority: 1
|
||||
$.module_id: "fake2"
|
||||
$.enabled: false
|
||||
$.description: "fake rating module"
|
||||
$.hot-config: false
|
||||
|
||||
- name: get information of a unknown module
|
||||
url: /v1/rating/modules/fakb
|
||||
status: 400
|
||||
response_strings:
|
||||
- "Module not found."
|
||||
|
||||
- name: change priority of a module
|
||||
url: /v1/rating/modules/fake3
|
||||
method: PUT
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
module_id: "fake3"
|
||||
priority: 5
|
||||
status: 302
|
||||
response_headers:
|
||||
location: "$SCHEME://$NETLOC/v1/rating/modules/fake3"
|
||||
|
||||
- name: get information of the modified module (priority)
|
||||
url: $LOCATION
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.priority: 5
|
||||
$.module_id: "fake3"
|
||||
$.enabled: false
|
||||
$.description: "fake rating module"
|
||||
$.hot-config: false
|
||||
|
||||
- name: change enabled status of a module
|
||||
url: /v1/rating/modules/fake3
|
||||
method: PUT
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
module_id: "fake3"
|
||||
enabled: true
|
||||
status: 302
|
||||
response_headers:
|
||||
location: "$SCHEME://$NETLOC/v1/rating/modules/fake3"
|
||||
|
||||
- name: get information of the modified module (status)
|
||||
url: $LOCATION
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.priority: 5
|
||||
$.module_id: "fake3"
|
||||
$.enabled: true
|
||||
$.description: "fake rating module"
|
||||
$.hot-config: false
|
||||
|
||||
- name: change status and priority of a module
|
||||
url: /v1/rating/modules/fake3
|
||||
method: PUT
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
module_id: "fake3"
|
||||
priority: 3
|
||||
enabled: false
|
||||
status: 302
|
||||
response_headers:
|
||||
location: "$SCHEME://$NETLOC/v1/rating/modules/fake3"
|
||||
|
||||
- name: get information of the modified module (both)
|
||||
url: $LOCATION
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.priority: 3
|
||||
$.module_id: "fake3"
|
||||
$.enabled: false
|
||||
$.description: "fake rating module"
|
||||
$.hot-config: false
|
||||
|
||||
- name: get a quote for a resource description
|
||||
url: /v1/rating/quote
|
||||
method: POST
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
x-roles: admin
|
||||
data:
|
||||
resources:
|
||||
- service: "compute"
|
||||
volume: "1.0"
|
||||
desc:
|
||||
test: 1
|
||||
status: 200
|
||||
response_strings:
|
||||
- "1.0"
|
||||
|
||||
- name: module without custom API should use notconfigurable controller (GET)
|
||||
url: /v1/rating/module_config/fake1
|
||||
status: 409
|
||||
response_strings:
|
||||
- "Module is not configurable"
|
||||
|
||||
- name: module without custom API should use notconfigurable controller (POST)
|
||||
url: /v1/rating/module_config/fake1
|
||||
method: POST
|
||||
status: 409
|
||||
response_strings:
|
||||
- "Module is not configurable"
|
||||
|
||||
- name: module without custom API should use notconfigurable controller (PUT)
|
||||
url: /v1/rating/module_config/fake1
|
||||
method: PUT
|
||||
status: 409
|
||||
response_strings:
|
||||
- "Module is not configurable"
|
||||
|
||||
- name: verify module exposes its custom API
|
||||
url: /v1/rating/module_config/fake2/test
|
||||
status: 200
|
||||
response_strings:
|
||||
- "OK"
|
68
cloudkitty/tests/gabbi/gabbits/v1_report.yaml
Normal file
68
cloudkitty/tests/gabbi/gabbits/v1_report.yaml
Normal file
@ -0,0 +1,68 @@
|
||||
fixtures:
|
||||
- ConfigFixture
|
||||
- StorageDataFixture
|
||||
- NowStorageDataFixture
|
||||
|
||||
tests:
|
||||
- name: get period with two tenants
|
||||
url: /v1/report/tenants
|
||||
query_parameters:
|
||||
begin: "2015-01-01T00:00:00"
|
||||
end: "2015-01-04T00:00:00"
|
||||
status: 200
|
||||
response_strings:
|
||||
- "8f82cc70-e50c-466e-8624-24bdea811375"
|
||||
- "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
|
||||
|
||||
- name: by default give tenants for the current month
|
||||
url: /v1/report/tenants
|
||||
status: 200
|
||||
response_strings:
|
||||
- "3d9a1b33-482f-42fd-aef9-b575a3da9369"
|
||||
|
||||
- name: get period with no tenants
|
||||
url: /v1/report/tenants
|
||||
query_parameters:
|
||||
begin: "2015-02-01T00:00:00"
|
||||
end: "2015-02-02T00:00:00"
|
||||
status: 200
|
||||
response_strings:
|
||||
- "[]"
|
||||
|
||||
- name: get total for a period
|
||||
url: /v1/report/total
|
||||
query_parameters:
|
||||
begin: "2015-01-01T00:00:00"
|
||||
end: "2015-02-04T00:00:00"
|
||||
status: 200
|
||||
response_strings:
|
||||
- "110.971"
|
||||
|
||||
- name: get total for a period filtering on first tenant
|
||||
url: /v1/report/total
|
||||
query_parameters:
|
||||
begin: "2015-01-01T00:00:00"
|
||||
end: "2015-02-04T00:00:00"
|
||||
tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
|
||||
status: 200
|
||||
response_strings:
|
||||
- "73.535"
|
||||
|
||||
- name: get total for a period filtering on second tenant
|
||||
url: /v1/report/total
|
||||
query_parameters:
|
||||
begin: "2015-01-01T00:00:00"
|
||||
end: "2015-02-04T00:00:00"
|
||||
tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
|
||||
status: 200
|
||||
response_strings:
|
||||
- "37.436"
|
||||
|
||||
- name: get total for a period with no data
|
||||
url: /v1/report/total
|
||||
query_parameters:
|
||||
begin: "2015-02-01T00:00:00"
|
||||
end: "2015-02-02T00:00:00"
|
||||
status: 200
|
||||
response_strings:
|
||||
- "0"
|
135
cloudkitty/tests/gabbi/gabbits/v1_storage.yaml
Normal file
135
cloudkitty/tests/gabbi/gabbits/v1_storage.yaml
Normal file
@ -0,0 +1,135 @@
|
||||
fixtures:
|
||||
- ConfigFixture
|
||||
- StorageDataFixture
|
||||
|
||||
tests:
|
||||
- name: fetch period with no data
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
begin: "2015-01-01T00:00:00"
|
||||
end: "2015-01-04T00:00:00"
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.dataframes.`len`: 0
|
||||
|
||||
- name: fetch period with no data filtering on tenant_id
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
begin: "2015-01-01T00:00:00"
|
||||
end: "2015-01-04T00:00:00"
|
||||
tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.dataframes.`len`: 0
|
||||
|
||||
- name: check begin is mandatory for dataframes
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
end: "2015-01-04T00:00:00"
|
||||
status: 400
|
||||
response_strings:
|
||||
- "Missing argument: \\\"begin\\\""
|
||||
|
||||
- name: check end is mandatory for dataframes
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
begin: "2015-01-04T00:00:00"
|
||||
status: 400
|
||||
response_strings:
|
||||
- "Missing argument: \\\"end\\\""
|
||||
|
||||
- name: fetch data for the first tenant
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
begin: "2015-01-04T13:00:00"
|
||||
end: "2015-01-04T14:00:00"
|
||||
tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.dataframes.`len`: 1
|
||||
$.dataframes[0].tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
|
||||
$.dataframes[0].begin: "2015-01-04T13:00:00"
|
||||
$.dataframes[0].end: "2015-01-04T14:00:00"
|
||||
$.dataframes[0].resources.`len`: 1
|
||||
$.dataframes[0].resources[0].volume: "1"
|
||||
$.dataframes[0].resources[0].rating: "1.337"
|
||||
$.dataframes[0].resources[0].service: "compute"
|
||||
$.dataframes[0].resources[0].desc.dummy: true
|
||||
$.dataframes[0].resources[0].desc.fake_meta: 1.0
|
||||
|
||||
- name: fetch data for the second tenant
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
begin: "2015-01-04T13:00:00"
|
||||
end: "2015-01-04T14:00:00"
|
||||
tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.dataframes.`len`: 1
|
||||
$.dataframes[0].tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
|
||||
$.dataframes[0].begin: "2015-01-04T13:00:00"
|
||||
$.dataframes[0].end: "2015-01-04T14:00:00"
|
||||
$.dataframes[0].resources.`len`: 1
|
||||
$.dataframes[0].resources[0].volume: "1"
|
||||
$.dataframes[0].resources[0].rating: "1.337"
|
||||
$.dataframes[0].resources[0].service: "compute"
|
||||
$.dataframes[0].resources[0].desc.dummy: true
|
||||
$.dataframes[0].resources[0].desc.fake_meta: 1.0
|
||||
|
||||
- name: fetch data for multiple tenants
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
begin: "2015-01-04T13:00:00"
|
||||
end: "2015-01-04T14:00:00"
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.dataframes.`len`: 2
|
||||
$.dataframes[0].tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
|
||||
$.dataframes[0].begin: "2015-01-04T13:00:00"
|
||||
$.dataframes[0].end: "2015-01-04T14:00:00"
|
||||
$.dataframes[0].resources.`len`: 1
|
||||
$.dataframes[0].resources[0].volume: "1"
|
||||
$.dataframes[0].resources[0].rating: "1.337"
|
||||
$.dataframes[0].resources[0].service: "compute"
|
||||
$.dataframes[0].resources[0].desc.dummy: true
|
||||
$.dataframes[0].resources[0].desc.fake_meta: 1.0
|
||||
$.dataframes[1].tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
|
||||
$.dataframes[1].begin: "2015-01-04T13:00:00"
|
||||
$.dataframes[1].end: "2015-01-04T14:00:00"
|
||||
$.dataframes[1].resources.`len`: 1
|
||||
$.dataframes[1].resources[0].volume: "1"
|
||||
$.dataframes[1].resources[0].rating: "1.337"
|
||||
$.dataframes[1].resources[0].service: "compute"
|
||||
$.dataframes[1].resources[0].desc.dummy: true
|
||||
$.dataframes[1].resources[0].desc.fake_meta: 1.0
|
||||
|
||||
- name: fetch data filtering on service and tenant
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
begin: "2015-01-04T13:00:00"
|
||||
end: "2015-01-04T14:00:00"
|
||||
resource_type: "compute"
|
||||
tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.dataframes.`len`: 1
|
||||
$.dataframes[0].tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
|
||||
$.dataframes[0].begin: "2015-01-04T13:00:00"
|
||||
$.dataframes[0].end: "2015-01-04T14:00:00"
|
||||
$.dataframes[0].resources.`len`: 1
|
||||
$.dataframes[0].resources[0].volume: "1"
|
||||
$.dataframes[0].resources[0].rating: "1.337"
|
||||
$.dataframes[0].resources[0].service: "compute"
|
||||
$.dataframes[0].resources[0].desc.dummy: true
|
||||
$.dataframes[0].resources[0].desc.fake_meta: 1.0
|
||||
|
||||
- name: fetch data filtering on service with no data and tenant
|
||||
url: /v1/storage/dataframes
|
||||
query_parameters:
|
||||
begin: "2015-01-04T13:00:00"
|
||||
end: "2015-01-04T14:00:00"
|
||||
resource_type: "image"
|
||||
tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.dataframes.`len`: 0
|
33
cloudkitty/tests/gabbi/test_gabbi.py
Normal file
33
cloudkitty/tests/gabbi/test_gabbi.py
Normal file
@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
#
|
||||
# @author: Stéphane Albert
|
||||
#
|
||||
import os
|
||||
|
||||
from gabbi import driver
|
||||
|
||||
from cloudkitty.tests.gabbi import fixtures
|
||||
|
||||
TESTS_DIR = 'gabbits'
|
||||
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR)
|
||||
return driver.build_tests(test_dir,
|
||||
loader,
|
||||
host=None,
|
||||
intercept=fixtures.setup_app,
|
||||
fixture_module=fixtures)
|
@ -26,6 +26,7 @@ import datetime
|
||||
import sys
|
||||
|
||||
from oslo_utils import timeutils
|
||||
from six import moves
|
||||
from stevedore import extension
|
||||
|
||||
|
||||
@ -128,8 +129,20 @@ def get_next_month_timestamp(dt=None):
|
||||
|
||||
|
||||
def refresh_stevedore(namespace=None):
|
||||
# Trigger reload of entry points
|
||||
reload(sys.modules['pkg_resources'])
|
||||
"""Trigger reload of entry points.
|
||||
|
||||
Useful to have dynamic loading/unloading of stevedore modules.
|
||||
"""
|
||||
# NOTE(sheeprine): pkg_resources doesn't support reload on python3 due to
|
||||
# defining basestring which is still there on reload hence executing
|
||||
# python2 related code.
|
||||
try:
|
||||
del sys.modules['pkg_resources'].basestring
|
||||
except AttributeError:
|
||||
# python2, do nothing
|
||||
pass
|
||||
# Force working_set reload
|
||||
moves.reload_module(sys.modules['pkg_resources'])
|
||||
# Clear stevedore cache
|
||||
cache = extension.ExtensionManager.ENTRY_POINT_CACHE
|
||||
if namespace:
|
||||
|
@ -1,6 +1,9 @@
|
||||
[DEFAULT]
|
||||
output_file = etc/cloudkitty/cloudkitty.conf.sample
|
||||
namespace = cloudkitty.common.config
|
||||
namespace = oslo.messaging
|
||||
namespace = oslo.concurrency
|
||||
namespace = oslo.db
|
||||
namespace = oslo.log
|
||||
namespace = oslo.messaging
|
||||
namespace = oslo.policy
|
||||
namespace = keystonemiddleware.auth_token
|
||||
|
@ -4,6 +4,7 @@
|
||||
hacking<0.10,>=0.9.2
|
||||
coverage>=3.6
|
||||
discover
|
||||
gabbi>=0.12.0 # Apache-2.0
|
||||
testscenarios>=0.4
|
||||
testrepository>=0.0.18
|
||||
mock>=1.2
|
||||
|
Loading…
Reference in New Issue
Block a user