root / doc / manual / source / devguide.rst @ 2a36ca49
History | View | Annotate | Download (19.9 kB)
1 |
Development Guide |
---|---|
2 |
================= |
3 |
|
4 |
The development guide includes descriptions of the APIs and extention points |
5 |
offered by Aquarium. It also includes design and development setup information. |
6 |
|
7 |
Obtaining and Compiling |
8 |
----------------------- |
9 |
|
10 |
The source code to the Aquarium project can be obtained through Git, by |
11 |
cloning the repository `http://code.grnet.gr/git/aquarium`. This will give |
12 |
you read-only access to the repository. You need to contact the Aquarium |
13 |
team to obtain read-write access. |
14 |
|
15 |
To compile Aquarium, you need the following software in your path: |
16 |
|
17 |
- Java SDK 6. Java 7 has not been tested yet. |
18 |
- `Maven <http://maven.apache.org>`_, version > 3 |
19 |
|
20 |
After you install them, you can do the following: |
21 |
|
22 |
- ``mvn install``: To compile Aquarium and run the tests. This will also create |
23 |
the file ``target/aquarium-<ver>-jar-with-dependencies.jar`` which can be |
24 |
used to start Aquarium from the command line |
25 |
- ``mvn deploy``: To deploy the Aquarium libraries to the remote Maven repository |
26 |
- ``mvn release:prepare``, ``mvn release:perform``: To prepare a release, |
27 |
tag a new version and start the next development version |
28 |
- ``mvn scala:cctest``: To run the Scala compiler in continuous compilation |
29 |
mode |
30 |
|
31 |
Aquarium can also be build with the Simple Build Tool |
32 |
(`SBT <https://github.com/harrah/xsbt/wiki>`_). Just run ``sbt`` in the directory |
33 |
containing the Aquarium source code. |
34 |
|
35 |
Distributing |
36 |
^^^^^^^^^^^^ |
37 |
|
38 |
To prepare a binary distribution of Aquarium, use the ``make-dist.sh`` script. |
39 |
If the script is run without an argument, it will create a TAR archive from |
40 |
the latest tagged version of Aquarium. The script also accepts a Git commit-ish, |
41 |
in which case it will build a TAR archive out of the provided Git commit. |
42 |
|
43 |
Overall architecture |
44 |
-------------------- |
45 |
|
46 |
Aquarium's architectural design is mainly driven by two requirements: scaling |
47 |
and fault tolerance. Aquarium's functionality is based on event sourcing. |
48 |
`Event sourcing <http://en.wikipedia.org/wiki/Domain-driven_design>`_ |
49 |
assumes that all changes to application state are stored as a |
50 |
sequence of events, in an immutable log. With such a log at hand, a system can |
51 |
rebuild the current application state by replaying the events in order. The event |
52 |
sourcing design pattern has some very interesting properties, which made it |
53 |
particularity suitable for basing Aquarium on it: |
54 |
|
55 |
- Multiple models can be used in order to process the events, concurrently. This means that Aquarium can provide a limited data view to its REST API and a more detailed one to a helpdesk frontend. |
56 |
|
57 |
- It is possible to perform queries on past system states by stopping the event replay at a certain point of interest. This would prove very possible for a future debugging interface. |
58 |
|
59 |
- In a carefully implemented event sourcing system, application crashes are not destructive, as long as event replay is fast enough and no state is inserted to the application without being recorded to the event log first. |
60 |
|
61 |
- After event log replay, new events only cause updates in the system’s in-memory state, which can be done very fast. |
62 |
|
63 |
Components |
64 |
^^^^^^^^^^ |
65 |
|
66 |
.. image:: arch.png |
67 |
|
68 |
An overview of the Aquarium architecture is presented in the figure above. The |
69 |
system is modeled as a collection of logically and functionally isolated |
70 |
components, which communicate by message passing. Withing each component, a |
71 |
number of actors take care of concurrently processing incoming messages through |
72 |
a load balancer component which is the gateway to requests targeted to the |
73 |
component. Each component is also monitored by its own supervisor; should an |
74 |
actor fail, the supervisor will automatically restart it. The architecture |
75 |
allows certain application paths to fail individually while the system is still |
76 |
responsive, while also enabling future distribution of multiple components on |
77 |
clusters of machines. |
78 |
|
79 |
The system receives input mainly from two sources: a queue for resource and |
80 |
user events and a REST API for credits and resource state queries. The queue |
81 |
component reads messages from a configurable number of queues and persists them |
82 |
in the application’s immutable log store. Both input components then forward |
83 |
incoming messages to a network of dispatcher handlers which do not do any |
84 |
processing by themselves, but know where the user actors lay. Actual processing |
85 |
of billing events is done within the user actors. Finally, a separate network |
86 |
of actors take care of scheduling periodic tasks, such as refiling of user |
87 |
credits; it does so by issuing events to the appropriate queue. |
88 |
|
89 |
The accounting system |
90 |
---------------------- |
91 |
|
92 |
The accounting subsystem deals with charging users for services used and |
93 |
providing them with credits in order to be able to use the provided services. |
94 |
As with the rest of the Aquarium, the architecture is open-ended: the accounting |
95 |
system does not know in advance which services it supports or what resources |
96 |
are being offered. The configuration of the accounting system is done |
97 |
using a Domain Specific Language (DSL) described below. |
98 |
|
99 |
Data exchange with external systems is done through events, which are |
100 |
persisted to an *immutable log*. |
101 |
|
102 |
The accounting system is a generic event-processing engine that is configured by a |
103 |
DSL. The DSL is mostly based on the |
104 |
`YAML <http://en.wikipedia.org/wiki/Yaml>`_ |
105 |
format. The DSL supports limited algorithm definitions through integration of the Javascript language as defined below. |
106 |
|
107 |
Glossary of Entities |
108 |
^^^^^^^^^^^^^^^^^^^^ |
109 |
|
110 |
- *Credit*: A credit is the unit of currency used in Aquarium. It may or may not |
111 |
correspond to real money. |
112 |
- *Resource*: A resource represents an entity that can be charged for its usage. The |
113 |
currently charged resources are: Time of VM usage, bytes uploaded and downloaded and bytes used for storage |
114 |
- *Resource Event*: A resource event is generated from an external source and are permanently appended in an immutable event log. A raw event carries information about changes in an external system that could affect the status of a user's wallet (See more about `Resource Events`_). |
115 |
- *AccountingEntry*: An accounting entry is the result of processing a resource event and is what gets stored to the user's wallet. |
116 |
- *Price List*: A price list contains information of the cost of a resource. |
117 |
A pricelist is only applied within a specified time frame. |
118 |
- *Algorithm*: An algorithm specifies the way the charging calculation is done. It can be vary depending on resource usage, time of raw event or other information. |
119 |
- *Credit Plan*: Defines a periodic operation of refiling a user's wallet with a |
120 |
configurable amount of credits. |
121 |
- *Agreement*: An agreement associates pricelists with algorithms and credit |
122 |
plans. An agreement is assigned to one or more users/credit holders. |
123 |
- *Billing Period*: A billing period defines a recurring timeslot at the end |
124 |
of which the accumulated resource usage is accounted for and reset. |
125 |
|
126 |
Overall Schema |
127 |
^^^^^^^^^^^^^^ |
128 |
|
129 |
The Aquarium policy DSL allows the hierarchical definition of agreements, by |
130 |
means of compositing ingredients and specifying validity periods for individual |
131 |
components or for the policies themselves. The DSL also allows overriding |
132 |
between items of the same class (i.e. an algorithm definition can override |
133 |
certain fields of another algorithm definition, while both definitions can |
134 |
be referenced individually). |
135 |
|
136 |
The top-level schema for the DSL is as follows. |
137 |
|
138 |
.. code-block:: yaml |
139 |
|
140 |
aquariumpolicy: |
141 |
resources: |
142 |
- resource: |
143 |
... [see Resources] |
144 |
|
145 |
algorithms: |
146 |
- algorithm: |
147 |
... [see Algorithms] |
148 |
|
149 |
pricelists: |
150 |
- pricelist: |
151 |
... [see Pricelists] |
152 |
|
153 |
creditplans: |
154 |
- creditplan: |
155 |
... [see Creditplans] |
156 |
|
157 |
agreements: |
158 |
- agreement: |
159 |
... [see Agreements] |
160 |
|
161 |
|
162 |
Time frames |
163 |
^^^^^^^^^^^ |
164 |
|
165 |
Time frames allow the specification of applicability periods for algorithms, |
166 |
pricelists and agreements. A timeframe is by default continuous and has a |
167 |
starting point; if there is no ending point, the timeframe is considered open |
168 |
and its ending point is the time at the time of evaluation. |
169 |
|
170 |
A time frame definition can contain repeating time ranges that dissect it and |
171 |
consequently constrain the applicability of the time frame to the defined |
172 |
ranges only. A range always has a start and end point. A range is repeated |
173 |
within a timeframe, until the timeframe end point is reached. In case a |
174 |
repeating range ends later than the containing timeframe, the ending time is |
175 |
adjusted to match that of the timeframe. |
176 |
|
177 |
The definition of the starting and ending point of a time range is done in a |
178 |
syntax reminisent of the `cron <http://en.wikipedia.org/wiki/Cron>`_ format. |
179 |
|
180 |
.. code-block:: yaml |
181 |
|
182 |
effective: |
183 |
from: %d # Milliseconds since the epoch |
184 |
to: %d # [opt] Milliseconds since the epoch |
185 |
repeat: # [opt] Defines a repetion list |
186 |
- every: # [opt] A repetion entry |
187 |
start: "min hr dom moy dow" # 5-elem cron string |
188 |
end: "min hr dom moy dow" # 5-elem cron string |
189 |
|
190 |
|
191 |
The following declaration defines a timeframe starting at the designated |
192 |
timestamp and ending at the time of evaluation. |
193 |
|
194 |
.. code-block:: yaml |
195 |
|
196 |
effective: |
197 |
from: 1293703200 #(30/12/2010 10:00) |
198 |
|
199 |
The following declaration defines a timeframe of one year, within which the |
200 |
applicability of the specified policy, agreement or pricelist is constrained to |
201 |
time ranges from 12:00 Mon to 14:00 Fri (first ``every`` definition) |
202 |
and 15:00 Sat to 15:00 Sun. |
203 |
|
204 |
.. code-block:: yaml |
205 |
|
206 |
effective: |
207 |
from: 1293703200 #(30/12/2010 10:00) |
208 |
to: 1325239200 #(30/12/2011 10:00) |
209 |
repeat: |
210 |
- every: |
211 |
start: "00 12 * * Mon" |
212 |
end: "00 14 * * Fri" |
213 |
- every: |
214 |
start: "00 15 * * Sat" |
215 |
end: "00 15 * * Sun" |
216 |
|
217 |
Resources |
218 |
^^^^^^^^^ |
219 |
|
220 |
A resource represents an entity that can be charged for. Aquarium does not |
221 |
assume a fixed set of resource types and is extensible to any number of |
222 |
resources. A resource has a ``name`` and a ``unit``; both are free form |
223 |
strings. The resource name is used to uniquely identify the resource both inside |
224 |
Aquarium and among external systems. |
225 |
|
226 |
A resource definition also has a two fields that define how a resource is |
227 |
charged and whether a user can be assigned more instances of a resource. |
228 |
Specifically, the ``costpolicy`` field can have the following values: |
229 |
|
230 |
- `continuous:` For ``continuous`` resources, the charging algorithm calculates the |
231 |
total amount of resource usage over time, per billing period. Each new |
232 |
resource event modifies the resource usage counter and forces Aquarium |
233 |
to calculate a new cost for the previous amount of resource usage. A typical |
234 |
example of a continuous resource is disk space. |
235 |
- `onoff:` ``onoff`` resources are a category of continuous resources where the |
236 |
resource can only be in two states, on or off. In such cases, maintaining a usage |
237 |
counter is not necessary; the charging algorithm uses time as the unit of |
238 |
calculation. Virtual machine time is a typical example. |
239 |
- `discrete:` ``discrete`` resources are charged for instantly for the |
240 |
reported resource value. Examples are bandwidth and every resource whose usage |
241 |
is not a function of time (books, hits to an API etc). |
242 |
|
243 |
Regarding resource complexity, a resource can either be labeled complex |
244 |
or not. In the former case, a resource can have more than one instances per |
245 |
user, and resource usage is tracked individually per instance. The |
246 |
``instance-id`` field in the resource event message (See `Resource Events`_) |
247 |
helps Aquarium separate resource instances at charge time. |
248 |
|
249 |
The following resource definition defines the `bandwidthup` |
250 |
resource. |
251 |
|
252 |
.. code-block:: yaml |
253 |
|
254 |
resource: |
255 |
name: bandwidthup |
256 |
unit: MB/hr |
257 |
complex: false |
258 |
costpolicy: discrete |
259 |
|
260 |
Algorithms |
261 |
^^^^^^^^^^ |
262 |
|
263 |
An algorithm specifies the algorithm used to perform the cost calculation, by |
264 |
combining the reported resource usage with the applicable pricelist. As opposed |
265 |
to price lists, algorithms define behaviours, which have certain |
266 |
validity periods. |
267 |
|
268 |
.. code-block:: yaml |
269 |
|
270 |
algorithm: |
271 |
name: default |
272 |
bandwidthup: {price} times {volume} |
273 |
bandwidthdown: {price} times {volume} |
274 |
vmtime: {price} times {volume} |
275 |
diskspace: {price} times {volume} |
276 |
effective: |
277 |
[see Time frames] |
278 |
|
279 |
Price lists |
280 |
^^^^^^^^^^^ |
281 |
|
282 |
A price list defines the prices applicable for a resource within a validity |
283 |
period. Prices are attached to resource types and denote the policies that |
284 |
should be deducted from an entity's wallet in response to the entity's resource |
285 |
usage within a given charging period (currently, a month). The format is the |
286 |
following: |
287 |
|
288 |
.. code-block:: yaml |
289 |
|
290 |
pricelist: # Pricelist structure definition |
291 |
name: apricelist # Name for the price list, no spaces, must be unique |
292 |
[extends: anotherpl] # [Optional] Inheritance operation: all optional fields |
293 |
# are inherited from the named pricelist |
294 |
bandwidthup: # Price for used upstream bandwidth per MB |
295 |
bandwidthdown: # Price for used downstream bandwidth per MB |
296 |
vmtime: # Price for time |
297 |
diskspace: # Price for used diskspace, per MB |
298 |
effective: |
299 |
[see Timeframe format] |
300 |
|
301 |
Credit Plans |
302 |
^^^^^^^^^^^^ |
303 |
|
304 |
Credit plans define how user accounts are refilled with credits. Apart from |
305 |
the usual ``name`` and ``effective`` attributes, a credit plan has an ``at`` |
306 |
attribute (a five-field Cron string) which defines how offen the refilling |
307 |
operation will run and a ``credits`` attribute which defines the number of |
308 |
credits to add to the user's wallet. |
309 |
|
310 |
.. code-block:: yaml |
311 |
|
312 |
creditplan: |
313 |
name: default |
314 |
credits: 100 |
315 |
at: "00 00 1 * *" |
316 |
effective: |
317 |
from: 0 |
318 |
|
319 |
Agreements |
320 |
^^^^^^^^^^ |
321 |
|
322 |
An agreement is the result of combining an with algorithm with a pricelist |
323 |
and a creditplan. As the |
324 |
accounting DSL's main purpose is to facilitate the construction of agreements |
325 |
(which are then associated to users), the agreement is the centerpiece of |
326 |
the language. An agreement is defined in full using the following template: |
327 |
|
328 |
.. code-block:: yaml |
329 |
|
330 |
agreement: |
331 |
name: someuniqname # Unique name for |
332 |
extends: other # [opt] name of inhereted agreement |
333 |
pricelist: plname # Name of declared pricelist |
334 |
resourse: value # [opt] Overiding of price for resource |
335 |
algorithm: polname # Name of declared policy |
336 |
resourse: value # [opt] Overiding of algorithm for resourse |
337 |
|
338 |
An agreement definition can either reuse the pricelists, algorithms and creditplans |
339 |
defined above (referenced by name) or define the effective algorithm or pricelist |
340 |
in place. |
341 |
If a ``pricelist`` or ``algorithm`` name has not been defined explicitely (and |
342 |
therefore referenced by name), all prices or algorithms for the declared |
343 |
resources must be defined in either the ``agreement`` or one of its parents. |
344 |
|
345 |
As with all DSL resources, agreements can be overriden by other agreements. |
346 |
|
347 |
Examples |
348 |
^^^^^^^^^ |
349 |
.. toctree:: |
350 |
|
351 |
unicase |
352 |
|
353 |
|
354 |
Events |
355 |
------ |
356 |
|
357 |
Aquarium communicates with external systems through events published on an `AMQP <http://en.wikipedia.org/wiki/AMQP>`_ queue. Aquarium only understands events in the |
358 |
`JSON <http://www.json.org/>`_ format. |
359 |
|
360 |
Aquarium events share a common base format consisting of the following fields: |
361 |
|
362 |
.. code-block:: javascript |
363 |
|
364 |
{ |
365 |
id: "SHA-1", |
366 |
occurredMillis: 12345, |
367 |
receivedMillis: 12346 |
368 |
} |
369 |
|
370 |
- *id:* [``string``] A per message unique string. Should be able to identify messages of the same type uniquely across Aquarium clients. Preferably a SHA-1. |
371 |
- *occurredMillis:* [``long``] The timestamp at the event creation time. In milliseconds since the epoch. |
372 |
- *receivedMillis:* [``long``] For Aquarium internal use. Clients should not set a value. If a value is set, it will be overwritten upon receipt. |
373 |
|
374 |
In the following sections, we describe the exact format of each one of the concrete messages that Aquarium can process. |
375 |
|
376 |
Resource Events |
377 |
^^^^^^^^^^^^^^^ |
378 |
|
379 |
A resource event is sent by Aquarium clients to signify a change in a resource's |
380 |
state. This change is processed by Aquarium's accounting system according to |
381 |
the provisions of the configured policy in order to create entries to the user's |
382 |
wallet. |
383 |
|
384 |
.. code-block:: javascript |
385 |
|
386 |
{ |
387 |
id: "<SHA-1>", |
388 |
occurredMillis: 1321020852, |
389 |
receivedMillis: 1321020852, |
390 |
clientID: "platform-wide-unique-ID", |
391 |
userID: "administrator@admin.grnet.gr", |
392 |
resource: "vmtime", |
393 |
instanceId: "vmtime-01.02.123X.Z", |
394 |
eventVersion: "1.0", |
395 |
value: 0.3, |
396 |
details: { |
397 |
keyA: "value1", |
398 |
keyB: "value2", |
399 |
} |
400 |
} |
401 |
|
402 |
The meaning of the fields is as follows: |
403 |
|
404 |
- *id:* As above. |
405 |
- *occurredMillis:* As above. |
406 |
- *receivedMillis:* As above. |
407 |
- *clientID:* ``string`` A unique name for each message producer. |
408 |
- *userID:* ``string`` The ID of the user that will be charged for the resource usage details reported in the resource event. |
409 |
- *resource* ``string`` The name of the resource as declared in the Aquarium DSL. See `Resources`_ for more. |
410 |
- *instanceId* ``string`` If the resource is complex, then this field is set to a unique identifier for the specific instance of the resource. In case of a non-complex resource, Aquarium does not examine this value. |
411 |
- *eventVersion* ``string`` The event version. Currently fixed to "1". |
412 |
- *value*: ``double`` The value of resource usage. Depends on the cost policy defined for the resource as follows: |
413 |
+ For ``continuous`` resources, the value indicates the amount of resource usage since the last resource event for the specific resource. |
414 |
+ For ``onoff`` resources, it is set to 1 when the resource is actively used and to 0 when the resource usage has stopped. |
415 |
+ For ``discrete`` resources, the field indicates the amount of resource usage at the time of the event. |
416 |
- *details*: ``map[string, string]`` A map/dictionary indicating extra metadata for this resource event. Aquarium does not process this metadata. The field must always be present, even if it is empty. |
417 |
|
418 |
User Event |
419 |
^^^^^^^^^^ |
420 |
|
421 |
A user event is sent by an external identity provider system to signify |
422 |
changes in user states. The message format is the following: |
423 |
|
424 |
.. code-block:: javascript |
425 |
|
426 |
{ |
427 |
id: "<SHA-1>", |
428 |
occurredMillis: 1321020852, |
429 |
receivedMillis: 1321020852, |
430 |
clientID: "platform-wide-unique-ID", |
431 |
userID: "administrator@admin.grnet.gr", |
432 |
isActive: "true", |
433 |
role: "STUDENT", |
434 |
eventVersion: "1", |
435 |
eventType: "ACTIVE", |
436 |
details: { } |
437 |
} |
438 |
|
439 |
The meaning of the fields is as follows: |
440 |
|
441 |
- *id:* As above. |
442 |
- *occurredMillis:* As above. |
443 |
- *receivedMillis:* As above. |
444 |
- *clientID:* ``string`` A unique name for each message producer. |
445 |
- *userID:* ``string`` The ID of the user whom this event concerns |
446 |
- *isActive* ``boolean`` Whether the user is active or not (allowed values are |
447 |
``true`` or ``false``) |
448 |
- *eventVersion* ``string`` The event version. Currently fixed to "1". |
449 |
- *role* ``string`` The role of the user. If different than the role currently |
450 |
stored, the role will be changed. |
451 |
- *eventType* ``string (ACTIVE/SUSPENDED)`` The requested change to the user |
452 |
profile. |
453 |
- *details*: ``map[string, string]`` A map/dictionary indicating extra metadata for this resource event. Aquarium does not process this metadata. The field must always be present, even if it is empty. |
454 |
|
455 |
|
456 |
The charging algorithm |
457 |
---------------------- |
458 |
|
459 |
|
460 |
The Aquarium REST API |
461 |
--------------------- |
462 |
|
463 |
The Aquarium REST API is used to query a |
464 |
|
465 |
As Aquarium is a backend system, clients are trusted and therefore no |
466 |
authentication is required for accessing Aquarium's API. |
467 |
|
468 |
Get User Balance |
469 |
^^^^^^^^^^^^^^^^ |
470 |
|
471 |
**GET** /user/*id*/balance |
472 |
|
473 |
**Normal Response Code**: 200 |
474 |
|
475 |
**Error Response Codes**: itemNotFound (404), timeout (500) |
476 |
|
477 |
The operation returns the current balance for a user. |
478 |
|
479 |
**Example get balance response** |
480 |
|
481 |
.. code-block:: javascript |
482 |
|
483 |
{ |
484 |
userId: "1234" |
485 |
balance: "321,32" |
486 |
} |
487 |
|
488 |
|
489 |
Document Revisions |
490 |
------------------ |
491 |
|
492 |
================== ================================ |
493 |
Revision Description |
494 |
================== ================================ |
495 |
0.1 (Nov 2, 2011) Initial release. Credit and debit policy descriptions |
496 |
0.2 (Feb 23, 2012) Update definitions, remove company use case |
497 |
0.3 (Feb 28, 2012) Event and resource descriptions |
498 |
================== ================================ |
499 |
|
500 |
|