Improved architecture section in documentation
Change-Id: I71527cf3831a02f2ce7abbc38c53b5c5d69f99da
This commit is contained in:
@@ -5,20 +5,23 @@ CloudKitty's Architecture
|
|||||||
CloudKitty can be cut in four big parts:
|
CloudKitty can be cut in four big parts:
|
||||||
|
|
||||||
* API
|
* API
|
||||||
* collector
|
* Collector
|
||||||
* billing processor
|
* Rating processing
|
||||||
* writer pipeline
|
* Writing pipeline
|
||||||
|
|
||||||
|
|
||||||
|
.. graphviz:: graph/arch.dot
|
||||||
|
|
||||||
|
|
||||||
Module loading and extensions
|
Module loading and extensions
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
Nearly every part of CloudKitty makes use of stevedore to load extensions
|
Nearly every part of CloudKitty make use of stevedore to load extensions
|
||||||
dynamically.
|
dynamically.
|
||||||
|
|
||||||
Every billing module is loaded at runtime and can be enabled/disabled directly
|
Every rating module is loaded at runtime and can be enabled/disabled directly
|
||||||
via CloudKitty's API. The billing module is responsible of its own API to ease
|
via CloudKitty's API. The module is responsible of its own API to ease the
|
||||||
the management of its configuration.
|
management of its configuration.
|
||||||
|
|
||||||
Collectors and writers are loaded with stevedore but configured in CloudKitty's
|
Collectors and writers are loaded with stevedore but configured in CloudKitty's
|
||||||
configuration file.
|
configuration file.
|
||||||
@@ -27,21 +30,115 @@ configuration file.
|
|||||||
Collector
|
Collector
|
||||||
=========
|
=========
|
||||||
|
|
||||||
This part is responsible of the information gathering. It consists of a python
|
**Loaded with stevedore**
|
||||||
module that load data from a backend and return them in a format that
|
|
||||||
CloudKitty can handle.
|
|
||||||
|
|
||||||
Processor
|
The name of the collector to use is specified in the configuration, only one
|
||||||
=========
|
collector can be loaded at once.
|
||||||
|
This part is responsible of information gathering. It consists of a python
|
||||||
|
class that load data from a backend and return them in a format that CloudKitty
|
||||||
|
can handle.
|
||||||
|
|
||||||
This is where every pricing calculations is done. The data gathered by
|
The data format of CloudKitty is the following:
|
||||||
the collector is pushed in a pipeline of billing processors. Every
|
|
||||||
processor does its calculations and updates the data.
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"myservice": [
|
||||||
|
{
|
||||||
|
"billing": {
|
||||||
|
"price": 0.1
|
||||||
|
},
|
||||||
|
"desc": {
|
||||||
|
"sugar": "25",
|
||||||
|
"fiber": "10",
|
||||||
|
"name": "apples",
|
||||||
|
},
|
||||||
|
"vol": {
|
||||||
|
"qty": 1,
|
||||||
|
"unit": "banana"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Example code of a basic collector:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class MyCollector(BaseCollector):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(MyCollector, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def get_mydata(self, start, end=None, project_id=None, q_filter=None):
|
||||||
|
# Do stuff
|
||||||
|
return ck_data
|
||||||
|
|
||||||
|
|
||||||
|
You'll now be able to add the gathering of mydata in CloudKitty by modifying
|
||||||
|
the configuration and specifying the new service in collect/services.
|
||||||
|
|
||||||
|
Rating
|
||||||
|
======
|
||||||
|
|
||||||
|
**Loaded with stevedore**
|
||||||
|
|
||||||
|
This is where every rating calculations is done. The data gathered by the
|
||||||
|
collector is pushed in a pipeline of billing processors. Every processor does
|
||||||
|
its calculations and updates the data.
|
||||||
|
|
||||||
|
Example of minimal rating module (taken from the Noop module):
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class NoopController(billing.BillingController):
|
||||||
|
|
||||||
|
module_name = 'noop'
|
||||||
|
|
||||||
|
def get_module_info(self):
|
||||||
|
module = Noop()
|
||||||
|
infos = {
|
||||||
|
'name': self.module_name,
|
||||||
|
'description': 'Dummy test module.',
|
||||||
|
'enabled': module.enabled,
|
||||||
|
'hot_config': False,
|
||||||
|
}
|
||||||
|
return infos
|
||||||
|
|
||||||
|
|
||||||
|
class Noop(billing.BillingProcessorBase):
|
||||||
|
|
||||||
|
controller = NoopController
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
"""Check if the module is enabled
|
||||||
|
|
||||||
|
:returns: bool if module is enabled
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def reload_config(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def process(self, data):
|
||||||
|
for cur_data in data:
|
||||||
|
cur_usage = cur_data['usage']
|
||||||
|
for service in cur_usage:
|
||||||
|
for entry in cur_usage[service]:
|
||||||
|
if 'billing' not in entry:
|
||||||
|
entry['billing'] = {'price': 0}
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
Writer
|
Writer
|
||||||
======
|
======
|
||||||
|
|
||||||
In the same way as the processor pipeline, the writing is handled with a
|
**Loaded with stevedore**
|
||||||
pipeline. The data is pushed to every writer in the pipeline which is
|
|
||||||
responsible of the writing.
|
In the same way as the rating pipeline, the writing is handled with a pipeline.
|
||||||
|
The data is pushed to write orchestrator that will store the data in a
|
||||||
|
transient DB (in case of output file invalidation). And then to every writer in
|
||||||
|
the pipeline which is responsible of the writing.
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ sys.path.insert(0, os.path.abspath('../..'))
|
|||||||
# ones.
|
# ones.
|
||||||
extensions = [
|
extensions = [
|
||||||
'sphinx.ext.autodoc',
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.graphviz',
|
||||||
'sphinx.ext.intersphinx',
|
'sphinx.ext.intersphinx',
|
||||||
'sphinx.ext.viewcode',
|
'sphinx.ext.viewcode',
|
||||||
'wsmeext.sphinxext',
|
'wsmeext.sphinxext',
|
||||||
|
|||||||
59
doc/source/graph/arch.dot
Normal file
59
doc/source/graph/arch.dot
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
digraph "CloudKitty's Architecture" {
|
||||||
|
|
||||||
|
// Graph parameters
|
||||||
|
label="CloudKitty's Internal Architecture";
|
||||||
|
node [shape=box];
|
||||||
|
compound=true;
|
||||||
|
|
||||||
|
// API
|
||||||
|
api [label="API"];
|
||||||
|
|
||||||
|
// Orchestrator
|
||||||
|
subgraph cluster_3 {
|
||||||
|
label="Orchestrator";
|
||||||
|
node[shape=none, width=1.3, height=0, label=""];
|
||||||
|
{rank=same; o1 -> o2 -> o3 [style=invis];}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collector
|
||||||
|
ceilometer [label="Ceilometer"];
|
||||||
|
vendor [label="Vendor specific", style=dotted];
|
||||||
|
subgraph cluster_0 {
|
||||||
|
label="Collector";
|
||||||
|
style=dashed;
|
||||||
|
ceilometer -> vendor [style=invis];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rating
|
||||||
|
hashmap [label="HashMap module"];
|
||||||
|
r_others [label="Other modules...", style=dotted];
|
||||||
|
subgraph cluster_1 {
|
||||||
|
label="Rating engines";
|
||||||
|
style=dashed;
|
||||||
|
hashmap -> r_others [style=invis];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write Orchestrator
|
||||||
|
w_orchestrator [label="Write Orchestrator"];
|
||||||
|
tdb [label="Transient DB"];
|
||||||
|
|
||||||
|
//Writers
|
||||||
|
osrf [label="OpenStack\nReference Format\n(json)"];
|
||||||
|
w_others [label="Other modules...", style=dotted];
|
||||||
|
subgraph cluster_2 {
|
||||||
|
label="Writers";
|
||||||
|
style=dashed;
|
||||||
|
osrf -> w_others [style=invis];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
api -> hashmap;
|
||||||
|
api -> r_others;
|
||||||
|
o1 -> ceilometer [dir=both, ltail=cluster_3, lhead=cluster_0];
|
||||||
|
o2 -> hashmap [dir=both, ltail=cluster_3, lhead=cluster_1];
|
||||||
|
o3 -> w_orchestrator [ltail=cluster_3];
|
||||||
|
w_orchestrator -> osrf [constraint=false];
|
||||||
|
w_orchestrator -> w_others [style=dotted, constraint=false];
|
||||||
|
w_orchestrator -> tdb;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user