Add rating modules GET endpoints to v2 API

This introduces GET methods for rating modules in the v2 API. Work items:

* Implement the "/v1/rating/modules" endpoints in the v2 API, including
  unit tests and documentation

Story: 2006572
Task: 36677

Co-Authored-By: Rafael Weingärtner <rafael@apache.org>

Change-Id: I0a2f24051d268a396955c8df7e3e5615546f6293
This commit is contained in:
Quentin Anglade
2019-09-25 14:07:36 +02:00
committed by Rafael Weingärtner
parent 34d121bf18
commit 9f9f4f1233
15 changed files with 380 additions and 13 deletions

View File

@@ -34,7 +34,8 @@ API_MODULES = [
'cloudkitty.api.v2.scope',
'cloudkitty.api.v2.dataframes',
'cloudkitty.api.v2.summary',
'cloudkitty.api.v2.task'
'cloudkitty.api.v2.task',
'cloudkitty.api.v2.rating',
]

View File

@@ -0,0 +1,31 @@
# Copyright 2019 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 cloudkitty.api.v2 import utils as api_utils
def init(app):
api_utils.do_init(app, 'rating', [
{
'module': __name__ + '.' + 'modules',
'resource_class': 'RatingModule',
'url': '/modules/<string:module_id>',
},
{
'module': __name__ + '.' + 'modules',
'resource_class': 'RatingModuleList',
'url': '/modules',
},
])
return app

View File

@@ -0,0 +1,102 @@
# Copyright 2019 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 flask
from oslo_concurrency import lockutils
from stevedore import extension
import voluptuous
from werkzeug import exceptions as http_exceptions
from cloudkitty.api.v2 import base
from cloudkitty.api.v2 import utils as api_utils
from cloudkitty.common import policy
from cloudkitty import utils as ck_utils
from cloudkitty.utils import validation as vutils
PROCESSORS_NAMESPACE = 'cloudkitty.rating.processors'
MODULE_SCHEMA = {
voluptuous.Required(
'description',
default=None,
): vutils.get_string_type(),
voluptuous.Required(
'module_id',
default=None,
): vutils.get_string_type(),
voluptuous.Required(
'enabled',
default=None,
): voluptuous.Boolean(),
voluptuous.Required(
'hot_config',
default=None,
): voluptuous.Boolean(),
voluptuous.Required(
'priority',
default=None,
): voluptuous.All(int, min=1),
}
class BaseRatingModule(base.BaseResource):
@classmethod
def reload(cls):
super(BaseRatingModule, cls).reload()
with lockutils.lock('rating-modules'):
ck_utils.refresh_stevedore(PROCESSORS_NAMESPACE)
cls.rating_modules = extension.ExtensionManager(
PROCESSORS_NAMESPACE, invoke_on_load=True)
class RatingModule(BaseRatingModule):
@api_utils.add_output_schema(MODULE_SCHEMA)
def get(self, module_id):
policy.authorize(flask.request.context, 'v2_rating:get_module', {})
try:
module = self.rating_modules[module_id]
except KeyError:
raise http_exceptions.NotFound(
"Module '{}' not found".format(module_id))
infos = module.obj.module_info.copy()
return {
'module_id': module_id,
'description': infos['description'],
'enabled': infos['enabled'],
'hot_config': infos['hot_config'],
'priority': infos['priority'],
}
class RatingModuleList(BaseRatingModule):
@api_utils.add_output_schema({
'modules': [MODULE_SCHEMA],
})
def get(self):
modules = []
for module in self.rating_modules:
infos = module.obj.module_info.copy()
modules.append({
'module_id': infos['name'],
'description': infos['description'],
'enabled': infos['enabled'],
'hot_config': infos['hot_config'],
'priority': infos['priority'],
})
return {'modules': modules}

View File

@@ -22,6 +22,7 @@ from cloudkitty.common.policies.v1 import rating as v1_rating
from cloudkitty.common.policies.v1 import report as v1_report
from cloudkitty.common.policies.v1 import storage as v1_storage
from cloudkitty.common.policies.v2 import dataframes as v2_dataframes
from cloudkitty.common.policies.v2 import rating as v2_rating
from cloudkitty.common.policies.v2 import scope as v2_scope
from cloudkitty.common.policies.v2 import summary as v2_summary
from cloudkitty.common.policies.v2 import tasks as v2_tasks
@@ -36,6 +37,7 @@ def list_rules():
v1_report.list_rules(),
v1_storage.list_rules(),
v2_dataframes.list_rules(),
v2_rating.list_rules(),
v2_scope.list_rules(),
v2_summary.list_rules(),
v2_tasks.list_rules()

View File

@@ -0,0 +1,37 @@
# Copyright 2019 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_policy import policy
from cloudkitty.common.policies import base
rating_policies = [
policy.DocumentedRuleDefault(
name='v2_rating:list_modules',
check_str=base.ROLE_ADMIN,
description='Returns the list of loaded modules in Cloudkitty.',
operations=[{'path': '/v2/rating/modules',
'method': 'GET'}]),
policy.DocumentedRuleDefault(
name='v2_rating:get_module',
check_str=base.ROLE_ADMIN,
description='Get specified module.',
operations=[{'path': '/v2/rating/modules/{module_id}',
'method': 'GET'}]),
]
def list_rules():
return rating_policies

View File

@@ -0,0 +1,42 @@
fixtures:
- ConfigFixtureStorageV2
- RatingModulesFixture
- QuoteFakeRPC
tests:
- name: list all modules available
url: /v2/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: /v2/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: /v2/rating/modules/fakb
status: 404
response_json_paths:
$.message: "Module 'fakb' not found"

View File

@@ -1215,18 +1215,6 @@
# heartbeat. (integer value)
#heartbeat_rate = 2
# DEPRECATED: (DEPRECATED) Enable/Disable the RabbitMQ mandatory flag
# for direct send. The direct send is used as reply, so the
# MessageUndeliverable exception is raised in case the client queue
# does not exist.MessageUndeliverable exception will be used to loop
# for a timeout to lets a chance to sender to recover.This flag is
# deprecated and it will not be possible to deactivate this
# functionality anymore (boolean value)
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Mandatory flag no longer deactivable.
#direct_mandatory_flag = true
# Enable x-cancel-on-ha-failover flag so that rabbitmq server will
# cancel and notify consumerswhen queue is down (boolean value)
#enable_cancel_on_failover = false

View File

@@ -89,6 +89,14 @@
# GET /v2/dataframes
#"dataframes:get": "rule:admin_or_owner"
# Returns the list of loaded modules in Cloudkitty.
# GET /v2/rating/modules
#"v2_rating:list_modules": "role:admin"
# Get specified module.
# GET /v2/rating/modules/{module_id}
#"v2_rating:get_module": "role:admin"
# Get the state of one or several scopes
# GET /v2/scope
#"scope:get_state": "role:admin"

View File

@@ -0,0 +1,7 @@
{
"module_id": "sample_id",
"description": "Sample extension",
"enabled": true,
"hot-config": false,
"priority": 2
}

View File

@@ -0,0 +1,18 @@
{
"modules": [
{
"module_id": "noop"
"description": "Dummy test module.",
"enabled": true,
"hot-config": false,
"priority": 1
},
{
"module_id": "hashmap",
"description": "HashMap rating module.",
"enabled": false,
"hot-config": true,
"priority": 2
}
]
}

View File

@@ -4,3 +4,4 @@
.. include:: scope/scope.inc
.. include:: summary/summary.inc
.. include:: task/reprocessing.inc
.. include:: rating/modules.inc

View File

@@ -0,0 +1 @@
../http_status.yml

View File

@@ -0,0 +1,76 @@
=======================
Rating modules endpoint
=======================
Get the list of modules
=======================
Returns the list of all rating modules loaded. This method does not require any
parameter.
.. rest_method:: GET /v2/rating/modules
Status codes
------------
.. rest_status_code:: success http_status.yml
- 200
.. rest_status_code:: error http_status.yml
- 400
- 401
- 403
Response
--------
.. rest_parameters:: rating/modules_parameters.yml
- modules: modules_list
Response Example
----------------
.. literalinclude:: ./api_samples/rating/modules_list_get.json
:language: javascript
Get one module
==============
Returns the details of one specific module. This method does not require any
parameter.
.. rest_method:: GET /v2/rating/modules/<module_id>
Status codes
------------
.. rest_status_code:: success http_status.yml
- 200
.. rest_status_code:: error http_status.yml
- 400
- 401
- 403
- 404
Response
--------
.. rest_parameters:: rating/modules_parameters.yml
- module_id: module_id
- description: description
- enabled: enabled
- hot_config: hot_config
- priority: priority
Response Example
----------------
.. literalinclude:: ./api_samples/rating/module_get.json
:language: javascript

View File

@@ -0,0 +1,49 @@
description:
in: body
description: |
A quick description of the module
type: string
required: true
enabled: &enabled
in: body
description: |
Boolean representing if the module is enabled
type: bool
required: true
enabled_opt:
<<: *enabled
required: false
hot_config:
in: body
description: |
Boolean representing if the module supports hot-config
type: bool
required: true
module_id:
in: body
description: |
The id of the module
type: string
required: true
modules_list:
in: body
description: |
List of modules.
type: list
required: true
priority: &priority
in: body
description: |
Priority of the module, relative to other modules
type: int
required: true
priority_opt:
<<: *priority
required: false

View File

@@ -0,0 +1,4 @@
---
features:
- |
Add rating modules GET endpoints to v2 API.