Compare commits

..

246 Commits

Author SHA1 Message Date
Vincent Françoise
de7b0129a1 Doc on how to set up a thirdparty project
This documentation is a pre-requisite to all plugin documentation
as it guides you through the creation of a project from scratch
instead of simply forcusing on the implementation of the plugin
itself.

Change-Id: Id2e09b3667390ee6c4be42454c41f9d266fdfac2
Related-Bug: #1534639
Related-Bug: #1533739
Related-Bug: #1533740
2016-03-04 12:07:41 +01:00
Jenkins
323fd01a85 Merge "Updated Strategy plugin doc" 2016-03-04 08:27:36 +00:00
Jenkins
c0133e6585 Merge "Remove tests omission from coverage target in tox.ini" 2016-03-04 08:25:30 +00:00
Gábor Antal
63fffeacd8 Remove tests omission from coverage target in tox.ini
In coverage target in tox.ini, there is the following argument:
  --omit="watcher/tests/*"

However, in .coveragerc, the tests are also omitted,
according to line 4 in .coveragerc:
  omit = watcher/tests/*

So the watcher/tests/* directory is omitted twice. As it can be
seen in other modules (e.g.: swift, nova) omitting only in
.coveragerc should be enough.

Change-Id: I72951196a346fb73a90c998138fc91dd171432cd
Closes-Bug: #1552617
2016-03-04 07:36:26 +00:00
Jenkins
bc791f0e75 Merge "add Goal into RESTful Web API (v1) documentation" 2016-03-03 16:45:20 +00:00
David TARDIVEL
6ed417e6a7 add Goal into RESTful Web API (v1) documentation
Change-Id: I94ba5debf6f0ff9562dc95cbc5c646f33881d9e2
Closes-Bug: #1546630
2016-03-03 16:02:56 +00:00
Vincent Françoise
4afefa3dfb Updated Strategy plugin doc
As we modified the way a strategy gets implemented in
blueprint watcher-add-actions-via-conf, this patchset updates the
documentation regarding the implementation of a strategy plugin.

Change-Id: I517455bc34623feff704956ce30ed545a0e1014b
Closes-Bug: #1533740
2016-03-03 16:18:33 +01:00
Jenkins
9fadfbe40a Merge "Doc on how to implement a custom Watcher action" 2016-03-03 15:12:21 +00:00
Jenkins
f278874a93 Merge "Add Watcher dashboard to the list of projects" 2016-03-03 14:34:20 +00:00
Jenkins
1c5b247300 Merge "Doc on how to implement a custom Watcher planner" 2016-03-03 14:19:58 +00:00
Jenkins
547bf5f87e Merge "Improve DevStack documentation for beginners" 2016-03-03 14:19:21 +00:00
Vincent Françoise
96c0ac0ca8 Doc on how to implement a custom Watcher planner
This documentation describes step-by-step the process for implementing
a new planner in Watcher.

Change-Id: I8addba53de69be93730924a58107687020c19c74
Closes-Bug: #1533739
2016-03-03 14:09:41 +00:00
Antoine Cabot
a80fd2a51e Add Watcher dashboard to the list of projects
We provide a list of Watcher related projects
on the generated doc. This commit add links to
the Watcher dashboard project in various locations.

Change-Id: I1993cd5a11d3fcc8ca2c40b1779700359adab4ea
2016-03-03 13:47:24 +00:00
Vincent Françoise
58ea85c852 Doc on how to implement a custom Watcher action
This documentation describes step-by-step the process for implementing
a new action in Watcher.

Change-Id: I978b81cdf9ac6dcf43eb3ecbb79ab64ae4fd6f72
Closes-Bug: #1534639
2016-03-02 14:46:37 +01:00
Jenkins
78f122f241 Merge "RST directive to discover and generate drivers doc" 2016-03-01 23:09:18 +00:00
Jenkins
43eb997edb Merge "Updated Watcher doc to mention Tempest tests" 2016-03-01 22:58:24 +00:00
Taylor Peoples
b5bccba169 Improve DevStack documentation for beginners
The DevStack documentation should provide basic steps for setting up
Watcher with DevStack assuming the reader has no previous knowledge of
DevStack.

Change-Id: I830b1d9accb0e65bba73944697cba9c53ac3263e
Closes-Bug: #1538291
2016-03-01 17:25:24 +01:00
Jenkins
e058437ae0 Merge "Replace "Triggered" state by "Pending" state" 2016-03-01 15:48:33 +00:00
Jenkins
1acacaa812 Merge "Added support for live migration on non-shared storage" 2016-03-01 09:31:50 +00:00
Daniel Pawlik
5bb1b6cbf0 Added support for live migration on non-shared storage
Watcher applier should be able to live migrate instances on any storage
type. To do this watcher will catch error 400 returned from nova if we
try to live migrate instance which is not on shared storage and live
migrate instance using block_migrate.

Added unit tests, changed action in watcher applier.

Closes-bug: #1549307

Change-Id: I97e583c9b4a0bb9daa1d39e6d652d6474a5aaeb1
2016-03-01 08:35:14 +00:00
Vincent Françoise
de058d7ed1 Updated Watcher doc to mention Tempest tests
The Watcher Tempest tests are only mentioned inside a README.rst.
They are now part of the main documentation.

Change-Id: Ieca85dc7f7307b45e4b99af4a4600a8c2d2b59d7
Closes-Bug: #1536993
2016-02-29 17:42:21 +01:00
Vincent Françoise
98a65efb16 RST directive to discover and generate drivers doc
This patchset introduces a new custom directive called 'drivers-doc'
which loads all available drivers under a given namespace and import
their respective docstring into the .rst document.

This patchset also contains some modification/addition to the
docstring of these drivers to make the final document complete.

Change-Id: Ib3df59fa45cea9d11d20fb73a5f0f1d564135bca
Closes-Bug: #1536218
Closes-Bug: #1536735
2016-02-29 16:34:44 +01:00
Tin Lam
338539ec53 Rename 'TRIGGERED' state as 'PENDING'
"TRIGGERED" is not the correct word to use to describe the action
state, we should use "PENDING" instead.

Change-Id: If24979cdb916523861324f7bcc024e2f1fc28b05
Closes-Bug: #1548377
2016-02-26 23:07:59 -06:00
Jenkins
02f0f8e70a Merge "Add missing requirements" 2016-02-26 17:06:55 +00:00
Jenkins
7f1bd20a09 Merge "Add start directory for oslo_debug_helper" 2016-02-26 17:04:03 +00:00
Jenkins
d3d2a5ef8c Merge "Updated from global requirements" 2016-02-26 17:03:57 +00:00
Jenkins
3a6ae820c0 Merge "Fixed type in get_audit_template_by_name method" 2016-02-26 17:03:51 +00:00
Jenkins
5a8860419e Merge "Cleanup in tests/__init__.py" 2016-02-26 16:59:08 +00:00
Steve Wilkerson
4aa1c7558b Fixed type in get_audit_template_by_name method
Removed an extra underscore in the
get_audit_template_by__name method name in
watcher/db/api.py

Change-Id: I2687858ff4510c626c4dd2e2e9a5701405b5da55
Closes-Bug: #1548765
2016-02-26 08:25:42 -06:00
OpenStack Proposal Bot
a8dab52376 Updated from global requirements
Change-Id: Id68a055e6514ea30e28ef6adcfb22bb2ff3eeafb
2016-02-26 01:54:52 +00:00
Gábor Antal
5615d0523d Cleanup in tests/__init__.py
In watcher/tests/__init__.py has a totally unused,
misleading class so I removed it, as it is never used.

Change-Id: Ib878252453489eb3e1b1ff06f4d6b5e2b0726be5
Closes-Bug: #1549920
2016-02-25 19:10:54 +01:00
Jenkins
10823ce133 Merge "Cleanup in test_objects.py" 2016-02-25 16:06:02 +00:00
Jenkins
18c098c4c1 Merge "Update nova service state" 2016-02-25 08:30:40 +00:00
Jenkins
db649d86b6 Merge "Useless return statement in validate_sort_dir" 2016-02-24 16:49:01 +00:00
Tin Lam
6e380b685b Update nova service state
The primitive ChangeNovaServiceState allows us to change the state of
the nova-compute by calling nova api. The state of a nova-compute can
be ENABLED or DISABLED, however in the current implementation we use
OFFLINE and ONLINE.

Update the code to use ENABLED or DISABLED.

Change-Id: If3d9726bc5ae980b66c7fd4c5b7986f89d8bc690
Closes-Bug: #1523891
2016-02-23 00:46:30 -06:00
Antoine Cabot
8dfff0e8e6 Replace "Triggered" state by "Pending" state
This commit only applies to documentation, state name
must be updated in code accordingly.

Change-Id: I027fd55d968c12992d800de3657543be417c71b0
Related-Bug: #1548377
2016-02-22 17:33:41 +01:00
Chaozhe.Chen
fbc7da755a Add start directory for oslo_debug_helper
When I used `tox -e debug <test-name>` to test a case, the case did not
run properly and there is an error like this:

ImportError: Start directory is not importable: './python-watcher/tests'

If we use '-t wartcher/tests' to point out the test path, the case will
run properly under debug.
So we'd better add this start directory for oslo_debug_helper.

Change-Id: I04d9937f72a95f8f045129af08df0cd0d0870d39
2016-02-23 00:01:18 +08:00
Chaozhe.Chen
b947c30910 Add missing requirements
WebOb is needed in our code[1].

[1]https://github.com/openstack/watcher/blob/master/watcher/api/
middleware/parsable_error.py#L70

Change-Id: I6dd3940057368ff70656dd40c75ec59284f378bf
2016-02-22 21:14:25 +08:00
OpenStack Proposal Bot
9af96114af Updated from global requirements
Change-Id: I28d515eef452dd715a28e3fb4c177fc93f307c79
2016-02-20 22:02:12 +00:00
Vincent Françoise
1ddf69a68f Re-enable related Tempest test
Following the previous 2 patchset
https://review.openstack.org/#/c/279517/ and
https://review.openstack.org/#/c/279555/, this patchset re-enables
the related Tempest test which filters goals while removing the one
that was filtering by host aggregate.

Change-Id: I384e62320de34761e29d5cbac37ddc8ae253a70c
Closes-Bug: #1510189
2016-02-19 14:32:42 +00:00
Jenkins
379ac791a8 Merge "Pass parameter to the query in get_last_sample_values" 2016-02-19 10:24:36 +00:00
Jenkins
81ea37de41 Merge "Remove unused function and argument" 2016-02-19 01:01:10 +00:00
Gábor Antal
1c963fdc96 Useless return statement in validate_sort_dir
In watcher/api/controllers/v1/utils.py, in
the validate_sort_dir method has a return statement,
however the return value is exactly the parameter's value.

This is misleading, so I removed it.

Change-Id: I18c5c7853a5afedac88431347712a4348c9fd5dd
Closes-Bug: #1546917
2016-02-18 17:56:16 +01:00
Gábor Antal
f32995228b Pass parameter to the query in get_last_sample_values
In watcher/common/ceilometer_help.py:129,
there is an unused parameter, called limit:
  def get_last_sample_values(self, resource_id, meter_name, limit=1):

In the next line, there is a method call which can take this
currently unused 'limit' parameter. Probably, passing this 'limit'
parameter to the query was originally intended, however it wasn't
added to the query call.

Closes-Bug: #1541415
Change-Id: I025070c6004243d6b8a6ea7a1d83081480c4148b
2016-02-18 13:55:02 +01:00
Jenkins
0ec3d68994 Merge "Added goal filter in Watcher API" 2016-02-18 10:26:38 +00:00
Jenkins
3503e11506 Merge "Improve variable names in strategy implementations" 2016-02-18 10:22:50 +00:00
Jenkins
c7f0ef37d0 Merge "Added unit tests on actions" 2016-02-18 08:42:46 +00:00
Béla Vancsics
d93b1ffe9f Remove unused function and argument
I removed the unused function and (function)argument in code

Change-Id: Ib7afa5d868c3c7769f53e45c270850e4c3370f86
2016-02-18 09:17:43 +01:00
Vincent Françoise
4e71a0c655 Added goal filter in Watcher API
Although it was proposed via python-watcherclient, the feature was
not implemented on the Watcher API.
As the notion of host aggregate is currently unused in Watcher,
decision was made to only implement the filtering of goal within
the Watcher API whilst removing the host_aggregate filter from the
Watcher client.
Thus, this patchset adds this missing functionality by adding the
'goal' parameter to the API.

Change-Id: I54d248f7e470249c6412650ddf50a3e3631d2a09
Related-Bug: #1510189
2016-02-17 18:22:51 +01:00
Steve Wilkerson
37dd713ed5 Improve variable names in strategy implementations
Renamed many of the variables and method parameters
in the strategy implementations to make the names
more meaningful.  Also changed the abstract method
signature in base.py to reflect these changes.

Closes-Bug: #1541615

Change-Id: Ibeba6c6ef6d5b70482930f387b05d5d650812355
2016-02-16 09:15:14 -06:00
Vincent Françoise
55aeb783e3 Added unit tests on actions
As we had a low test coverage on actions, I added some more tests
with this patchset. This actually revealed a small bug (typo) in
"change_nova_service_state" which has been fixed in here.

Note that Tempest test also cover these action via the
basic_consolidation strategy.

Change-Id: I2d7116a6fdefee82ca254512a9cf50fc61e3c80e
Closes-Bug: #1523513
2016-02-16 11:42:37 +01:00
Jenkins
fe3f6e73be Merge "Remove KEYSTONE_CATALOG_BACKEND from DevStack plugin" 2016-02-16 08:24:54 +00:00
Jean-Emile DARTOIS
5baff7dc3e Clean imports in code
In some part in the code we import objects.
In the Openstack style guidelines they recommand
to import only modules.
We need to fix that.

Change-Id: I4bfee2b94d101940d615f78f9bebb83310ed90ba
Partial-Bug:1543101
2016-02-15 18:04:24 +01:00
Jean-Emile DARTOIS
e3198d25a5 Add Voluptuous to validate the action parameters
We want a simplest way to validate the input parameters of an
Action through a schema.

APIImpact
DocImpact
Partially implements: blueprint watcher-add-actions-via-conf

Change-Id: I139775f467fe7778c7354b0cfacf796fc27ffcb2
2016-02-12 17:47:52 +01:00
Jenkins
33ee575936 Merge "Better cleanup for Tempest tests" 2016-02-12 16:04:26 +00:00
Darren Shaw
259f2562e6 Remove KEYSTONE_CATALOG_BACKEND from DevStack plugin
Via Sean Dague in the mailing list:
In Newton, the KEYSTONE_CATALOG_BACKEND variable will be removed.
This commit removes the if check and variable from watcher

Change-Id: I73c5dd25feff9ba9824c267a8817a49e4ad3a06a
Closes-Bug: #1544433
2016-02-12 09:51:51 -06:00
Jenkins
1629247413 Merge "Update the default version of Neutron API" 2016-02-12 15:41:34 +00:00
Jenkins
58d84aca6d Merge "Ceilometer client instantiation fixup" 2016-02-12 10:00:33 +00:00
Gábor Antal
236879490d Cleanup in test_objects.py
In watcher/tests/objects/test_objects.py, there is a class
called "_TestObject(object)" which is probably an older test class.

This test class runs never (as the name starts with "_"
and inherits from object), and has some really old test,
like test_orphaned_object method, which is testing an exception
that doesn't exist at the current codebase.

Change-Id: I7559a004e8c136a206fc1cf7ac330c7d4157f94f
Closes-Bug: #1544685
2016-02-11 19:29:16 +01:00
Vincent Françoise
79850cc89c Better cleanup for Tempest tests
When running our Tempest tests, we are now cleaning up all the
objects we have created within the WatcherDB via a soft_delete.

Partially Implements: blueprint deletion-of-actions-plan

Change-Id: Ibdcfd2be37094377d09ad77d5c20298ee2baa4d0
2016-02-11 18:36:16 +01:00
Vincent Françoise
b958214db8 Ceilometer client instantiation fixup
A problem was found during manual integration tests which were failing
because we couldn't instantiate the ceilometer client when trying to
execute an action plan using the 'basic_consolidation' strategy.

This patchset fixes the problem with an update of the related tests

Change-Id: I2b1f1dcc16fd8dfbf508c4d5661c1fce194254e4
Closes-Bug: #1544652
2016-02-11 18:29:58 +01:00
Jenkins
a0b5f5aa1d Merge "Delete linked actions when deleting an action plan" 2016-02-11 09:39:33 +00:00
David TARDIVEL
a4a009a2c6 Update the default version of Neutron API
Default value of Neutron API should be 2.0 in neutronclient.
Closes-Bug: #1544134

Change-Id: I241c067c33c992da53f30e974ffca3edb466811d
2016-02-10 17:04:34 +01:00
Taylor Peoples
0ba8a35ade Sync with openstack/requirements master branch
In order to accept the requirements contract defined in
openstack/requirements, we need to sync with the master branch of that
project's global-requirements.txt and test-requirements.txt files.

Most of the changes are just version changes and nothing major.  The
only major change is:

1) The twine dependency is removed, and therefore the pypi tox
environment was also removed.

Change-Id: Idbe9e73ddc5a34ac49aa6f6eff0779d46a75f583
Closes-Bug: #1533282
2016-02-09 16:31:07 +00:00
Vincent Françoise
8bcc1b2097 Delete linked actions when deleting an action plan
When a user deletes an action plan, we now delete all related actions.

Partially Implements: blueprint deletion-of-actions-plan

Change-Id: I5d519c38458741be78591cbec04dbd410a6dc14b
2016-02-08 17:04:37 +01:00
Jenkins
b440f5c69a Merge "Add IRC information into contributing page" 2016-02-05 17:19:48 +00:00
Jenkins
ad40c61ea9 Merge "Update docs for password auth configuration options" 2016-02-05 15:58:31 +00:00
David TARDIVEL
858bbbf126 Add IRC information into contributing page
Add Watcher IRC channels : #openstack-watcher and
 #openstack-meeeting-4

Change-Id: I4c128544a32548065fddde54b28f645fc63ebfd5
Closes-Bug: #1536200
2016-02-05 15:53:21 +00:00
Jenkins
1d74f7e3bc Merge "Remove references to SERVERS_CONSOLIDATION" 2016-02-05 14:28:34 +00:00
Jenkins
b7641a9311 Merge "Added Tempest scenario for BASIC_CONSOLIDATION" 2016-02-04 08:18:30 +00:00
David TARDIVEL
376d669af6 Update docs for password auth configuration options
Watcher uses now auth_type 'password' plugin for authentication.
Configuration related to credentials used to validate and apply
for a token has been updated.

Change-Id: If71bb908741130cb01d5d1525a12cf9a68b58a58
Closes-Bug: #1541296
2016-02-03 19:24:00 +01:00
Jenkins
25d27f0288 Merge "Create OpenStackClients convenience class" 2016-02-03 10:21:32 +00:00
Jenkins
3f4686ce79 Merge "Use install instead of mkdir for DevStack dirs" 2016-02-03 06:43:17 +00:00
Taylor Peoples
86c1a9d77f Remove references to SERVERS_CONSOLIDATION
Change I6c43eba941022a88851a199b56a6c20f017b9e71 seemed to have remove
most references to the SERVERS_CONSOLIDATION goal.  Since this goal does
not currently exist in the actual code and all usages of it are for
samples or for tests, it is replaced with the DUMMY goal to avoid
confusion.

Change-Id: I4d2240d3b22c42ebf4e6120e2cd7677ec49d8e98
Closes-Bug: #1538388
2016-02-03 07:22:44 +01:00
Taylor Peoples
9a6811ae6b Create OpenStackClients convenience class
The OpenStackClients class provides a convenient way to create and
cache client instances.  The idea behind this code comes from Magnum
[0].

The OpenStackClients class will act as the manager of other project's
clients, providing an easy way to fetch instances of said clients. This
will allow the clients to be cached.

An instance of OpenStackClients is created for every call that comes
into the decision engine and the applier, using the request context to
pass needed (domain id) parameters to get a Keystone session.  This
instance should be shared as much as possible to avoid additional
unneccessary connections to the other services.

This class will also allow for the version of each client to be
configurable via the watcher.conf file.

The method by which a Keystone session is also changed to use the
keystoneauth1.loading library.  In order to avoid DuplicateOptErrors
with the keystone_authtoken group used for the keystonemiddleware in the
API code, a new conf group named "watcher_clients_auth" is created.  A
typical configuration using a password authentication scheme will look
like:
  [watcher_clients_auth]
  auth_type = password
  auth_url = http://<server-ip>:<port>
  username = <username>
  password = <password>
  project_domain_id = default
  user_domain_id = default

[0]: https://github.com/openstack/magnum/blob/master/magnum/common/clients.py

DocImpact
Change-Id: Iab9d0b304099686da2e9e2b19e8b1de4332ff378
Implements: blueprint external-api-versioning
Closes-Bug: #1530790
Closes-Bug: #1539670
Closes-Bug: #1522774
2016-02-03 02:27:26 +01:00
Jenkins
e520f5f452 Merge "Removed unused parameter in dt_deserializer()" 2016-02-03 00:13:38 +00:00
Jenkins
6a25bd983c Merge "Remove InvalidParameterValue exception" 2016-02-02 23:29:55 +00:00
Jenkins
c175ef2170 Merge "Define self.client in MessagingCore" 2016-02-02 16:08:12 +00:00
Jenkins
28733a5f30 Merge "Remove unused parameter in Actions API controller" 2016-02-02 14:45:47 +00:00
Vincent Françoise
7f8fec1bca Added Tempest scenario for BASIC_CONSOLIDATION
As of now we only have a single scenario which creates and
successfully executes the DUMMY goal.

This patchset adds a new scenario which creates and executes the
BASIC_CONSOLIDATION goal mapped to the 'basic' (sercon) strategy.

The documentation has also been updated to take into account the
multinode configuration.

Change-Id: Ie246aed288ade56a8fe9c0d9b08365d72e60ada1
Closes-Bug: #1538606
2016-02-02 13:35:17 +00:00
Edwin Zhai
278b1819d6 Use install instead of mkdir for DevStack dirs
The current code will not work if WATCHER_CONF_DIR or
WATCHER_AUTH_CACHE_DIR already exist but are owned by a different user
such as root. Use install instead of mkdir to handle this scenario.

Change-Id: Ie582a4b393e898e007d73f31de490c4b77e40be3
Closes-Bug: #1539422
2016-02-02 09:53:55 +00:00
Gábor Antal
978bb11d4a Removed unused parameter in dt_deserializer()
In the file watcher/objects/utils.py, on line 120,
there is an unused parameter:
  def dt_deserializer(instance, val):

I removed that parameter, and modified the test.

Change-Id: Ibc7ab703d37d7f9248a84e41508820453c8954b7
Closes-Bug: #1540521
2016-02-01 19:29:04 +01:00
Steve Wilkerson
3027b28942 Remove unused parameter in Actions API controller
Removed the action_uuid parameter in get_all() and
detail()

Change-Id: If99a4a50bb72383bd96ad284d35946911cb68d1d
Closes-Bug: #1538171
2016-01-29 12:41:10 -06:00
Darren Shaw
2f0c1c12cf Define self.client in MessagingCore
Currently self.client is referenced within MessagingCore,
but no definition is made in its constructor. Additionally
self.client is defined in children classes of MessagingCore.
This patchset defines self.client in the constructor of
MessagingCore and removes the redefinition in its children.

-self.client lazily loaded

Co-Authored-By: v-francoise <Vincent.FRANCOISE@b-com.com>
Change-Id: I14525a175bf1ebde3d2636024ad2f2219c79d6e1
Closes-Bug: #1521636
2016-01-27 16:24:45 +01:00
Taylor Peoples
e122c61840 Remove InvalidParameterValue exception
The InvalidParameterValue exception does not define a meaningful
msg_fmt.  It is currently _("%(err)s"), which is the equivalent of
nothing and does not help with translation.

Replace InvalidParameterValue with Invalid exceptions.

Change-Id: If8b064e446cbc97e380127f360f262be9e8877a1
Closes-Bug: #1538398
2016-01-27 16:13:52 +01:00
Vincent Françoise
8f6eac819f Tempest API tests on /actions
Following the blueprint tempest-basic-set-up which implemented a first
batch of tests, this one adds a new set of API tests on actions.

I also added extra check on actions within the dummy strategy
scenario.

Change-Id: Ib9bf093d0ed457ecba32e8251c019d2cf5c98128
Closes-Bug: #1538074
2016-01-27 10:02:59 +01:00
Vincent Françoise
de307e536e GET on an action_plan provides first_action_uuid
Whenever trying to get the first action related to a given action
plan, we were getting back a 'null' value from the API even though
we knew there were actions to be linked to it in the DB.
So I fixed this issue and added a related unit test.

Change-Id: I1fa755f24fbf37ecd6ce2cc2396658fca8743a1c
Closes-Bug: #1538130
2016-01-27 09:38:15 +01:00
Vincent Françoise
7406a1e713 Fixed ActionPlanNotFound typo in msg_fmt
The msg_fmt of ActionPlanNotFound was missing an "_" which caused
errors upon trying to format it, so I fixed it.

Change-Id: I515c2097a563f809e319d2e57480fd340b878cef
Closes-Bug: #1538065
2016-01-26 11:32:09 +01:00
Vincent Françoise
982410dd3e Fixed tempest test bug
has_audit_succeeded was not implemented so I added it back.

Change-Id: Ic567ff56ea6d513c32fbe7ad08cca96b5dfb15e8
Closes-Bug: #1537144
2016-01-26 09:17:39 +01:00
Vincent Françoise
83fdbf7366 Action plan state transition - payload validation
This patchset fixes the lack of field validation that are provided
by an API user.

Via a PATCH on /action_plans, the only field that can be modified
is now the 'state'. This field can only perform to the following
state transitions:

- RECOMMENDED --> TRIGGERED
- RECOMMENDED --> CANCELLED
- ONGOING --> CANCELLED
- TRIGGERED --> CANCELLED

The DELETED state can only be set using a DELETE request.

Closes-Bug: #1531106
Change-Id: I6669cbe63407f0bbb792fb2e2ce6b1e8a7365238
2016-01-25 17:37:59 +00:00
Jenkins
2db5ae31c7 Merge "API Tempest tests on goals" 2016-01-25 16:59:13 +00:00
Jenkins
2191844ebb Merge "Add 'workers' section into configuration doc" 2016-01-25 16:27:48 +00:00
David TARDIVEL
9c71b3df88 Add 'workers' section into configuration doc
Workers has been introduced into Decision Engine and Applier.
We can now tune ithe number of workers (threads) used by the
Decision Engine and the Applier services, by updating the Watcher
configuration file.

Change-Id: I81523666e4373e7b1dd59b36daf19067fd61b48f
Closes-Bug: #1528132
2016-01-25 16:17:21 +00:00
Jenkins
7beb9b4c29 Merge "Fix HTML warnings on HTML doc" 2016-01-25 16:04:23 +00:00
Vincent Françoise
8b7dce4803 API Tempest tests on goals
This patchset adds CRUD tests on goals via the API.

Partially Implements: blueprint tempest-basic-set-up

Change-Id: Ief544e738d4530bcf981824803de059ae554a059
2016-01-25 16:51:02 +01:00
Jenkins
94c50825e4 Merge "Added doc8" 2016-01-25 15:18:34 +00:00
David TARDIVEL
6ff4ba991e Fix HTML warnings on HTML doc
Some cros-references were broken. This pathset fixes them.

Change-Id: Iddd5df6cffb85258582c5571ce541a27467bea35
Closes-Bug: #1522034
2016-01-25 11:23:28 +01:00
Jenkins
9e5a3708e1 Merge "Action Plan state - Changed STARTING to TRIGGERED" 2016-01-22 16:52:03 +00:00
Jenkins
ca5ae03b81 Merge "Tempest scenario - execute a dummy strategy" 2016-01-22 16:51:22 +00:00
Jenkins
b47aefac30 Merge "API Tempest tests on Action plans" 2016-01-22 16:49:27 +00:00
Jenkins
70c7e1c639 Merge "Add reference to Ceilometer developer guide" 2016-01-22 16:48:26 +00:00
Jenkins
ef78e68022 Merge "Re-organize the Wacher documentation Home Page" 2016-01-22 15:20:22 +00:00
Vincent Françoise
4dfe6a3fa8 Action Plan state - Changed STARTING to TRIGGERED
As the STARTING state was not clear on its meaning, I renamed it
to TRIGGERED.

Change-Id: I99cceeb57f3d7d42c1543b21fad88a6872bc4e55
Closes-Bug: #1533245
2016-01-22 15:19:04 +00:00
Vincent Françoise
b53da21b9b Tempest scenario - execute a dummy strategy
This patchset implements the first scenario for Watcher which does
the following actions:

- create an audit template with the dummy strategy
- run the audit to create an action plan
- get the action plan
- run the action plan
- get results and make sure it succeeded

Partially Implements: blueprint tempest-basic-set-up

Change-Id: Iee74ede0bd1bcd03e8938f2ec8c6884f99e7f99a
2016-01-22 15:18:44 +00:00
Vincent Françoise
4305935312 Added doc8
For a better doc QoS, we now use doc8 as part of the testing
procedure while removing the existing tests we had on doc formatting.

I also updated tox.ini to run doc8 as within 'pep8' and 'docs' venvs.

Change-Id: Ia0ad99541509f4c026e26d28c41ff0210b12a504
Closes-Bug: #1524228
2016-01-22 15:56:47 +01:00
David TARDIVEL
894dfa0d7e Add reference to Ceilometer developer guide
Strategies can require, from Watcher, metrics collected on the IAAS.
Watcher uses only Ceilometer API to retrieve metrics.
Change the metrics database backend, add a new metric is not in
the scope of Watcher, but rather in the Ceilometer one. We add some
links to Ceilometer documentation about these topics.

Change-Id: If37c7df8e5852f5ecf94b4a9eb9c8c91fe6637eb
2016-01-22 14:53:40 +00:00
Vincent Françoise
a16351d352 API Tempest tests on Action plans
This patchset adds CRUD tests on Action Plans via the API.

Partially Implements: blueprint tempest-basic-set-up

Change-Id: I8ff3c3f0dbf7d301be2e3f401edf24bca44914bd
2016-01-22 15:18:15 +01:00
Jenkins
75772cd3db Merge "Fix 'Module index' broken HTTP link" 2016-01-22 14:04:59 +00:00
David TARDIVEL
63d9500904 Re-organize the Wacher documentation Home Page
Change the title of the page.
Create a 'Getting Started' section.
Create a 'Plugins' section.
Add links to related source code repositories.
Fix bad link to watcher client installation doc.

Change-Id: Ie0548149751d53b5fca235da69798dd0d333b14c
Partial-Bug: #1535244
2016-01-22 14:19:41 +01:00
David TARDIVEL
c8096c6148 Fix 'Module index' broken HTTP link
'Module Index' link points to HTML doc generated from source/api
RST files. These RST files are generated by pbr, by setting the
parameter 'autodoc_index_modules' to True.

Partial-Bug: #1535244
Change-Id: Ifceb5a140599d3968ea3d353b12c0bbe99d955e6
2016-01-22 12:58:53 +01:00
Vincent Françoise
6d0754bb65 API Tempest tests on Audits
This patchset adds CRUD tests on Audits via the API.
Many of them are currently skipped as they revealed some underlying
bugs which are referenced on launchpad.

Partially Implements: blueprint tempest-basic-set-up
Change-Id: I5769f601f9d1cb94bb541959f94f0fa2e17d15c9
2016-01-22 11:37:26 +01:00
Vincent Françoise
595b13a622 Refactored existing tempest API tests
We must set up Tempest for Watcher
(http://docs.openstack.org/developer/tempest/configuration.html)
to run integration tests inside devstack environment.

This patchset is a refactoring of the stale Tempest tests to now
use the latest Tempest coding standards (like using plugins and
credentials factory).

This commit will have an effect on the doc as we need to integrate
Tempest in the Watcher documentation.

DocImpact
Partially Implements: blueprint tempest-basic-set-up
Change-Id: I7600ff8a28d524b56c7dd4903ac4d203634ae412
2016-01-22 09:53:33 +00:00
Jenkins
8832ad78e2 Merge "Refactor Commands section" 2016-01-22 09:47:51 +00:00
Jenkins
be81f8aff2 Merge "Renamed Status to State" 2016-01-22 09:45:10 +00:00
Jenkins
e95548b1a9 Merge "Update the user-guide to explain the main steps" 2016-01-22 09:41:27 +00:00
Vincent Françoise
62b39fefbb Renamed Status to State
As we can see in the codebase, we have 3 "Status" enums which are
located at:

- watcher/objects/action.py
- watcher/objects/action_plan.py
- watcher/objects/audit.py

So I renamed them from "Status" to "State" to be consistent with
the DB schema.

Change-Id: If3d180c9daba6ae9083775ad6813aa4c21763dbf
Closes-Bug: #1522733
2016-01-22 09:36:21 +01:00
vmahe
f23548b92f Update the user-guide to explain the main steps
Added a new sequence diagram to explain showing the main steps
regarding Watcher usage:
- create a new audit template
- launch an audit and receive a recommended action plan
- launch the action plan

Change-Id: I1a0031b957d34908020be06870ed1d2772cd5af6
Closes-Bug: 1536731
2016-01-22 09:16:47 +01:00
Jenkins
43275537ea Merge "Removed unused parameters from api controllers" 2016-01-22 07:52:10 +00:00
David TARDIVEL
eaed650000 Refactor Commands section
Rename this section to 'Watcher Manual Pages' and add man RST
files.

Partial-Bug: #1535244
Change-Id: I1c890c8d053238aa568075d844f97b1e4289adcb
2016-01-21 19:46:04 +01:00
Jenkins
0505466ea5 Merge "Use taskflow library for building and executing action plans" 2016-01-21 17:38:14 +00:00
Jean-Emile DARTOIS
0e7bfe61bd Use taskflow library for building and executing action plans
The aim of this patchset is to integrate taskflow in
the Watcher Applier. Taskflow will help us a lot to make
Action Plan execution easy, consistent, scalable and reliable.

DocImpact

Partially implements: blueprint use-taskflow

Change-Id: I903d6509d74a61ad64e1506b8a7156e6e91abcfb
Closes-Bug: #1535326
Closes-Bug: #1531912
2016-01-21 18:13:42 +01:00
Jenkins
096354c255 Merge "Add diagrams to the architecture doc page" 2016-01-21 17:06:11 +00:00
Jenkins
afe06e2349 Merge "Reduced the complexity of the execute() method" 2016-01-21 17:05:05 +00:00
Jenkins
1d689ef410 Merge "Validate audit template UUID on audit create" 2016-01-21 16:55:20 +00:00
Gábor Antal
58fb86a3da Removed unused parameters from api controllers
In the Audit controller (watcher/api/controllers/v1/audit.py),
we have an unused "audit_uuid" parameter in both get_all()
and detail()

Similarly, the Action Plan controller
(watcher/api/controllers/v1/action_plan.py) has an unused
"action_plan_uuid" parameter in both get_all() and detail().

I also removed them from the @wsme_pecan.wsexpose decorator

Change-Id: I7f1edfd44dd95c9768249650e19b16fcbe916b89
Closes-Bug: #1534615
2016-01-21 13:55:18 +01:00
Jenkins
f675003076 Merge "Remove shadow BaseException class" 2016-01-21 08:10:55 +00:00
Taylor Peoples
e34ee792a8 Validate audit template UUID on audit create
The audit template UUID should be validated during the creation of an
audit.  An HTTP 400 error is returned to the client if an invalid audit
template UUID is passed as part of the body when creating an audit.

APIImpact
Closes-Bug: #1510188

Change-Id: I0543d22751b77f6641ddef6a7f0f4acce61180fd
2016-01-21 08:43:28 +01:00
Jenkins
037f43cd04 Merge "Missing super() in API collection controllers" 2016-01-20 14:14:08 +00:00
vmahe
8f87699910 Add diagrams to the architecture doc page
Added some data model diagrams, sequence diagrams and state machine
diagrams.
The state machine diagrams and sequence diagrams are built with
PlantUML whereas data model diagrams are built with Dia.
Also added some textual description with the sequence diagrams.

Change-Id: Iffbb47b0f2d12ce63eeaa1531a1bd1a790d69e79
Closes-Bug: #1531802
2016-01-20 07:23:03 +01:00
David TARDIVEL
c811051351 Fix Warnings generated while building of HTML docu
Same ID have been set to reference different RST blocks.
To avoid this, I added the prefix 'archi_' within ID referencing
architecture RST block.

Bad indentation warnings have been fixed.

Change-Id: I17f43f2f564ffd83fd5c345aed96fad06ee56b1d
Partial-Bug: #1522034
2016-01-20 00:58:09 +01:00
Béla Vancsics
a2750c74f9 Reduced the complexity of the execute() method
The execute() method is very large - almost 150 lines,
and McCabe's cyclomatic complexity 22.
(watcher/decision_engine/strategy/strategies/basic_consolidation.py)
I extracted some of the functionalities into helper functions
to reduce the length and complexity of execute().
http://openqa.sed.hu/dashboard/index/544838?did=1
Additionally it became more readable as well, without
changing its functionality.

Change-Id: I760929f56e258b87d7f1d4586bcc90665f1e0d8f
Closes-Bug: 1535729
2016-01-19 18:37:11 +01:00
Gábor Antal
67455c6a84 Missing super() in API collection controllers
The __init__ of these 3 classes are missing the super() call

watcher/watcher/api/controllers/v1/goal.py:113
watcher/api/controllers/v1/audit.py:217
watcher/watcher/api/controllers/v1/audit_template.py:166

Change-Id: I2fec1d5dac29a311f617ef141d5bf91f00b96219
Closes-Bug: #1535354
2016-01-19 13:15:32 +01:00
Taylor Peoples
a7455a8bf7 Remove shadow BaseException class
The BaseException class defined in exceptions.py is shadowing the
built-in BaseException class of the Python exception hierarchy, which
could potentially cause confusion.

This removes the BaseException definition and replaces it with the
existing WatcherException object.  Instantiations of the
IllegalArgumentException are also changed to use the message kwarg.

Change-Id: I20abf135805c7a354924de8a5194b59fc040460a
Closes-Bug: #1535504
2016-01-19 06:59:54 +01:00
Taylor Peoples
7fcb683404 Replace message with msg_fmt for custom exceptions
The custom exceptions were defining the message format string as the
'message' attribute, which was confusing as 'message' is also being used
as an input argument to the class.  This combined with the fact that the
the 'message' attribute of Python's BaseException has been deprecated,
msg_fmt replaces message to hold the message format of the exception.

The root cause of the bug in question was caused by the __unicode__
method returning self.message, which did not have the kwargs substituted
into the actual message format.  The fix is to use self.args[0] instead,
which will contain the message format with the kwargs substituted in
since that is passed to the super's (Exception) __init__ method.  See
PEP 0352 for more information on how Exception and BaseException work.

The _cleanse_dict method is also removed as it is not used anywhere.

Change-Id: Ie8ac96afaecc732693a184d0e06e77c56ca8eeb9
Closes-Bug: #1535473
2016-01-19 04:20:37 +01:00
Jenkins
65cf19a7e4 Merge "Removed use of deprecated LOG.warn method" 2016-01-18 17:08:58 +00:00
Jenkins
facca13dc4 Merge "Clean up flake8 ignore list" 2016-01-18 13:42:00 +00:00
Jenkins
e8576e8963 Merge "Renamed diskInfo.py" 2016-01-18 13:12:03 +00:00
Gábor Antal
dfc9a3b37d Removed use of deprecated LOG.warn method
LOG.warn is deprecated. We were still used it some places.
So I replaced the LOG.warn method to LOG.warning, which is not
deprecated.

Change-Id: I9461cec569445ad6c40db9ce2feeeba1ef0af0e3
Closes-Bug: #1508442
2016-01-18 13:12:52 +01:00
Jenkins
24dc92091c Merge "Keep py3.X compatibility for urllib" 2016-01-18 08:22:24 +00:00
Jenkins
53d6d7d131 Merge "Changed testr to os-testr" 2016-01-18 08:21:40 +00:00
Jenkins
fd6ecc1b13 Merge "Add a dynamic loading of Actions handlers in the Watcher Applier" 2016-01-15 15:44:56 +00:00
Jean-Emile DARTOIS
8bac4fd42a Add a dynamic loading of Actions handlers in the Watcher Applier
In watcher, an audit generates a set of actions which
aims at achieving a given goal (lower energy consumption, ...).
It is possible to configure different strategies in order to achieve
each goal. Each strategy is written as a Python class which produces
a set of actions. Today, the set of possible actions is fixed for a
given version of Watcher and enables optimization algorithms to
include actions such as instance migration, changing hypervisor state,
changing power state (ACPI level, ...).

The objective of this patchset is to give the ability to load
the actions dynamically in order to apply the Action Plan.

DocImpact
Partially implements: blueprint watcher-add-actions-via-conf

Change-Id: Idf295b94dca549ac65d4636e8889c8ab2ecc0df6
2016-01-15 16:08:18 +01:00
Jenkins
55d186be58 Merge "Move terminology definition to class related" 2016-01-15 14:54:48 +00:00
Jenkins
ed438d2eb2 Merge "Update API documentation for action plan" 2016-01-14 16:40:24 +00:00
David TARDIVEL
4898df89ca Update API documentation for action plan
Fix the Action Plan patch method docstring.

Change-Id: I631ba0f0754c00f49212e416f259c9158bb37d89
Closes-Bug: #1532691
2016-01-14 14:07:02 +00:00
Steve Wilkerson
ae949148ef Renamed diskInfo.py
Renamed diskInfo.py to disk_info.py and the associated test to
test_disk_info.  Also changed the usage in the test to reflect
the name change.

Closes-bug: #1533189

Change-Id: Ice63cf8ea6cd4fcc770f88952cf784e5d46cca5c
2016-01-14 07:59:55 -06:00
Jean-Emile DARTOIS
e0242b49f1 Fix extraction of _LI _LW _LE _LC for translation
In order to start translating we should gradually update the messages.
To fix this issue, we need to add _LI _LW _LE _LC in setup.cfg

Change-Id: Ia0bdea4dd3ecc12a6b804add9163926dc34bb581
Related-Bug: #1534164
2016-01-14 14:53:52 +01:00
ting.wang
5b57985686 Clean up flake8 ignore list
clean up ignore list and fix errors.

Change-Id: I3f44def50fbf44fa3cf5c49cc15a98947084c236
2016-01-14 18:42:45 +08:00
Jean-Emile DARTOIS
f6ef197473 Move terminology definition to class related
As of now, the glossary defined in our documentation reflects the
current state of the codebase. In order to avoid any discrepancy
between the codebase and each definition, the objective here is to
gather both in a single place and link it into the rst documentation
via a custom directive.
Also re-aligned the requirements with liberty for doc.

Change-Id: I3089fd9f948b948115672f10937b1500b96ce180
Partial-Bug: #1526671
2016-01-14 10:47:32 +01:00
ting.wang
61c926a10b Keep py3.X compatibility for urllib
Replace urllib with six.moves.urlparse

Change-Id: I224b7b00f7ecbe6d44ce5c9414bdaab825cbd6c7
2016-01-14 17:10:49 +08:00
Jenkins
9c33f8aaeb Merge "Remove incorrect spaces for libvirt_opts value" 2016-01-14 08:50:16 +00:00
ting.wang
08fc69cfc4 Use dict.items() dirrectly instead of six.iteritems
Replacing dict.iteritems()/.itervalues() with
six.iteritems(dict)/six.itervalues(dict) was preferred in the past,
but there was a discussion suggesting to avoid six for this.

ref:
http://lists.openstack.org/pipermail/openstack-dev/2015-June/066391.html

Change-Id: I63b1e51597307862968f37803ffdbba63306d8c6
2016-01-14 16:10:37 +08:00
Jenkins
dc6b8aad7e Merge "Test: make enforce_type=True in CONF.set_override and fix error" 2016-01-14 07:59:00 +00:00
ting.wang
473cee8ad3 Test: make enforce_type=True in CONF.set_override and fix error
Each config option has limitation for type and value.
We make enforce_type=True to check whether we pass wrong type.

Also fixes a type error issue in test_db_manager.py.

Change-Id: I6e111e21588525d32b05eeba75b06583d4e605ed
Related-Bug: #1517839
2016-01-14 07:10:16 +00:00
Taylor Peoples
2ffeb48010 Remove incorrect spaces for libvirt_opts value
The documentation for the DevStack plugin suggests changing the
libvirt_opts value of /etc/default/libvirt-bin to allow for live
migration between compute nodes.  This changeset removes the incorrect
spaces surrounding the equals sign in the file, which would cause the
libvirt-bin service to crash.

Change-Id: Id073da5cdc84a900a398f260d7f7b40f027eb9d3
Closes-Bug: #1533761
2016-01-14 07:50:16 +01:00
Jean-Emile DARTOIS
86d3c2ff89 Add a generic and extensible way to describe the flow of actions
In watcher, an audit generates a set of actions which
aims at achieving a given goal (lower energy consumption, ...).
It is possible to configure different strategies in order to achieve
each goal. Each strategy is written as a Python class which produces
a set of actions. Today, the set of possible actions is fixed for a
given version of Watcher and enables optimization algorithms to
include actions such as instance migration, changing hypervisor state,
changing power state (ACPI level, ...).

This patchset propose a generic and extensible way to describe
the actions and his parameters that we want to add to Action Plan.
It also remove the static actions because they are now deprecated.

The documentation regarding strategy plugin need to be
updated (plugins.rst).

DocImpact
Partially implements: blueprint watcher-add-actions-via-conf

Change-Id: I3d641080e8ad89786abca79a942c8deb2d53355b
2016-01-13 11:18:20 +01:00
Jean-Emile DARTOIS
47759202a8 Add a dynamic loading of the Watcher Planner implementation
In watcher, an audit generates a set of actions which
aims at achieving a given goal (lower energy consumption, ...).
It is possible to configure different strategies in order to achieve
each goal. Each strategy is written as a Python class which produces
a set of actions. Today, the set of possible actions is fixed for a
given version of Watcher and enables optimization algorithms to
include actions such as instance migration, changing hypervisor state,
changing power state (ACPI level, ...).

The objective of this patchset is to give the ability to extend the
default set of planner algorithms currently available in Watcher
using Stevedore.

The doc need to explain how create a new planner.

DocImpact
Partially implements: blueprint watcher-add-actions-via-conf

Change-Id: I2fd73f8c4a457ee391d764a7a3f494deecd2634f
2016-01-13 08:26:21 +00:00
Jean-Emile DARTOIS
c0306ea8f4 Add a common generic dynamic loader for watcher
In watcher, an audit generates a set of actions which
aims at achieving a given goal (lower energy consumption, ...).
It is possible to configure different strategies in order to achieve
each goal. Each strategy is written as a Python class which produces
a set of actions. Today, the set of possible actions is fixed for a
given version of Watcher and enables optimization algorithms to
include actions such as instance migration, changing hypervisor state,
changing power state (ACPI level, ...).

This patchset add a common generic dynamic loader for plugins,
such as for custom Actions, Strategies, Planners, etc.

Partially implements: blueprint watcher-add-actions-via-conf

Change-Id: I59d031b93865fff2540e3973921e1bdafa95f88e
2016-01-13 08:26:14 +00:00
Jean-Emile DARTOIS
34ccb7c23e Add the possibility to store several parameters for an Action
In watcher, an audit generates a set of actions which
aims at achieving a given goal (lower energy consumption, ...).
It is possible to configure different strategies in order to achieve
each goal. Each strategy is written as a Python class which produces
a set of actions. Today, the set of possible actions is fixed for a
given version of Watcher and enables optimization algorithms to
include actions such as instance migration, changing hypervisor state,
changing power state (ACPI level, ...).

This patchset add the possibility to store several parameters
for an Action.
The parameters store info that the custom Action needs.
The parameters provided as tuples with the following fields:
(parameter_name, parameter_type).

It remove also the deprecated attributes
(src,dst,description)

APIImpact
Partially implements: blueprint watcher-add-actions-via-conf

Change-Id: Ic6727341822f8ac62f212d337814b2dca76044e3
2016-01-13 09:25:18 +01:00
Biswajeeban Mishra
9900273988 Changed testr to os-testr
Change-Id: I6dd478e3bac50cf5a2d0ad32af0e36c25c0df071
Related-Bug: #1525854
2016-01-13 00:30:09 +00:00
Jenkins
4662f248b3 Merge "Remove duplicated nova wrapper" 2016-01-07 16:25:38 +00:00
Jenkins
7638103203 Merge "Implement DevStack plugin" 2016-01-07 16:25:13 +00:00
Jenkins
adc5587a2b Merge "Strategy goals should be required in conf" 2016-01-07 16:16:57 +00:00
Gábor Antal
34ab93a5f2 Strategy goals should be required in conf
In the section "WATCHER_GOALS_OPTS" the dict option "goals"
is not mandatory. However it should be.

Change-Id: I2e0770cf7787fed449c012bc45462e3138992ebf
Closes-Bug: #1531116
2016-01-07 16:08:50 +00:00
Swapnil Kulkarni (coolsvap)
c286e1ec4b Use assertTrue/False instead of assertEqual(T/F)
The usage of assertEqual(True/False, ***) should be changed
to a meaningful format of assertTrue/False(***).

Change-Id: Id708a94ac461adf021893a05796163bd2ced153c
Closes-Bug:#1512207
2016-01-07 12:51:22 +05:30
Taylor Peoples
6c1769a15e Implement DevStack plugin
Use DevStack's plugin model to allow for developers to easily set up
the Watcher services within DevStack.

Provides documentation on how to use the plugin as well as documentation
on how to configure a multi-node DevStack installation to use.  Also
provides an example local.conf for both the controller and compute
nodes.

Implements blueprint devstack-plugin

Change-Id: Ide663813f7a49d645877a21a0d1914be3218385e
2016-01-06 17:05:40 +01:00
Jean-Emile DARTOIS
b41a2cc940 Remove useless Meta-Action
Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher objects
and components by every contributor.

Partially implements: blueprint glossary-related-refactoring

Change-Id: Ie0e33562f5e990c264a50ab3f533cfa62eac1d19
2016-01-05 14:26:11 +01:00
junjie huang
8ebc898924 outlet Temperature based migration strategy
It implements one of the algorithm of Intel thermal POC.
It extends the BaseStrategy class, getting the Outlet
metrics of servers via Ceilometer, and generates solutions
when the outlet Temperature is greater than threshold.
current threshold is hard-coded, will make it configurable
in the next patches.

Implements: blueprint outlet-temperature-based-strategy

Change-Id: I248147329d34eddf408652205a077895be572010
Co-Authored-By: Zhenzan Zhou <zhenzan.zhou@intel.com>
2015-12-28 21:29:08 +00:00
Jenkins
660c782626 Merge "Move Audit-template management in DefaultStrategyContext" 2015-12-23 08:29:36 +00:00
Jean-Emile DARTOIS
dfaba80252 Move Audit-template management in DefaultStrategyContext
This aim of this patchset is to move the management of the
Audit-Template into StrategyContext
in order to prepare to pass parameters to strategies
but also to prepare to add more dynamic Actions management

Partially implements: blueprint glossary-related-refactoring

Change-Id: I13ee063da947113ce349855aa331a22f40567051
2015-12-22 17:52:51 +01:00
Zhenzan Zhou
8b357ace5a Remove duplicated nova wrapper
The nova wrapper2 just uses the same credential to create another
nova client session with the same capability. And it is hardcode
for keystone API v3.

Change-Id: I52b11a9b48ce2bb37a7872e2335ac3bae3f742c7
Closes-Bug: #1528142
2015-12-22 14:57:58 +08:00
Darren Shaw
9dccb29bf4 Move glossary.rst to root folder of doc
Move glossary.rst from /doc/dev to /doc/ as glossary is not only
related to dev. Updated index.rst.

Change-Id: I2e5251bdb0b94ef03727dea26bc43866544d2fd3
Closes-Bug: #1527130
2015-12-21 13:14:06 -06:00
Jenkins
853145f4d1 Merge "Remove string concatenation in favor of string formatting" 2015-12-21 10:13:56 +00:00
Jenkins
764f5c7681 Merge "Add Creative Commons Attribution header to documentation" 2015-12-21 10:10:36 +00:00
Jenkins
b81b767567 Merge "Rename NovaWrapper to NovaClient" 2015-12-21 10:06:53 +00:00
Jenkins
764e31a7a1 Merge "Remove useless event factory" 2015-12-21 10:06:21 +00:00
Steve Wilkerson
1c49d07912 Remove string concatenation in favor of string formatting
Some of the modules still utilized string concatenation
instead of using formatting.  In order to align with other
modules in the project, I refactored these modules to use string
formatting instead.

Change-Id: I708392e1d03b6331a134419aa0ae9dc02a05c31b
Closes-Bug: 1522738
2015-12-21 11:04:00 +01:00
Jean-Emile DARTOIS
18549dc182 Remove useless event factory
Change-Id: I8442a26ebfcfe3c17c378b283ffbbc0810b7a067
2015-12-21 10:31:15 +01:00
Jenkins
9f222221a7 Merge "Change default strategy to DummyStrategy" 2015-12-21 09:29:29 +00:00
Jean-Emile DARTOIS
1a64383a68 Rename NovaWrapper to NovaClient
This patchset aim to rename the nova client class and move it with
the other openstack clients in the folder openstack/common.

Change-Id: Ie8aab199922985f42ad85e6688f0727b24f53ffd
2015-12-21 10:26:20 +01:00
Vincent Françoise
ac07f35dc7 i18n - Make string translatable
Since internationalization should be enabled in Watcher, this
patchset refactors the Watcher codebase to wrap previously
untranslatable strings with i18n translation functions so we can
import them for translation into the .pot template file.

Partially Implements: blueprint support-translation
Change-Id: I425967a60b5a7957f753894e5d2ba0d2c5009d1d
2015-12-21 10:08:59 +01:00
Jean-Emile DARTOIS
010bc61cc9 Change default strategy to DummyStrategy
The aim of this patchset is to change the default strategy
and set invoke_on_load to False.

Change-Id: I0e374993614f465b11a22e33008f7026642154ee
2015-12-21 09:55:53 +01:00
Jenkins
3dd02ee895 Merge "Code refactoring - StrategyContext and Auditendpoint" 2015-12-21 08:40:04 +00:00
Steve Wilkerson
a4bbe7f893 Add Creative Commons Attribution header to documentation
Changed the header in the documentation to reflect that the docs
are covered under the CCBY 3.0 license.

Change-Id: I29f3f1a2491c28b1a4ee12a7b97a7ab00c919867
Closes-Bug: 1526331
2015-12-20 01:51:00 -06:00
Jean-Emile DARTOIS
4c3073efb4 Code refactoring - StrategyContext and Auditendpoint
This patchset aim to remove useless code in StrategyContext
and AuditEndPoint.
This patchset also add a parameter for strategy context to define the
numbers of thread of execute the strategies.

DocImpact
Change-Id: I83e87165b03b42fe6b863921502a300bd94d2982
2015-12-18 14:25:07 +00:00
Jenkins
642226812f Merge "Remove *.pyc files before running tox tests" 2015-12-18 12:52:31 +00:00
Jenkins
8f2ca2518f Merge "'admin_user' opt (and others) imported twice" 2015-12-18 12:48:56 +00:00
Jenkins
1698eb31f3 Merge "Fix generation of watcher config file" 2015-12-17 18:34:26 +00:00
Jenkins
5fb74e677f Merge "Rename command to audit" 2015-12-17 13:21:39 +00:00
Gábor Antal
35a5ba1cd0 Remove *.pyc files before running tox tests
Modified tox.ini to remove any .pyc file before running tests.
Also, now it removes all the __pycache__ folders which was also
in the bug report.

Change-Id: I2f60751b155f8098b746339c12406b934bcdbcf9
Closes-Bug: #1525852
2015-12-17 11:59:54 +01:00
Jean-Emile DARTOIS
c0a85be7b8 Add missing parameter in prepare_service for api
Although the bug #1526888 (prepare_service() - duplicate function) got merged
https://review.openstack.org/#/c/258007/ we didn't see that the file in
(watcher/cmd/api.py) api didn't have sys.argv parameter.

Change-Id: I458b4ff169684131a20ef6ac090b1c918f08d427
Closes-Bug: 1526888
2015-12-16 17:58:18 +01:00
Jean-Emile DARTOIS
633b360652 Fix generation of watcher config file
Although the refactoring of the applier got merged we have an issue
to generate the config. This pathset fix that

Change-Id: Iafce7d0136b2ad813102c3e3caeebafa215363c8
Closes-Bug: 1526851
2015-12-16 16:09:12 +00:00
Jenkins
d3160bf007 Merge "Removed duplicated function prepare_service()" 2015-12-16 10:41:32 +00:00
Jean-Emile DARTOIS
69152f2449 Rename command to audit
This patchset is there to change the code structure.

Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset is there to rename the folder command to audit

Partially implements: blueprint glossary-related-refactoring

Change-Id: I76616fb58d5e79a7dc209b80e882d216850d18a4
2015-12-16 10:23:44 +01:00
Jenkins
37cd75ffe3 Merge "Include terminology definition from docstring" 2015-12-16 09:08:25 +00:00
Gábor Antal
c7fd5e8b21 'admin_user' opt (and others) imported twice
"admin_user", "admin_tenant_name", "admin_password" and "auth_uri"
are options which are imported from the "keystone_authtoken" group,
itself being part of the "keystonemiddleware" 3rd-party library.

The problem is that these options are imported at 2 different
locations in the codebase:

- watcher/applier/manager.py
- watcher/common/keystone.py

Removed one from the applier.

Change-Id: Ie7075883018ec69f6a4e8190f50a9ea949ab9745
Closes-Bug: #1526259
2015-12-15 20:10:02 +01:00
Gábor Antal
f0b58f8c27 Removed duplicated function prepare_service()
The prepare_service() function is defined both in watcher/service.py
and in watcher/common/service.py.

These 2 needed to be merged into a single one
to avoid code duplication.

At the same time, the watcher/service.py only contains this function,
so I removed that file.

Change-Id: I0c935dfcd011bee9597315752dae8668221c53f9
Closes-Bug: #1525842
2015-12-15 19:14:52 +01:00
Jenkins
22dd6d42c3 Merge "Internationalization (i18n) - Enable French locale" 2015-12-15 17:49:50 +00:00
Vincent Françoise
bd29e2e79f Internationalization (i18n) - Enable French locale
Our project should now enable its internationalization.
This patchset add the french locale to the project but also
refactors the codebase to following the oslo_i18n recommendations.

DocImpact
Implements: blueprint support-translation

Change-Id: I0e4fbf05d16afb5e25bac78438c640f147c754b1
2015-12-15 17:57:10 +01:00
Vincent Françoise
c2eb112184 Include terminology definition from docstring
As of now, the glossary defined in our documentation reflects the
current state of the codebase. In order to avoid any discrepancy
between the codebase and each definition, the objective here is to
gather both in a single place and link it into the rst documentation
via a custom directive.
Also re-aligned the requirements with liberty for doc.

DocImpact

Change-Id: I9ca50f8d3c32b4690ee240e13dec0cb7eeedcc2c
2015-12-15 11:38:42 +01:00
Jean-Emile DARTOIS
57cecb27f5 Remove pragma no cover from code
Add exclude_lines in the report section of .coveragerc to ignore
abstract in test coverage

Change-Id: I7863a8ba7e20358fb7cdf3cc7e4d83871a5104ef
2015-12-15 10:14:40 +01:00
Jenkins
7c72d6f912 Merge "Some tests are ignored" 2015-12-15 07:57:58 +00:00
Jean-Emile DARTOIS
62570525ad Tidy up - Watcher Decision Engine package
Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset is there to change the code structure by adding the folder
"strategies" and "loading".

Partially implements: blueprint glossary-related-refactoring

Change-Id: I56fb24ee6762b3186eccde5983233e17bb227cc1
2015-12-14 14:33:56 +01:00
Jenkins
92940ba9e2 Merge "Typo in ClusteStateNotDefined" 2015-12-14 10:56:51 +00:00
Jenkins
7c8ce453ed Merge "Add Apache license header to all rst documentation" 2015-12-14 10:43:37 +00:00
Gábor Antal
33ea5f96f8 Typo in ClusteStateNotDefined
ClusteStateNotDefined has a typo, it should be ClusterStateNotDefine

Change-Id: I727301786d47db847215d73722051e59d340f1c2
Closes-Bug: #1525818
2015-12-14 11:41:29 +01:00
Jenkins
916f4d0c08 Merge "Tidy up - Rename Base" 2015-12-14 10:27:47 +00:00
Jenkins
3fb5defc16 Merge "Removed H404, H405, H305 ignore in pep8" 2015-12-14 10:12:37 +00:00
Jean-Emile DARTOIS
b01f4bead4 Some tests are ignored
Testr is using the prefix _test for discover tests.

Change-Id: I963c25f1d331273dd0d28a374be894246413530b
2015-12-14 10:31:06 +01:00
Jenkins
8516d629c2 Merge "Added unit tests on nova wrapper" 2015-12-14 09:21:54 +00:00
Jean-Emile DARTOIS
35a1f0a657 Tidy up - Rename Base
Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset add missing Base in class name

Partially implements: blueprint glossary-related-refactoring

Change-Id: I95a3e41fbd5fcd90a99d81c9cf278940f50c7732
2015-12-11 14:45:50 +01:00
Vincent Françoise
d934971458 Refactored Watcher codebase to add py34 support
Even though Watcher was mentioning python 3.4 as supported, it
really wasn't the case as all the unit tests were not passing in this
version of Python.

This patchset fixes all the failing tests in Python 3.4 while
keeping Watcher Python 2.7 compatible.

DocImpact
BugImpact
Change-Id: Ie74acc08ef0a2899349a4b419728c89e416a18cb
2015-12-11 13:24:02 +00:00
Vincent Françoise
d92f85574d Added unit tests on nova wrapper
As the coverage on the nova wrapper is actually very low, this
patchset is here to raise it a bit since it is a quite central
piece of code.

Change-Id: Ie7879c74c8d322d5031953827c339bb11d9085c1
2015-12-11 10:47:03 +01:00
Jenkins
b1fe7a5f3d Merge "Remove references to removed watcher/openstack directory" 2015-12-11 08:42:46 +00:00
Jenkins
8a5eb4b6a1 Merge "Remove unreachable code in basic_consolidation.py" 2015-12-11 08:29:57 +00:00
Jenkins
91c14e4eda Merge "Removed unnecessary code from basic_consolidation" 2015-12-11 08:07:28 +00:00
Gábor Antal
1613bd6904 Removed H404, H405, H305 ignore in pep8
In the file tox.ini we have some pep8 rules disabled.
We should remove H404,H405,H305 from the ignore list.

Removed them from the ignore list, and got some errors.
I restructured the comments, and now with H404, H405, H305 enabled,
pep8 works without any failures.

Change-Id: Ic2aeb2a8bd47e92fbd2bb0f43fd00d44b6c220ca
Closes-Bug: #1523841
2015-12-10 19:35:52 +01:00
Gábor Antal
ba4f5569d1 Removed unnecessary code from basic_consolidation
In basic_consolidation.py has a method, called calculate_score_vm().
This method used to get vm's id as a parameter, but it was replaced
and now it gets the vm as a parameter. So we don't need to get the
vm from our vm's id, as we already have the vm.

Change-Id: I96af7fbdbe85eda8d4fc44b4b162e8ba9d4967fa
2015-12-10 18:55:16 +01:00
Gábor Antal
a62553a6a5 Remove unreachable code in basic_consolidation.py
In basic_consolidation.py there is a method called calculate_score_vm()
which had two return statements following each other. The second one
(which equals the first one) is unreachable as the first one returns.

Change-Id: Ia4877c22188fae6217e07597a2dd939633414349
Closes-Bug: #1524911
2015-12-10 18:20:35 +01:00
Jean-Emile DARTOIS
d5ba40530f Rename Mapper to Mapping
Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset is there to change mapper to mapping

Partially implements: blueprint glossary-related-refactoring

Change-Id: Ieaca42431322ce40d87de147ac0b46a1f446f390
2015-12-10 17:58:58 +01:00
Jean-Emile DARTOIS
f98e96da42 Tidy up - Primitive
Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset is there to change
Primitive to Primitives.
Add BasePrimitive.

Partially implements: blueprint glossary-related-refactoring

Change-Id: I839bddd12b5320b338b2f207d74963afa23de522
2015-12-10 17:37:13 +01:00
Jenkins
64747cad1f Merge "Rename Command to Action" 2015-12-10 16:33:04 +00:00
Jenkins
5f87e82bac Merge "Removed py33, pypy support" 2015-12-10 16:31:43 +00:00
Jean-Emile DARTOIS
ff89e942ca Remove references to removed watcher/openstack directory
Since we removed openstack/common this is not useful.

Change-Id: Ifb8e1cdacd1879874be7496a8bc4bc8349085456
2015-12-10 16:13:43 +00:00
Gábor Antal
7faa501fb7 Removed py33, pypy support
As Openstack Liberty now targets Python 2.7 and 3.4,
we should drop Python 3.3 support from our setup.cfg

Change-Id: I8c8f9222fcf1111e99a943976aae9c5b427f7ee4
Closes-Bug: #1523983
2015-12-10 15:53:09 +01:00
Jenkins
ab8d242c1f Merge "Remove alembic revision of watcher db" 2015-12-10 08:35:04 +00:00
Jenkins
bbd26cafae Merge "Update the glossary to lay down Watcher terminology" 2015-12-10 08:35:01 +00:00
Jenkins
0042356245 Merge "Rename command to action_plan" 2015-12-10 08:31:21 +00:00
Jean-Emile DARTOIS
e1d4026c7c Remove alembic revision of watcher db
This code is not useful because we have only one version 
of watcher db currently and this alembic revision is out
of date compared to the initial schema as of mitaka-1.

Change-Id: Id21c665ff7a600a716e80d112e131a0e13687b41
2015-12-09 16:20:51 +00:00
Jenkins
df692a8215 Merge "Rename efficiency to efficacy" 2015-12-09 15:43:43 +00:00
Darren Shaw
f9323889d6 Add Apache license header to all rst documentation
This patch has the Apache license header found in
doc/source/dev/glossary.rst added to every .rst file

Change-Id: Icc20e7baf7d3cd0f116c371d54ef03c7c8401778
Closes-Bug: #1523986
2015-12-09 08:34:53 -06:00
Jenkins
0a44b2972e Merge "Rename Meta-Action to Action" 2015-12-09 10:59:14 +00:00
Jean-Emile DARTOIS
c5c16ac055 Rename Command to Action
Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset is there to change Command to Action

Partially implements: blueprint glossary-related-refactoring

Change-Id: Id38e133fc8b789319c7db5712d3820ecca1bd51d
2015-12-09 09:35:30 +00:00
Jenkins
3016d3da11 Merge "Add a checker for the documentation" 2015-12-09 08:14:45 +00:00
Jean-Emile DARTOIS
daa560111c Update the glossary to lay down Watcher terminology
The Primitive used by the applier is not defined in the glossary.
This patchset start to fix this gap

Change-Id: Ie3fe153fc4c396da5c83425ccdd540910ad33e49
2015-12-09 09:10:14 +01:00
Jenkins
16705f68da Merge "Removed unused enum" 2015-12-08 23:14:03 +00:00
Jean-Emile DARTOIS
109a980c29 Rename command to action_plan
Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset is there to change command to action_plan

Partially implements: blueprint glossary-related-refactoring

Change-Id: I19a70adeca347ce747a2221b5fc31658139c95a2
2015-12-08 15:26:44 +01:00
Vincent Françoise
531373cb84 Removed unused enum
This StrategyState Enum is not used anywhere in the codebase (and
shouldn't be), so I removed it.

Change-Id: I0b5d3102b4d08856dccd751313fdd097937d8ccf
2015-12-08 14:22:22 +01:00
Jean-Emile DARTOIS
087c4d49ed Rename Meta-Action to Action
Some Python class and packages need to be renamed
for a better compliance with the shared terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset is there to change Meta-Action to Action

Partially implements: blueprint glossary-related-refactoring

Change-Id: Ie67b800332179be93718030ddd7a36ddc76a544c
2015-12-08 11:53:29 +01:00
Jean-Emile DARTOIS
022b15dc1e Add a checker for the documentation
This patchset add some unit tests on the documentation:
Checking wrapping
Checking trailing spaces
Checking no_cr

Change-Id: I3fa56d3e7dd3218dcd398e6750bdd2fb3a8e75b4
2015-12-08 11:28:45 +01:00
Jean-Emile DARTOIS
454f70a19f Rename efficiency to efficacy
Some Python class and packages need to be renamed
for a better compliance with the shared Terminology
which provides a better understanding of Watcher
objects and components by every contributor.

This patchset is there to change efficiency to efficacy

Partially implements: blueprint glossary-related-refactoring

Change-Id: I4c84192d49a147e0fd406da35e2805143b902331
2015-12-08 09:18:48 +00:00
Steve Wilkerson
98f05a52a8 Fix Watcher Applier variables in CamelCase
PEP8 standards call for instance variable names to be written
in the same manner as methods, with underscores separating words.
This fix addresses an instance variable  in the DeployPhase class.

As the instance variable seems public, the Java-ish getter and
setter mentioned in the bug report have been removed as well.

Change-Id: I8835315c8cae64665d3ccb321c4066e37a22c467
Closes-Bug: 1522489
2015-12-07 10:40:57 -06:00
Jenkins
5ff9f28a83 Merge "Remove duplicate setup in Watcher API main()" 2015-12-07 10:35:55 +00:00
Darren Shaw
4a88220ffe Remove duplicate setup in Watcher API main()
main() duplicates code for oslo_log setup, which is first
called within prepare_services(). This patches removes
the duplication call in main()

Change-Id: I712d3733218c18b2eb02d4bf26e54b29ef72a988
Closes-Bug: #1522781
2015-12-04 20:39:42 -06:00
Jean-Emile DARTOIS
4c2d0e6345 Cleanup deprecated documentation
Since vincent mahe created a great
documentation available in doc/source
this one is deprecated

Change-Id: I81ff633771570f28a38e3d277718f0a97a9d5090
2015-12-04 18:21:47 +01:00
vmahe
7710b1670e Provide detailed information on architecture
Added new global architecture diagram using Dia tool
and a more detailed description of each component.
Exported this diagram in SVG format.
The source code of the diagram is stored in
/doc/images_src/ folder.
Moved also the architecture.rst file from the /dev
folder to the root folder of the Watcher doc.

Change-Id: I74379390178673dcb6a043967b31e38ca9560bb3
2015-12-04 17:33:05 +01:00
312 changed files with 13484 additions and 7376 deletions

View File

@@ -1,7 +1,9 @@
[run]
branch = True
source = watcher
omit = watcher/tests/*,watcher/openstack/*
omit = watcher/tests/*
[report]
ignore_errors = True
exclude_lines =
@abstract

1
.gitignore vendored
View File

@@ -43,6 +43,7 @@ output/*/index.html
# Sphinx
doc/build
doc/source/api
# pbr generates these
AUTHORS

View File

@@ -1,3 +1,9 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
==========================
watcher Style Commandments
==========================

View File

@@ -1,3 +1,9 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
=======
Watcher
=======

246
devstack/lib/watcher Normal file
View File

@@ -0,0 +1,246 @@
#!/bin/bash
#
# lib/watcher
# Functions to control the configuration and operation of the watcher services
# Dependencies:
#
# - ``functions`` file
# - ``SERVICE_{TENANT_NAME|PASSWORD}`` must be defined
# - ``DEST``, ``DATA_DIR``, ``STACK_USER`` must be defined
# ``stack.sh`` calls the entry points in this order:
#
# - is_watcher_enabled
# - install_watcher
# - configure_watcher
# - create_watcher_conf
# - init_watcher
# - start_watcher
# - stop_watcher
# - cleanup_watcher
# Save trace setting
_XTRACE_WATCHER=$(set +o | grep xtrace)
set +o xtrace
# Defaults
# --------
# Set up default directories
WATCHER_REPO=${WATCHER_REPO:-${GIT_BASE}/openstack/watcher.git}
WATCHER_BRANCH=${WATCHER_BRANCH:-master}
WATCHER_DIR=$DEST/watcher
GITREPO["python-watcherclient"]=${WATCHERCLIENT_REPO:-${GIT_BASE}/openstack/python-watcherclient.git}
GITBRANCH["python-watcherclient"]=${WATCHERCLIENT_BRANCH:-master}
GITDIR["python-watcherclient"]=$DEST/python-watcherclient
WATCHER_STATE_PATH=${WATCHER_STATE_PATH:=$DATA_DIR/watcher}
WATCHER_AUTH_CACHE_DIR=${WATCHER_AUTH_CACHE_DIR:-/var/cache/watcher}
WATCHER_CONF_DIR=/etc/watcher
WATCHER_CONF=$WATCHER_CONF_DIR/watcher.conf
WATCHER_POLICY_JSON=$WATCHER_CONF_DIR/policy.json
if is_ssl_enabled_service "watcher" || is_service_enabled tls-proxy; then
WATCHER_SERVICE_PROTOCOL="https"
fi
# Public facing bits
WATCHER_SERVICE_HOST=${WATCHER_SERVICE_HOST:-$HOST_IP}
WATCHER_SERVICE_PORT=${WATCHER_SERVICE_PORT:-9322}
WATCHER_SERVICE_PORT_INT=${WATCHER_SERVICE_PORT_INT:-19322}
WATCHER_SERVICE_PROTOCOL=${WATCHER_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
# Support entry points installation of console scripts
if [[ -d $WATCHER_DIR/bin ]]; then
WATCHER_BIN_DIR=$WATCHER_DIR/bin
else
WATCHER_BIN_DIR=$(get_python_exec_prefix)
fi
# Entry Points
# ------------
# Test if any watcher services are enabled
# is_watcher_enabled
function is_watcher_enabled {
[[ ,${ENABLED_SERVICES} =~ ,"watcher-" ]] && return 0
return 1
}
# cleanup_watcher() - Remove residual data files, anything left over from previous
# runs that a clean run would need to clean up
function cleanup_watcher {
sudo rm -rf $WATCHER_STATE_PATH $WATCHER_AUTH_CACHE_DIR
}
# configure_watcher() - Set config files, create data dirs, etc
function configure_watcher {
# Put config files in ``/etc/watcher`` for everyone to find
sudo install -d -o $STACK_USER $WATCHER_CONF_DIR
install_default_policy watcher
# Rebuild the config file from scratch
create_watcher_conf
}
# create_watcher_accounts() - Set up common required watcher accounts
#
# Project User Roles
# ------------------------------------------------------------------
# SERVICE_TENANT_NAME watcher service
function create_watcher_accounts {
create_service_user "watcher" "admin"
local watcher_service=$(get_or_create_service "watcher" \
"infra-optim" "Watcher Infrastructure Optimization Service")
get_or_create_endpoint $watcher_service \
"$REGION_NAME" \
"$WATCHER_SERVICE_PROTOCOL://$WATCHER_SERVICE_HOST:$WATCHER_SERVICE_PORT" \
"$WATCHER_SERVICE_PROTOCOL://$WATCHER_SERVICE_HOST:$WATCHER_SERVICE_PORT" \
"$WATCHER_SERVICE_PROTOCOL://$WATCHER_SERVICE_HOST:$WATCHER_SERVICE_PORT"
}
# create_watcher_conf() - Create a new watcher.conf file
function create_watcher_conf {
# (Re)create ``watcher.conf``
rm -f $WATCHER_CONF
iniset $WATCHER_CONF DEFAULT debug "$ENABLE_DEBUG_LOG_LEVEL"
iniset $WATCHER_CONF DEFAULT control_exchange watcher
iniset $WATCHER_CONF database connection $(database_connection_url watcher)
iniset $WATCHER_CONF api host "$WATCHER_SERVICE_HOST"
iniset $WATCHER_CONF api port "$WATCHER_SERVICE_PORT"
iniset $WATCHER_CONF oslo_policy policy_file $WATCHER_POLICY_JSON
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_userid $RABBIT_USERID
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_host $RABBIT_HOST
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR "watcher_clients_auth"
if is_fedora || is_suse; then
# watcher defaults to /usr/local/bin, but fedora and suse pip like to
# install things in /usr/bin
iniset $WATCHER_CONF DEFAULT bindir "/usr/bin"
fi
if [ -n "$WATCHER_STATE_PATH" ]; then
iniset $WATCHER_CONF DEFAULT state_path "$WATCHER_STATE_PATH"
iniset $WATCHER_CONF oslo_concurrency lock_path "$WATCHER_STATE_PATH"
fi
if [ "$SYSLOG" != "False" ]; then
iniset $WATCHER_CONF DEFAULT use_syslog "True"
fi
# Format logging
if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
setup_colorized_logging $WATCHER_CONF DEFAULT
else
# Show user_name and project_name instead of user_id and project_id
iniset $WATCHER_CONF DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(levelname)s %(name)s [%(request_id)s %(user_name)s %(project_name)s] %(instance)s%(message)s"
fi
# Register SSL certificates if provided
if is_ssl_enabled_service watcher; then
ensure_certificates WATCHER
iniset $WATCHER_CONF DEFAULT ssl_cert_file "$WATCHER_SSL_CERT"
iniset $WATCHER_CONF DEFAULT ssl_key_file "$WATCHER_SSL_KEY"
iniset $WATCHER_CONF DEFAULT enabled_ssl_apis "$WATCHER_ENABLED_APIS"
fi
if is_service_enabled ceilometer; then
iniset $WATCHER_CONF watcher_messaging notifier_driver "messaging"
fi
}
# create_watcher_cache_dir() - Part of the init_watcher() process
function create_watcher_cache_dir {
# Create cache dir
sudo install -d -o $STACK_USER $WATCHER_AUTH_CACHE_DIR
rm -rf $WATCHER_AUTH_CACHE_DIR/*
}
# init_watcher() - Initialize databases, etc.
function init_watcher {
# clean up from previous (possibly aborted) runs
# create required data files
if is_service_enabled $DATABASE_BACKENDS && is_service_enabled watcher-api; then
# (Re)create watcher database
recreate_database watcher
# Create watcher schema
$WATCHER_BIN_DIR/watcher-db-manage --config-file $WATCHER_CONF create_schema
fi
create_watcher_cache_dir
}
# install_watcherclient() - Collect source and prepare
function install_watcherclient {
if use_library_from_git "python-watcherclient"; then
git_clone_by_name "python-watcherclient"
setup_dev_lib "python-watcherclient"
fi
}
# install_watcher() - Collect source and prepare
function install_watcher {
git_clone $WATCHER_REPO $WATCHER_DIR $WATCHER_BRANCH
setup_develop $WATCHER_DIR
}
# start_watcher_api() - Start the API process ahead of other things
function start_watcher_api {
# Get right service port for testing
local service_port=$WATCHER_SERVICE_PORT
local service_protocol=$WATCHER_SERVICE_PROTOCOL
if is_service_enabled tls-proxy; then
service_port=$WATCHER_SERVICE_PORT_INT
service_protocol="http"
fi
run_process watcher-api "$WATCHER_BIN_DIR/watcher-api --config-file $WATCHER_CONF"
echo "Waiting for watcher-api to start..."
if ! wait_for_service $SERVICE_TIMEOUT $service_protocol://$WATCHER_SERVICE_HOST:$service_port; then
die $LINENO "watcher-api did not start"
fi
# Start proxies if enabled
if is_service_enabled tls-proxy; then
start_tls_proxy '*' $WATCHER_SERVICE_PORT $WATCHER_SERVICE_HOST $WATCHER_SERVICE_PORT_INT &
start_tls_proxy '*' $EC2_SERVICE_PORT $WATCHER_SERVICE_HOST $WATCHER_SERVICE_PORT_INT &
fi
}
# start_watcher() - Start running processes, including screen
function start_watcher {
# ``run_process`` checks ``is_service_enabled``, it is not needed here
start_watcher_api
run_process watcher-decision-engine "$WATCHER_BIN_DIR/watcher-decision-engine --config-file $WATCHER_CONF"
run_process watcher-applier "$WATCHER_BIN_DIR/watcher-applier --config-file $WATCHER_CONF"
}
# stop_watcher() - Stop running processes (non-screen)
function stop_watcher {
for serv in watcher-api watcher-decision-engine watcher-applier; do
stop_process $serv
done
}
# Restore xtrace
$_XTRACE_WATCHER
# Tell emacs to use shell-script-mode
## Local variables:
## mode: shell-script
## End:

View File

@@ -0,0 +1,46 @@
# Sample ``local.conf`` for compute node for Watcher development
# NOTE: Copy this file to the root DevStack directory for it to work properly.
[[local|localrc]]
ADMIN_PASSWORD=nomoresecrete
DATABASE_PASSWORD=stackdb
RABBIT_PASSWORD=stackqueue
SERVICE_PASSWORD=$ADMIN_PASSWORD
SERVICE_TOKEN=azertytoken
HOST_IP=192.168.42.2 # Change this to this compute node's IP address
FLAT_INTERFACE=eth0
FIXED_RANGE=10.254.1.0/24 # Change this to whatever your network is
NETWORK_GATEWAY=10.254.1.1 # Change this for your network
MULTI_HOST=1
SERVICE_HOST=192.168.42.1 # Change this to the IP of your controller node
MYSQL_HOST=$SERVICE_HOST
RABBIT_HOST=$SERVICE_HOST
GLANCE_HOSTPORT=${SERVICE_HOST}:9292
DATABASE_TYPE=mysql
# Enable services (including neutron)
ENABLED_SERVICES=n-cpu,n-api-meta,c-vol,q-agt
NOVA_VNC_ENABLED=True
NOVNCPROXY_URL="http://$SERVICE_HOST:6080/vnc_auto.html"
VNCSERVER_LISTEN=0.0.0.0
VNCSERVER_PROXYCLIENT_ADDRESS=$HOST_IP
NOVA_INSTANCES_PATH=/opt/stack/data/instances
# Enable the Ceilometer plugin for the compute agent
enable_plugin ceilometer git://git.openstack.org/openstack/ceilometer
disable_service ceilometer-acentral,ceilometer-collector,ceilometer-api
LOGFILE=$DEST/logs/stack.sh.log
LOGDAYS=2
[[post-config|$NOVA_CONF]]
[DEFAULT]
compute_monitors=cpu.virt_driver

View File

@@ -0,0 +1,48 @@
# Sample ``local.conf`` for controller node for Watcher development
# NOTE: Copy this file to the root DevStack directory for it to work properly.
[[local|localrc]]
ADMIN_PASSWORD=nomoresecrete
DATABASE_PASSWORD=stackdb
RABBIT_PASSWORD=stackqueue
SERVICE_PASSWORD=$ADMIN_PASSWORD
SERVICE_TOKEN=azertytoken
HOST_IP=192.168.42.1 # Change this to your controller node IP address
FLAT_INTERFACE=eth0
FIXED_RANGE=10.254.1.0/24 # Change this to whatever your network is
NETWORK_GATEWAY=10.254.1.1 # Change this for your network
MULTI_HOST=1
# This is the controller node, so disable nova-compute
disable_service n-cpu
# Disable nova-network and use neutron instead
disable_service n-net
ENABLED_SERVICES+=,q-svc,q-dhcp,q-meta,q-agt,q-l3,neutron
# Enable remote console access
enable_service n-cauth
# Enable the Watcher plugin
enable_plugin watcher git://git.openstack.org/openstack/watcher
# Enable the Ceilometer plugin
enable_plugin ceilometer git://git.openstack.org/openstack/ceilometer
# This is the controller node, so disable the ceilometer compute agent
disable_service ceilometer-acompute
LOGFILE=$DEST/logs/stack.sh.log
LOGDAYS=2
[[post-config|$NOVA_CONF]]
[DEFAULT]
compute_monitors=cpu.virt_driver
[[post-config|$WATCHER_CONF]]
[watcher_goals]
goals=BASIC_CONSOLIDATION:basic,DUMMY:dummy

53
devstack/plugin.sh Normal file
View File

@@ -0,0 +1,53 @@
#!/bin/bash
#
# plugin.sh - DevStack plugin script to install watcher
# Save trace setting
_XTRACE_WATCHER_PLUGIN=$(set +o | grep xtrace)
set -o xtrace
echo_summary "watcher's plugin.sh was called..."
source $DEST/watcher/devstack/lib/watcher
# Show all of defined environment variables
(set -o posix; set)
if is_service_enabled watcher-api watcher-decision-engine watcher-applier; then
if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then
echo_summary "Before Installing watcher"
elif [[ "$1" == "stack" && "$2" == "install" ]]; then
echo_summary "Installing watcher"
install_watcher
LIBS_FROM_GIT="${LIBS_FROM_GIT},python-watcherclient"
install_watcherclient
cleanup_watcher
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
echo_summary "Configuring watcher"
configure_watcher
if is_service_enabled key; then
create_watcher_accounts
fi
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
# Initialize watcher
init_watcher
# Start the watcher components
echo_summary "Starting watcher"
start_watcher
fi
if [[ "$1" == "unstack" ]]; then
stop_watcher
fi
if [[ "$1" == "clean" ]]; then
cleanup_watcher
fi
fi
# Restore xtrace
$_XTRACE_WATCHER_PLUGIN

9
devstack/settings Normal file
View File

@@ -0,0 +1,9 @@
# DevStack settings
# Make sure rabbit is enabled
enable_service rabbit
# Enable Watcher services
enable_service watcher-api
enable_service watcher-decision-engine
enable_service watcher-applier

385
doc/source/architecture.rst Normal file
View File

@@ -0,0 +1,385 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
.. _architecture:
===================
System Architecture
===================
This page presents the current technical Architecture of the Watcher system.
.. _architecture_overview:
Overview
========
Below you will find a diagram, showing the main components of Watcher:
.. image:: ./images/architecture.svg
:width: 100%
.. _components_definition:
Components
==========
.. _amqp_bus_definition:
AMQP Bus
--------
The AMQP message bus handles internal asynchronous communications between the
different Watcher components.
.. _cluster_history_db_definition:
Cluster History Database
------------------------
This component stores the data related to the
:ref:`Cluster History <cluster_history_definition>`.
It can potentially rely on any appropriate storage system (InfluxDB, OpenTSDB,
MongoDB,...) but will probably be more performant when using
`Time Series Databases <https://en.wikipedia.org/wiki/Time_series_database>`_
which are optimized for handling time series data, which are arrays of numbers
indexed by time (a datetime or a datetime range).
.. _cluster_model_db_definition:
Cluster Model Database
------------------------
This component stores the data related to the
:ref:`Cluster Data Model <cluster_data_model_definition>`.
.. _archi_watcher_api_definition:
Watcher API
-----------
This component implements the REST API provided by the Watcher system to the
external world.
It enables the :ref:`Administrator <administrator_definition>` of a
:ref:`Cluster <cluster_definition>` to control and monitor the Watcher system
via any interaction mechanism connected to this API:
- :ref:`CLI <archi_watcher_cli_definition>`
- Horizon plugin
- Python SDK
You can also read the detailed description of `Watcher API`_.
.. _archi_watcher_applier_definition:
Watcher Applier
---------------
This component is in charge of executing the
:ref:`Action Plan <action_plan_definition>` built by the
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>`.
It connects to the :ref:`message bus <amqp_bus_definition>` and launches the
:ref:`Action Plan <action_plan_definition>` whenever a triggering message is
received on a dedicated AMQP queue.
The triggering message contains the Action Plan UUID.
It then gets the detailed information about the
:ref:`Action Plan <action_plan_definition>` from the
:ref:`Watcher Database <watcher_database_definition>` which contains the list
of :ref:`Actions <action_definition>` to launch.
It then loops on each :ref:`Action <action_definition>`, gets the associated
class and calls the execute() method of this class.
Most of the time, this method will first request a token to the Keystone API
and if it is allowed, sends a request to the REST API of the OpenStack service
which handles this kind of :ref:`atomic Action <action_definition>`.
Note that as soon as :ref:`Watcher Applier <watcher_applier_definition>` starts
handling a given :ref:`Action <action_definition>` from the list, a
notification message is sent on the :ref:`message bus <amqp_bus_definition>`
indicating that the state of the action has changed to **ONGOING**.
If the :ref:`Action <action_definition>` is successful,
the :ref:`Watcher Applier <watcher_applier_definition>` sends a notification
message on :ref:`the bus <amqp_bus_definition>` informing the other components
of this.
If the :ref:`Action <action_definition>` fails, the
:ref:`Watcher Applier <watcher_applier_definition>` tries to rollback to the
previous state of the :ref:`Managed resource <managed_resource_definition>`
(i.e. before the command was sent to the underlying OpenStack service).
.. _archi_watcher_cli_definition:
Watcher CLI
-----------
The watcher command-line interface (CLI) can be used to interact with the
Watcher system in order to control it or to know its current status.
Please, read `the detailed documentation about Watcher CLI
<https://factory.b-com.com/www/watcher/doc/python-watcherclient/>`_.
.. _archi_watcher_dashboard_definition:
Watcher Dashboard
-----------------
The Watcher Dashboard can be used to interact with the Watcher system through
Horizon in order to control it or to know its current status.
Please, read `the detailed documentation about Watcher Dashboard
<https://factory.b-com.com/www/watcher/doc/watcher-dashboard/>`_.
.. _archi_watcher_database_definition:
Watcher Database
----------------
This database stores all the Watcher domain objects which can be requested
by the :ref:`Watcher API <archi_watcher_api_definition>` or the
:ref:`Watcher CLI <archi_watcher_cli_definition>`:
- :ref:`Audit templates <audit_template_definition>`
- :ref:`Audits <audit_definition>`
- :ref:`Action plans <action_plan_definition>`
- :ref:`Actions <action_definition>`
- :ref:`Goals <goal_definition>`
The Watcher domain being here "*optimization of some resources provided by an
OpenStack system*".
.. _archi_watcher_decision_engine_definition:
Watcher Decision Engine
-----------------------
This component is responsible for computing a set of potential optimization
:ref:`Actions <action_definition>` in order to fulfill
the :ref:`Goal <goal_definition>` of an :ref:`Audit <audit_definition>`.
It first reads the parameters of the :ref:`Audit <audit_definition>` from the
associated :ref:`Audit Template <audit_template_definition>` and knows the
:ref:`Goal <goal_definition>` to achieve.
It then selects the most appropriate :ref:`Strategy <strategy_definition>`
depending on how Watcher was configured for this :ref:`Goal <goal_definition>`.
The :ref:`Strategy <strategy_definition>` is then dynamically loaded (via
`stevedore <https://github.com/openstack/stevedore/>`_). The
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>` calls the
**execute()** method of the :ref:`Strategy <strategy_definition>` class which
generates a solution composed of a set of :ref:`Actions <action_definition>`.
These :ref:`Actions <action_definition>` are scheduled in time by the
:ref:`Watcher Planner <watcher_planner_definition>` (i.e., it generates an
:ref:`Action Plan <action_plan_definition>`).
In order to compute the potential :ref:`Solution <solution_definition>` for the
Audit, the :ref:`Strategy <strategy_definition>` relies on two sets of data:
- the current state of the
:ref:`Managed resources <managed_resource_definition>`
(e.g., the data stored in the Nova database)
- the data stored in the
:ref:`Cluster History Database <cluster_history_db_definition>`
which provides information about the past of the
:ref:`Cluster <cluster_definition>`
So far, only one :ref:`Strategy <strategy_definition>` can be associated to a
given :ref:`Goal <goal_definition>` via the main Watcher configuration file.
.. _data_model:
Data model
==========
The following diagram shows the data model of Watcher, especially the
functional dependency of objects from the actors (Admin, Customer) point of
view (Goals, Audits, Action Plans, ...):
.. image:: ./images/functional_data_model.svg
:width: 100%
.. _sequence_diagrams:
Sequence diagrams
=================
The following paragraph shows the messages exchanged between the different
components of Watcher for the most often used scenarios.
.. _sequence_diagrams_create_audit_template:
Create a new Audit Template
---------------------------
The :ref:`Administrator <administrator_definition>` first creates an
:ref:`Audit template <audit_template_definition>` providing at least the
following parameters:
- A name
- A goal to achieve
.. image:: ./images/sequence_create_audit_template.png
:width: 100%
The `Watcher API`_ just makes sure that the goal exists (i.e. it is declared
in the Watcher configuration file) and stores a new audit template in the
:ref:`Watcher Database <watcher_database_definition>`.
.. _sequence_diagrams_create_and_launch_audit:
Create and launch a new Audit
-----------------------------
The :ref:`Administrator <administrator_definition>` can then launch a new
:ref:`Audit <audit_definition>` by providing at least the unique UUID of the
previously created :ref:`Audit template <audit_template_definition>`:
.. image:: ./images/sequence_create_and_launch_audit.png
:width: 100%
A message is sent on the :ref:`AMQP bus <amqp_bus_definition>` which triggers
the Audit in the
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>`:
.. image:: ./images/sequence_trigger_audit_in_decision_engine.png
:width: 100%
The :ref:`Watcher Decision Engine <watcher_decision_engine_definition>` reads
the Audit parameters from the
:ref:`Watcher Database <watcher_database_definition>`. It instantiates the
appropriate :ref:`Strategy <strategy_definition>` (using entry points)
associated to the :ref:`Goal <goal_definition>` of the
:ref:`Audit <audit_definition>` (it uses the information of the Watcher
configuration file to find the mapping between the
:ref:`Goal <goal_definition>` and the :ref:`Strategy <strategy_definition>`
python class).
The :ref:`Watcher Decision Engine <watcher_decision_engine_definition>` also
builds the :ref:`Cluster Data Model <cluster_data_model_definition>`. This
data model is needed by the :ref:`Strategy <strategy_definition>` to know the
current state and topology of the audited
:ref:`Openstack cluster <cluster_definition>`.
The :ref:`Watcher Decision Engine <watcher_decision_engine_definition>` calls
the **execute()** method of the instantiated
:ref:`Strategy <strategy_definition>` and provides the data model as an input
parameter. This method computes a :ref:`Solution <strategy_definition>` to
achieve the goal and returns it to the
:ref:`Decision Engine <watcher_decision_engine_definition>`. At this point,
actions are not scheduled yet.
The :ref:`Watcher Decision Engine <watcher_decision_engine_definition>`
dynamically loads the :ref:`Watcher Planner <watcher_planner_definition>`
implementation which is configured in Watcher (via entry points) and calls the
**schedule()** method of this class with the solution as an input parameter.
This method finds an appropriate scheduling of
:ref:`Actions <action_definition>` taking into account some scheduling rules
(such as priorities between actions).
It generates a new :ref:`Action Plan <action_plan_definition>` with status
**RECOMMENDED** and saves it into the
:ref:`Watcher Database <watcher_database_definition>`. The saved action plan is
now a scheduled flow of actions.
If every step executed successfully, the
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>` updates
the current status of the Audit to **SUCCEEDED** in the
:ref:`Watcher Database <watcher_database_definition>` and sends a notification
on the bus to inform other components that the :ref:`Audit <audit_definition>`
was successful.
.. _sequence_diagrams_launch_action_plan:
Launch Action Plan
------------------
The :ref:`Administrator <administrator_definition>` can then launch the
recommended :ref:`Action Plan <action_plan_definition>`:
.. image:: ./images/sequence_launch_action_plan.png
:width: 100%
A message is sent on the :ref:`AMQP bus <amqp_bus_definition>` which triggers
the :ref:`Action Plan <action_plan_definition>` in the
:ref:`Watcher Applier <watcher_applier_definition>`:
.. image:: ./images/sequence_launch_action_plan_in_applier.png
:width: 100%
The :ref:`Watcher Applier <watcher_applier_definition>` will get the
description of the flow of :ref:`Actions <action_definition>` from the
:ref:`Watcher Database <watcher_database_definition>` and for each
:ref:`Action <action_definition>` it will instantiate a corresponding
:ref:`Action <action_definition>` handler python class.
The :ref:`Watcher Applier <watcher_applier_definition>` will then call the
following methods of the :ref:`Action <action_definition>` handler:
- **validate_parameters()**: this method will make sure that all the
provided input parameters are valid:
- If all parameters are valid, the Watcher Applier moves on to the next
step.
- If it is not, an error is raised and the action is not executed. A
notification is sent on the bus informing other components of the
failure.
- **preconditions()**: this method will make sure that all conditions are met
before executing the action (for example, it makes sure that an instance
still exists before trying to migrate it).
- **execute()**: this method is what triggers real commands on other
OpenStack services (such as Nova, ...) in order to change target resource
state. If the action is successfully executed, a notification message is
sent on the bus indicating that the new state of the action is
**SUCCEEDED**.
If every action of the action flow has been executed successfully, a
notification is sent on the bus to indicate that the whole
:ref:`Action Plan <action_plan_definition>` has **SUCCEEDED**.
.. _state_machine_diagrams:
State Machine diagrams
======================
.. _audit_state_machine:
Audit State Machine
-------------------
The following diagram shows the different possible states of an
:ref:`Audit <audit_definition>` and what event makes the state change to a new
value:
.. image:: ./images/audit_state_machine.png
:width: 100%
.. _action_plan_state_machine:
Action Plan State Machine
-------------------------
The following diagram shows the different possible states of an
:ref:`Action Plan <action_plan_definition>` and what event makes the state
change to a new value:
.. image:: ./images/action_plan_state_machine.png
:width: 100%
.. _Watcher API: webapi/v1.html

View File

@@ -11,24 +11,20 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
from watcher import version as watcher_version
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc',
# 'sphinx.ext.intersphinx',
'sphinx.ext.viewcode',
'sphinxcontrib.httpdomain',
'sphinxcontrib.pecanwsme.rest',
'wsmeext.sphinxext',
'oslosphinx'
'oslosphinx',
'watcher.doc',
]
wsme_protocols = ['restjson']
@@ -45,8 +41,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'watcher'
copyright = u'2015, OpenStack Foundation'
project = u'Watcher'
copyright = u'OpenStack Foundation'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -61,6 +57,14 @@ version = watcher_version.version_info.version_string()
# A list of ignored prefixes for module index sorting.
modindex_common_prefix = ['watcher.']
exclude_patterns = [
# The man directory includes some snippet files that are included
# in other documents during the build but that should not be
# included in the toctree themselves, so tell Sphinx to ignore
# them when scanning for input files.
'man/footer.rst',
'man/general-options.rst',
]
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
@@ -72,6 +76,22 @@ add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for man page output --------------------------------------------
# Grouping the document tree for man pages.
# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual'
man_pages = [
('man/watcher-api', 'watcher-api', u'Watcher API Server',
[u'OpenStack'], 1),
('man/watcher-applier', 'watcher-applier', u'Watcher Applier',
[u'OpenStack'], 1),
('man/watcher-db-manage', 'watcher-db-manage',
u'Watcher Db Management Utility', [u'OpenStack'], 1),
('man/watcher-decision-engine', 'watcher-decision-engine',
u'Watcher Decision Engine', [u'OpenStack'], 1),
]
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with

View File

@@ -1,4 +1,8 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
===================
Configuring Watcher
@@ -30,6 +34,8 @@ The Watcher service includes the following components:
- ``watcher-applier``: applies the action plan.
- `python-watcherclient`_: A command-line interface (CLI) for interacting with
the Watcher service.
- `watcher-dashboard`_: An Horizon plugin for interacting with the Watcher
service.
Additionally, the Bare Metal service has certain external dependencies, which
are very similar to other OpenStack services:
@@ -48,6 +54,7 @@ additional functionality:
.. _`ceilometer`: https://github.com/openstack/ceilometer
.. _`nova`: https://github.com/openstack/nova
.. _`python-watcherclient`: https://github.com/openstack/python-watcherclient
.. _`watcher-dashboard`: https://github.com/openstack/watcher-dashboard
.. _`watcher metering`: https://github.com/b-com/watcher-metering
.. _`RabbitMQ`: https://www.rabbitmq.com/
@@ -162,11 +169,17 @@ The configuration file is organized into the following sections:
* ``[api]`` - API server configuration
* ``[database]`` - SQL driver configuration
* ``[keystone_authtoken]`` - Keystone Authentication plugin configuration
* ``[watcher_clients_auth]`` - Keystone auth configuration for clients
* ``[watcher_applier]`` - Watcher Applier module configuration
* ``[watcher_decision_engine]`` - Watcher Decision Engine module configuration
* ``[watcher_goals]`` - Goals mapping configuration
* ``[watcher_strategies]`` - Strategy configuration
* ``[oslo_messaging_rabbit]`` - Oslo Messaging RabbitMQ driver configuration
* ``[ceilometer_client]`` - Ceilometer client configuration
* ``[cinder_client]`` - Cinder client configuration
* ``[glance_client]`` - Glance client configuration
* ``[nova_client]`` - Nova client configuration
* ``[neutron_client]`` - Neutron client configuration
The Watcher configuration file is expected to be named
``watcher.conf``. When starting Watcher, you can specify a different
@@ -233,39 +246,105 @@ so that the watcher service is configured for your needs.
#rabbit_port = 5672
#. Configure the Watcher Service to use these credentials with the Identity
Service. Replace IDENTITY_IP with the IP of the Identity server, and
replace WATCHER_PASSWORD with the password you chose for the ``watcher``
user in the Identity Service::
#. Watcher API shall validate the token provided by every incoming request,
via keystonemiddleware, which requires the Watcher service to be configured
with the right credentials for the Identity service.
[keystone_authtoken]
In the configuration section here below:
# Complete public Identity API endpoint (string value)
#auth_uri=<None>
auth_uri=http://IDENTITY_IP:5000/v3
* replace IDENTITY_IP with the IP of the Identity server
* replace WATCHER_PASSWORD with the password you chose for the ``watcher``
user
* replace KEYSTONE_SERVICE_PROJECT_NAME with the name of project created
for OpenStack services (e.g. ``service``) ::
# Complete admin Identity API endpoint. This should specify the
# unversioned root endpoint e.g. https://localhost:35357/ (string
# value)
#identity_uri = <None>
identity_uri = http://IDENTITY_IP:5000
[keystone_authtoken]
# Keystone account username (string value)
#admin_user=<None>
admin_user=watcher
# Authentication type to load (unknown value)
# Deprecated group/name - [DEFAULT]/auth_plugin
#auth_type = <None>
auth_type = password
# Keystone account password (string value)
#admin_password=<None>
admin_password=WATCHER_DBPASSWORD
# Authentication URL (unknown value)
#auth_url = <None>
auth_url = http://IDENTITY_IP:35357
# Keystone service account tenant name to validate user tokens
# (string value)
#admin_tenant_name=admin
admin_tenant_name=KEYSTONE_SERVICE_PROJECT_NAME
# Username (unknown value)
# Deprecated group/name - [DEFAULT]/username
#username = <None>
username=watcher
# Directory used to cache files related to PKI tokens (string
# value)
#signing_dir=<None>
# User's password (unknown value)
#password = <None>
password = WATCHER_PASSWORD
# Domain ID containing project (unknown value)
#project_domain_id = <None>
project_domain_id = default
# User's domain id (unknown value)
#user_domain_id = <None>
user_domain_id = default
# Project name to scope to (unknown value)
# Deprecated group/name - [DEFAULT]/tenant-name
#project_name = <None>
project_name = KEYSTONE_SERVICE_PROJECT_NAME
#. Watcher's decision engine and applier interact with other OpenStack
projects through those projects' clients. In order to instantiate these
clients, Watcher needs to request a new session from the Identity service
using the right credentials.
In the configuration section here below:
* replace IDENTITY_IP with the IP of the Identity server
* replace WATCHER_PASSWORD with the password you chose for the ``watcher``
user
* replace KEYSTONE_SERVICE_PROJECT_NAME with the name of project created
for OpenStack services (e.g. ``service``) ::
[watcher_clients_auth]
# Authentication type to load (unknown value)
# Deprecated group/name - [DEFAULT]/auth_plugin
#auth_type = <None>
auth_type = password
# Authentication URL (unknown value)
#auth_url = <None>
auth_url = http://IDENTITY_IP:35357
# Username (unknown value)
# Deprecated group/name - [DEFAULT]/username
#username = <None>
username=watcher
# User's password (unknown value)
#password = <None>
password = WATCHER_PASSWORD
# Domain ID containing project (unknown value)
#project_domain_id = <None>
project_domain_id = default
# User's domain id (unknown value)
#user_domain_id = <None>
user_domain_id = default
# Project name to scope to (unknown value)
# Deprecated group/name - [DEFAULT]/tenant-name
#project_name = <None>
project_name = KEYSTONE_SERVICE_PROJECT_NAME
#. Configure the clients to use a specific version if desired. For example, to
configure Watcher to use a Nova client with version 2.1, use::
[nova_client]
# Version of Nova API to use in novaclient. (string value)
#api_version = 2
api_version = 2.1
#. Create the Watcher Service database tables::
@@ -314,3 +393,33 @@ pick any database system you prefer.
The original implementation has been based on MongoDB but you can create your
own storage driver using whatever technology you want.
For more information : https://wiki.openstack.org/wiki/Gnocchi
Workers
=======
You can define a number of workers for the Decision Engine and the Applier.
If you want to create and run more audits simultaneously, you have to raise
the number of workers used by the Decision Engine::
[watcher_decision_engine]
...
# The maximum number of threads that can be used to execute strategies
# (integer value)
#max_workers = 2
If you want to execute simultaneously more recommended action plans, you
have to raise the number of workers used by the Applier::
[watcher_applier]
...
# Number of workers for applier, default value is 1. (integer value)
# Minimum value: 1
#workers = 1

View File

@@ -1,3 +1,9 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
==================
Installing Watcher
==================

View File

@@ -1,20 +1,35 @@
.. _user-guide:
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
=================================
Welcome to the Watcher User Guide
=================================
https://creativecommons.org/licenses/by/3.0/
.. _user-guide:
==================
Watcher User Guide
==================
See the
`architecture page <https://factory.b-com.com/www/watcher/doc/watcher/architecture.html>`_
for an architectural overview of the different components of Watcher and how
they fit together.
In the `architecture <https://wiki.openstack.org/wiki/WatcherArchitecture>`_
you got information about how it works.
In this guide we're going to take you through the fundamentals of using
Watcher.
The following diagram shows the main interactions between the
:ref:`Administrator <administrator_definition>` and the Watcher system:
.. image:: ../images/sequence_overview_watcher_usage.png
:width: 100%
Getting started with Watcher
----------------------------
This guide assumes you have a working installation of Watcher. If you get
"*watcher: command not found*" you may have to verify your installation.
Please refer to the :doc:`installation guide <installation>`.
Please refer to the `installation guide`_.
In order to use Watcher, you have to configure your credentials suitable for
watcher command-line tools.
If you need help on a specific command, you can use:
@@ -23,6 +38,12 @@ If you need help on a specific command, you can use:
$ watcher help COMMAND
If you want to deploy Watcher in Horizon, please refer to the `Watcher Horizon
plugin installation guide`_.
.. _`installation guide`: https://factory.b-com.com/www/watcher/doc/python-watcherclient
.. _`Watcher Horizon plugin installation guide`: https://factory.b-com.com/www/watcher/doc/watcher-dashboard/deploy/installation.html
Seeing what the Watcher CLI can do ?
------------------------------------
We can see all of the commands available with Watcher CLI by running the
@@ -43,7 +64,7 @@ This goal should be declared in the Watcher service configuration file
.. code:: bash
$ watcher audit-template-create my_first_audit SERVERS_CONSOLIDATION
$ watcher audit-template-create my_first_audit DUMMY
If you get "*You must provide a username via either --os-username or via
env[OS_USERNAME]*" you may have to verify your credentials.
@@ -84,7 +105,7 @@ configuration file.
$ watcher action-plan-list --audit <the_audit_uuid>
- Have a look on the list of optimization :ref:`actions <action_definition>`
contained in this new :ref:`action plan <action_plan_definition>`:
contained in this new :ref:`action plan <action_plan_definition>`:
.. code:: bash
@@ -113,3 +134,4 @@ You can also obtain more detailed information about a specific action:
.. code:: bash
$ watcher action-show <the_action_uuid>

View File

@@ -1,9 +0,0 @@
.. _architecture:
===================
System Architecture
===================
Please go to `Wiki Watcher Architecture <https://wiki.openstack.org/wiki/WatcherArchitecture>`_
.. _API service: ../webapi/v1.html

View File

@@ -1,8 +1,14 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
.. _contributing:
======================
=======================
Contributing to Watcher
======================
=======================
If you're interested in contributing to the Watcher project,
the following will help get you started.
@@ -37,14 +43,14 @@ notifications of important events.
Project Hosting Details
-------------------------
-----------------------
Bug tracker
http://launchpad.net/watcher
Mailing list (prefix subjects with ``[watcher]`` for faster responses)
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
Wiki
http://wiki.openstack.org/Watcher
@@ -54,3 +60,12 @@ Code Hosting
Code Review
https://review.openstack.org/#/q/status:open+project:openstack/watcher,n,z
IRC Channel
``#openstack-watcher`` (changelog_)
Weekly Meetings
on Wednesdays at 14:00 UTC in the ``#openstack-meeting-4`` IRC
channel (`meetings logs`_)
.. _changelog: http://eavesdrop.openstack.org/irclogs/%23openstack-watcher/
.. _meetings logs: http://eavesdrop.openstack.org/meetings/watcher/

204
doc/source/dev/devstack.rst Normal file
View File

@@ -0,0 +1,204 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
=============================================
Set up a development environment via DevStack
=============================================
Watcher is currently able to optimize compute resources - specifically Nova
compute hosts - via operations such as live migrations. In order for you to
fully be able to exercise what Watcher can do, it is necessary to have a
multinode environment to use.
You can set up the Watcher services quickly and easily using a Watcher
DevStack plugin. See `PluginModelDocs`_ for information on DevStack's plugin
model. To enable the Watcher plugin with DevStack, add the following to the
`[[local|localrc]]` section of your controller's `local.conf` to enable the
Watcher plugin::
enable_plugin watcher git://git.openstack.org/openstack/watcher
For more detailed instructions, see `Detailed DevStack Instructions`_. Check
out the `DevStack documentation`_ for more information regarding DevStack.
.. _PluginModelDocs: http://docs.openstack.org/developer/devstack/plugins.html
.. _DevStack documentation: http://docs.openstack.org/developer/devstack/
Detailed DevStack Instructions
==============================
#. Obtain N (where N >= 1) servers (virtual machines preferred for DevStack).
One of these servers will be the controller node while the others will be
compute nodes. N is preferably >= 3 so that you have at least 2 compute
nodes, but in order to stand up the Watcher services only 1 server is
needed (i.e., no computes are needed if you want to just experiment with
the Watcher services). These servers can be VMs running on your local
machine via VirtualBox if you prefer. DevStack currently recommends that
you use Ubuntu 14.04 LTS. The servers should also have connections to the
same network such that they are all able to communicate with one another.
#. For each server, clone the DevStack repository and create the stack user::
sudo apt-get update
sudo apt-get install git
git clone https://git.openstack.org/openstack-dev/devstack
sudo ./devstack/tools/create-stack-user.sh
Now you have a stack user that is used to run the DevStack processes. You
may want to give your stack user a password to allow SSH via a password::
sudo passwd stack
#. Switch to the stack user and clone the DevStack repo again::
sudo su stack
cd ~
git clone https://git.openstack.org/openstack-dev/devstack
#. For each compute node, copy the provided `local.conf.compute`_ example file
to the compute node's system at ~/devstack/local.conf. Make sure the
HOST_IP and SERVICE_HOST values are changed appropriately - i.e., HOST_IP
is set to the IP address of the compute node and SERVICE_HOST is set to the
IP address of the controller node.
If you need specific metrics collected (or want to use something other
than Ceilometer), be sure to configure it. For example, in the
`local.conf.compute`_ example file, the appropriate ceilometer plugins and
services are enabled and disabled. If you were using something other than
Ceilometer, then you would likely want to configure it likewise. The
example file also sets the compute monitors nova configuration option to
use the CPU virt driver. If you needed other metrics, it may be necessary
to configure similar configuration options for the projects providing those
metrics.
#. For the controller node, copy the provided `local.conf.controller`_ example
file to the controller node's system at ~/devstack/local.conf. Make sure
the HOST_IP value is changed appropriately - i.e., HOST_IP is set to the IP
address of the controller node.
Note: if you want to use another Watcher git repository (such as a local
one), then change the enable plugin line::
enable_plugin watcher <your_local_git_repo> [optional_branch]
If you do this, then the Watcher DevStack plugin will try to pull the
python-watcherclient repo from <your_local_git_repo>/../, so either make
sure that is also available or specify WATCHERCLIENT_REPO in the local.conf
file.
Note: if you want to use a specific branch, specify WATCHER_BRANCH in the
local.conf file. By default it will use the master branch.
#. Start stacking from the controller node::
./devstack/stack.sh
#. Start stacking on each of the compute nodes using the same command.
#. Configure the environment for live migration via NFS. See the
`Multi-Node DevStack Environment`_ section for more details.
.. _local.conf.controller: https://github.com/openstack/watcher/tree/master/devstack/local.conf.controller
.. _local.conf.compute: https://github.com/openstack/watcher/tree/master/devstack/local.conf.compute
Multi-Node DevStack Environment
===============================
Since deploying Watcher with only a single compute node is not very useful, a
few tips are given here for enabling a multi-node environment with live
migration.
Configuring NFS Server
----------------------
If you would like to use live migration for shared storage, then the controller
can serve as the NFS server if needed::
sudo apt-get install nfs-kernel-server
sudo mkdir -p /nfs/instances
sudo chown stack:stack /nfs/instances
Add an entry to `/etc/exports` with the appropriate gateway and netmask
information::
/nfs/instances <gateway>/<netmask>(rw,fsid=0,insecure,no_subtree_check,async,no_root_squash)
Export the NFS directories::
sudo exportfs -ra
Make sure the NFS server is running::
sudo service nfs-kernel-server status
If the server is not running, then start it::
sudo service nfs-kernel-server start
Configuring NFS on Compute Node
-------------------------------
Each compute node needs to use the NFS server to hold the instance data::
sudo apt-get install rpcbind nfs-common
mkdir -p /opt/stack/data/instances
sudo mount <nfs-server-ip>:/nfs/instances /opt/stack/data/instances
If you would like to have the NFS directory automatically mounted on reboot,
then add the following to `/etc/fstab`::
<nfs-server-ip>:/nfs/instances /opt/stack/data/instances nfs auto 0 0
Edit `/etc/libvirt/libvirtd.conf` to make sure the following values are set::
listen_tls = 0
listen_tcp = 1
auth_tcp = "none"
Edit `/etc/default/libvirt-bin`::
libvirt_opts="-d -l"
Restart the libvirt service::
sudo service libvirt-bin restart
Setting up SSH keys between compute nodes to enable live migration
------------------------------------------------------------------
In order for live migration to work, SSH keys need to be exchanged between
each compute node:
1. The SOURCE root user's public RSA key (likely in /root/.ssh/id_rsa.pub)
needs to be in the DESTINATION stack user's authorized_keys file
(~stack/.ssh/authorized_keys). This can be accomplished by manually
copying the contents from the file on the SOURCE to the DESTINATION. If
you have a password configured for the stack user, then you can use the
following command to accomplish the same thing::
ssh-copy-id -i /root/.ssh/id_rsa.pub stack@DESTINATION
2. The DESTINATION host's public ECDSA key (/etc/ssh/ssh_host_ecdsa_key.pub)
needs to be in the SOURCE root user's known_hosts file
(/root/.ssh/known_hosts). This can be accomplished by running the
following on the SOURCE machine (hostname must be used)::
ssh-keyscan -H DEST_HOSTNAME | sudo tee -a /root/.ssh/known_hosts
In essence, this means that every compute node's root user's public RSA key
must exist in every other compute node's stack user's authorized_keys file and
every compute node's public ECDSA key needs to be in every other compute
node's root user's known_hosts file.
Environment final checkup
-------------------------
If you are willing to make sure everything is in order in your DevStack
environment, you can run the Watcher Tempest tests which will validate its API
but also that you can perform the typical Watcher workflows. To do so, have a
look at the :ref:`Tempest tests <tempest_tests>` section which will explain to
you how to run them.

View File

@@ -1,8 +1,14 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
============================================
Setting up a Watcher development environment
============================================
https://creativecommons.org/licenses/by/3.0/
.. _watcher_developement_environment:
=========================================
Set up a development environment manually
=========================================
This document describes getting the source from watcher `Git repository`_
for development purposes.
@@ -71,17 +77,13 @@ extension, PyPi) cannot satisfy. These dependencies should be installed
prior to using `pip`, and the installation method may vary depending on
your platform.
* Ubuntu 14.04:
* Ubuntu 14.04::
.. code-block:: bash
$ sudo apt-get install python-dev libssl-dev libmysqlclient-dev libffi-dev
$ sudo apt-get install python-dev libssl-dev libmysqlclient-dev libffi-dev
* Fedora 19+::
* Fedora 19+:
.. code-block:: bash
$ sudo yum install openssl-devel libffi-devel mysql-devel
$ sudo yum install openssl-devel libffi-devel mysql-devel
PyPi Packages and VirtualEnv
@@ -143,34 +145,13 @@ You should then be able to `import watcher` using Python without issue:
If you can import watcher without a traceback, you should be ready to develop.
Run Watcher unit tests
======================
Run Watcher tests
=================
All unit tests should be run using tox. To run the unit tests under py27 and
also run the pep8 tests:
Watcher provides both :ref:`unit tests <unit_tests>` and
:ref:`functional/tempest tests <tempest_tests>`. Please refer to :doc:`testing`
to understand how to run them.
.. code-block:: bash
$ workon watcher
(watcher) $ pip install tox
(watcher) $ cd watcher
(watcher) $ tox -epep8 -epy27
You may pass options to the test programs using positional arguments. To run a
specific unit test, this passes the -r option and desired test (regex string)
to os-testr:
.. code-block:: bash
$ workon watcher
(watcher) $ tox -epy27 -- tests.api
When you're done, deactivate the virtualenv:
.. code-block:: bash
$ deactivate
Build the Watcher documentation
===============================
@@ -269,6 +250,11 @@ interface.
.. _`python-watcherclient`: https://github.com/openstack/python-watcherclient
There is also an Horizon plugin for Watcher `watcher-dashboard`_ which
allows to interact with Watcher through a web-based interface.
.. _`watcher-dashboard`: https://github.com/openstack/watcher-dashboard
Exercising the Watcher Services locally
=======================================
@@ -283,4 +269,3 @@ template files to easily play with Watcher services within a minimal OpenStack
isolated environment (Identity, Message Bus, SQL database, Horizon, ...).
.. _`watcher-tools`: https://github.com/b-com/watcher-tools

View File

@@ -1,703 +0,0 @@
..
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.
==========
Glossary
==========
.. glossary::
:sorted:
This page explains the different terms used in the Watcher system.
They are sorted in alphabetical order.
.. _action_definition:
Action
======
An :ref:`Action <action_definition>` is what enables Watcher to transform the
current state of a :ref:`Cluster <cluster_definition>` after an
:ref:`Audit <audit_definition>`.
An :ref:`Action <action_definition>` is an atomic task which changes the
current state of a target :ref:`Managed resource <managed_resource_definition>`
of the OpenStack :ref:`Cluster <cluster_definition>` such as:
- Live migration of an instance from one compute node to another compute
node with Nova
- Changing the power level of a compute node (ACPI level, ...)
- Changing the current state of an hypervisor (enable or disable) with Nova
In most cases, an :ref:`Action <action_definition>` triggers some concrete
commands on an existing OpenStack module (Nova, Neutron, Cinder, Ironic, etc.).
An :ref:`Action <action_definition>` has a life-cycle and its current state may
be one of the following:
- **PENDING** : the :ref:`Action <action_definition>` has not been executed
yet by the :ref:`Watcher Applier <watcher_applier_definition>`
- **ONGOING** : the :ref:`Action <action_definition>` is currently being
processed by the :ref:`Watcher Applier <watcher_applier_definition>`
- **SUCCEEDED** : the :ref:`Action <action_definition>` has been executed
successfully
- **FAILED** : an error occured while trying to execute the
:ref:`Action <action_definition>`
- **DELETED** : the :ref:`Action <action_definition>` is still stored in the
:ref:`Watcher database <watcher_database_definition>` but is not returned
any more through the Watcher APIs.
- **CANCELLED** : the :ref:`Action <action_definition>` was in **PENDING** or
**ONGOING** state and was cancelled by the
:ref:`Administrator <administrator_definition>`
.. _action_plan_definition:
Action Plan
===========
An :ref:`Action Plan <action_plan_definition>` is a flow of
:ref:`Actions <action_definition>` that should be executed in order to satisfy
a given :ref:`Goal <goal_definition>`.
An :ref:`Action Plan <action_plan_definition>` is generated by Watcher when an
:ref:`Audit <audit_definition>` is successful which implies that the :ref:`Strategy <strategy_definition>`
which was used has found a :ref:`Solution <solution_definition>` to achieve the
:ref:`Goal <goal_definition>` of this :ref:`Audit <audit_definition>`.
In the default implementation of Watcher, an :ref:`Action Plan <action_plan_definition>`
is only composed of successive :ref:`Actions <action_definition>`
(i.e., a Workflow of :ref:`Actions <action_definition>` belonging to a unique
branch).
However, Watcher provides abstract interfaces for many of its components,
allowing other implementations to generate and handle more complex :ref:`Action Plan(s) <action_plan_definition>`
composed of two types of Action Item(s):
- simple :ref:`Actions <action_definition>`: atomic tasks, which means it
can not be split into smaller tasks or commands from an OpenStack point of
view.
- composite Actions: which are composed of several simple :ref:`Actions <action_definition>`
ordered in sequential and/or parallel flows.
An :ref:`Action Plan <action_plan_definition>` may be described using
standard workflow model description formats such as
`Business Process Model and Notation 2.0 (BPMN 2.0) <http://www.omg.org/spec/BPMN/2.0/>`_
or `Unified Modeling Language (UML) <http://www.uml.org/>`_.
An :ref:`Action Plan <action_plan_definition>` has a life-cycle and its current
state may be one of the following:
- **RECOMMENDED** : the :ref:`Action Plan <action_plan_definition>` is waiting
for a validation from the :ref:`Administrator <administrator_definition>`
- **ONGOING** : the :ref:`Action Plan <action_plan_definition>` is currently
being processed by the :ref:`Watcher Applier <watcher_applier_definition>`
- **SUCCEEDED** : the :ref:`Action Plan <action_plan_definition>` has been
executed successfully (i.e. all :ref:`Actions <action_definition>` that it
contains have been executed successfully)
- **FAILED** : an error occured while executing the
:ref:`Action Plan <action_plan_definition>`
- **DELETED** : the :ref:`Action Plan <action_plan_definition>` is still
stored in the :ref:`Watcher database <watcher_database_definition>` but is
not returned any more through the Watcher APIs.
- **CANCELLED** : the :ref:`Action Plan <action_plan_definition>` was in
**PENDING** or **ONGOING** state and was cancelled by the
:ref:`Administrator <administrator_definition>`
.. _administrator_definition:
Administrator
=============
The :ref:`Administrator <administrator_definition>` is any user who has admin
access on the OpenStack cluster. This user is allowed to create new projects
for tenants, create new users and assign roles to each user.
The :ref:`Administrator <administrator_definition>` usually has remote access
to any host of the cluster in order to change the configuration and restart any
OpenStack service, including Watcher.
In the context of Watcher, the :ref:`Administrator <administrator_definition>`
is a role for users which allows them to run any Watcher commands, such as:
- Create/Delete an :ref:`Audit Template <audit_template_definition>`
- Launch an :ref:`Audit <audit_definition>`
- Get the :ref:`Action Plan <action_plan_definition>`
- Launch a recommended :ref:`Action Plan <action_plan_definition>` manually
- Archive previous :ref:`Audits <audit_definition>` and :ref:`Action Plans <action_plan_definition>`
The :ref:`Administrator <administrator_definition>` is also allowed to modify
any Watcher configuration files and to restart Watcher services.
.. _audit_definition:
Audit
=====
In the Watcher system, an :ref:`Audit <audit_definition>` is a request for
optimizing a :ref:`Cluster <cluster_definition>`.
The optimization is done in order to satisfy one :ref:`Goal <goal_definition>`
on a given :ref:`Cluster <cluster_definition>`.
For each :ref:`Audit <audit_definition>`, the Watcher system generates an
:ref:`Action Plan <action_plan_definition>`.
An :ref:`Audit <audit_definition>` has a life-cycle and its current state may
be one of the following:
- **PENDING** : a request for an :ref:`Audit <audit_definition>` has been
submitted (either manually by the
:ref:`Administrator <administrator_definition>` or automatically via some
event handling mechanism) and is in the queue for being processed by the
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>`
- **ONGOING** : the :ref:`Audit <audit_definition>` is currently being
processed by the :ref:`Watcher Decision Engine <watcher_decision_engine_definition>`
- **SUCCEEDED** : the :ref:`Audit <audit_definition>` has been executed
successfully (note that it may not necessarily produce a
:ref:`Solution <solution_definition>`).
- **FAILED** : an error occured while executing the
:ref:`Audit <audit_definition>`
- **DELETED** : the :ref:`Audit <audit_definition>` is still stored in the
:ref:`Watcher database <watcher_database_definition>` but is not returned
any more through the Watcher APIs.
- **CANCELLED** : the :ref:`Audit <audit_definition>` was in **PENDING** or
**ONGOING** state and was cancelled by the
:ref:`Administrator <administrator_definition>`
.. _audit_template_definition:
Audit Template
==============
An :ref:`Audit <audit_definition>` may be launched several times with the same
settings (:ref:`Goal <goal_definition>`, thresholds, ...). Therefore it makes
sense to save those settings in some sort of Audit preset object, which is
known as an :ref:`Audit Template <audit_template_definition>`.
An :ref:`Audit Template <audit_template_definition>` contains at least the
:ref:`Goal <goal_definition>` of the :ref:`Audit <audit_definition>`.
It may also contain some error handling settings indicating whether:
- :ref:`Watcher Applier <watcher_applier_definition>` stops the entire operation
- :ref:`Watcher Applier <watcher_applier_definition>` performs a rollback
and how many retries should be attempted before failure occurs (also the latter
can be complex: for example the scenario in which there are many first-time
failures on ultimately successful :ref:`Actions <action_definition>`).
Moreover, an :ref:`Audit Template <audit_template_definition>` may contain some
settings related to the level of automation for the
:ref:`Action Plan <action_plan_definition>` that will be generated by the
:ref:`Audit <audit_definition>`.
A flag will indicate whether the :ref:`Action Plan <action_plan_definition>`
will be launched automatically or will need a manual confirmation from the
:ref:`Administrator <administrator_definition>`.
Last but not least, an :ref:`Audit Template <audit_template_definition>` may
contain a list of extra parameters related to the
:ref:`Strategy <strategy_definition>` configuration. These parameters can be
provided as a list of key-value pairs.
.. _availability_zone_definition:
Availability Zone
=================
Please, read `the official OpenStack definition of an Availability Zone <http://docs.openstack.org/developer/nova/aggregates.html#availability-zones-azs>`_.
.. _cluster_definition:
Cluster
=======
A :ref:`Cluster <cluster_definition>` is a set of physical machines which
provide compute, storage and networking resources and are managed by the same
OpenStack Controller node.
A :ref:`Cluster <cluster_definition>` represents a set of resources that a
cloud provider is able to offer to his/her
:ref:`customers <customer_definition>`.
A data center may contain several clusters.
The :ref:`Cluster <cluster_definition>` may be divided in one or several
:ref:`Availability Zone(s) <availability_zone_definition>`.
.. _cluster_data_model_definition:
Cluster Data Model
==================
A :ref:`Cluster Data Model <cluster_data_model_definition>` is a logical
representation of the current state and topology of the :ref:`Cluster <cluster_definition>`
:ref:`Managed resources <managed_resource_definition>`.
It is represented as a set of :ref:`Managed resources <managed_resource_definition>`
(which may be a simple tree or a flat list of key-value pairs)
which enables Watcher :ref:`Strategies <strategy_definition>` to know the
current relationships between the different
:ref:`resources <managed_resource_definition>`) of the
:ref:`Cluster <cluster_definition>` during an :ref:`Audit <audit_definition>`
and enables the :ref:`Strategy <strategy_definition>` to request information
such as:
- What compute nodes are in a given :ref:`Availability Zone <availability_zone_definition>`
or a given :ref:`Host Aggregate <host_aggregates_definition>` ?
- What :ref:`Instances <instance_definition>` are hosted on a given compute
node ?
- What is the current load of a compute node ?
- What is the current free memory of a compute node ?
- What is the network link between two compute nodes ?
- What is the available bandwidth on a given network link ?
- What is the current space available on a given virtual disk of a given
:ref:`Instance <instance_definition>` ?
- What is the current state of a given :ref:`Instance <instance_definition>` ?
- ...
In a word, this data model enables the :ref:`Strategy <strategy_definition>`
to know:
- the current topology of the :ref:`Cluster <cluster_definition>`
- the current capacity for each :ref:`Managed resource <managed_resource_definition>`
- the current amount of used/free space for each :ref:`Managed resource <managed_resource_definition>`
- the current state of each :ref:`Managed resources <managed_resource_definition>`
In the Watcher project, we aim at providing a generic and very basic
:ref:`Cluster Data Model <cluster_data_model_definition>` for each
:ref:`Goal <goal_definition>`, usable in the associated
:ref:`Strategies <strategy_definition>` through some helper classes in order
to:
- simplify the development of a new
:ref:`Strategy <strategy_definition>` for a given
:ref:`Goal <goal_definition>` when there already are some existing
:ref:`Strategies <strategy_definition>` associated to the same
:ref:`Goal <goal_definition>`
- avoid duplicating the same code in several
:ref:`Strategies <strategy_definition>` associated to the same
:ref:`Goal <goal_definition>`
- have a better consistency between the different
:ref:`Strategies <strategy_definition>` for a given
:ref:`Goal <goal_definition>`
- avoid any strong coupling with any external
:ref:`Cluster Data Model <cluster_data_model_definition>`
(the proposed data model acts as a pivot data model)
There may be various :ref:`generic and basic Cluster Data Models <cluster_data_model_definition>`
proposed in Watcher helpers, each of them being adapted to achieving a given
:ref:`Goal <goal_definition>`:
- For example, for a
:ref:`Goal <goal_definition>` which aims at optimizing the network
:ref:`resources <managed_resource_definition>` the
:ref:`Strategy <strategy_definition>` may need to know which
:ref:`resources <managed_resource_definition>` are communicating together.
- Whereas for a :ref:`Goal <goal_definition>` which aims at optimizing thermal
and power conditions, the :ref:`Strategy <strategy_definition>` may need to
know the location of each compute node in the racks and the location of each
rack in the room.
Note however that a developer can use his/her own
:ref:`Cluster Data Model <cluster_data_model_definition>` if the proposed data
model does not fit his/her needs as long as the :ref:`Strategy <strategy_definition>`
is able to produce a :ref:`Solution <solution_definition>` for the requested :ref:`Goal <goal_definition>`.
For example, a developer could rely on the Nova Data Model to optimize some
compute resources.
The :ref:`Cluster Data Model <cluster_data_model_definition>` may be persisted
in any appropriate storage system (SQL database, NoSQL database, JSON file,
XML File, In Memory Database, ...).
.. _cluster_history_definition:
Cluster History
===============
The :ref:`Cluster History <cluster_history_definition>` contains all the
previously collected timestamped data such as metrics and events associated
to any :ref:`managed resource <managed_resource_definition>` of the
:ref:`Cluster <cluster_definition>`.
Just like the :ref:`Cluster Data Model <cluster_data_model_definition>`, this
history may be used by any :ref:`Strategy <strategy_definition>` in order to
find the most optimal :ref:`Solution <solution_definition>` during an
:ref:`Audit <audit_definition>`.
In the Watcher project, a generic :ref:`Cluster History <cluster_history_definition>`
API is proposed with some helper classes in order to :
- share a common measurement (events or metrics) naming based on what is
defined in Ceilometer. See `the full list of available measurements <http://docs.openstack.org/admin-guide-cloud/telemetry-measurements.html>`_
- share common meter types (Cumulative, Delta, Gauge) based on what is
defined in Ceilometer. See `the full list of meter types <http://docs.openstack.org/admin-guide-cloud/telemetry-measurements.html>`_
- simplify the development of a new :ref:`Strategy <strategy_definition>`
- avoid duplicating the same code in several :ref:`Strategies <strategy_definition>`
- have a better consistency between the different :ref:`Strategies <strategy_definition>`
- avoid any strong coupling with any external metrics/events storage system
(the proposed API and measurement naming system acts as a pivot format)
Note however that a developer can use his/her own history management system if
the Ceilometer system does not fit his/her needs as long as the
:ref:`Strategy <strategy_definition>` is able to produce a
:ref:`Solution <solution_definition>` for the requested
:ref:`Goal <goal_definition>`.
The :ref:`Cluster History <cluster_history_definition>` data may be persisted
in any appropriate storage system (InfluxDB, OpenTSDB, MongoDB,...).
.. _controller_node_definition:
Controller Node
===============
A controller node is a machine that typically runs the following core OpenStack
services:
- Keystone: for identity and service management
- Cinder scheduler: for volumes management
- Glance controller: for image management
- Neutron controller: for network management
- Nova controller: for global compute resources management with services such as
nova-scheduler, nova-conductor and nova-network
In many configurations, Watcher will reside on a controller node even if it
can potentially be hosted on a dedicated machine.
.. _compute_node_definition:
Compute node
============
Please, read `the official OpenStack definition of a Compute Node <http://docs.openstack.org/openstack-ops/content/compute_nodes.html>`_.
.. _customer_definition:
Customer
========
A :ref:`Customer <customer_definition>` is the person or company which
subscribes to the cloud provider offering. A customer may have several :ref:`Project(s) <project_definition>`
hosted on the same :ref:`Cluster <cluster_definition>` or dispatched on
different clusters.
In the private cloud context, the :ref:`Customers <customer_definition>` are
different groups within the same organization (different departments, project
teams, branch offices and so on). Cloud infrastructure includes the ability to
precisely track each customer's service usage so that it can be charged back to
them, or at least reported to them.
.. _goal_definition:
Goal
====
A :ref:`Goal <goal_definition>` is a human readable, observable and measurable
end result having one objective to be achieved.
Here are some examples of :ref:`Goals <goal_definition>`:
- minimize the energy consumption
- minimize the number of compute nodes (consolidation)
- balance the workload among compute nodes
- minimize the license cost (some softwares have a licensing model which is
based on the number of sockets or cores where the software is deployed)
- find the most appropriate moment for a planned maintenance on a
given group of host (which may be an entire availability zone):
power supply replacement, cooling system replacement, hardware
modification, ...
.. _host_aggregates_definition:
Host Aggregate
==============
Please, read `the official OpenStack definition of a Host Aggregate <http://docs.openstack.org/developer/nova/aggregates.html>`_.
.. _instance_definition:
Instance
========
A running virtual machine, or a virtual machine in a known state such as
suspended, that can be used like a hardware server.
.. _managed_resource_definition:
Managed resource
================
A :ref:`Managed resource <managed_resource_definition>` is one instance of
:ref:`Managed resource type <managed_resource_type_definition>` in a topology
with particular properties and dependencies on other
:ref:`Managed resources <managed_resource_definition>` (relationships).
For example, a :ref:`Managed resource <managed_resource_definition>` can be one
virtual machine (i.e., an :ref:`instance <instance_definition>`) hosted on a
:ref:`compute node <compute_node_definition>` and connected to another virtual
machine through a network link (represented also as a
:ref:`Managed resource <managed_resource_definition>` in the
:ref:`Cluster Data Model <cluster_data_model_definition>`).
.. _managed_resource_type_definition:
Managed resource type
=====================
A :ref:`Managed resource type <managed_resource_definition>` is a type of
hardware or software element of the :ref:`Cluster <cluster_definition>` that
the Watcher system can act on.
Here are some examples of
:ref:`Managed resource types <managed_resource_definition>`:
- `Nova Host Aggregates <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Nova::HostAggregate>`_
- `Nova Servers <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Nova::Server>`_
- `Cinder Volumes <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Cinder::Volume>`_
- `Neutron Routers <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Neutron::Router>`_
- `Neutron Networks <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Neutron::Net>`_
- `Neutron load-balancers <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Neutron::LoadBalancer>`_
- `Sahara Hadoop Cluster <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Sahara::Cluster>`_
- ...
It can be any of the `the official list of available resource types defined in OpenStack for HEAT <http://docs.openstack.org/developer/heat/template_guide/openstack.html>`_.
.. _efficiency_definition:
Optimization Efficiency
=======================
The :ref:`Optimization Efficiency <efficiency_definition>` is the objective
measure of how much of the :ref:`Goal <goal_definition>` has been achieved in
respect with constraints and :ref:`SLAs <sla_definition>` defined by the
:ref:`Customer <customer_definition>`.
The way efficiency is evaluated will depend on the :ref:`Goal <goal_definition>`
to achieve.
Of course, the efficiency will be relevant only as long as the :ref:`Action Plan <action_plan_definition>`
is relevant (i.e., the current state of the :ref:`Cluster <cluster_definition>`
has not changed in a way that a new :ref:`Audit <audit_definition>` would need
to be launched).
For example, if the :ref:`Goal <goal_definition>` is to lower the energy
consumption, the :ref:`Efficiency <efficiency_definition>` will be computed
using several indicators (KPIs):
- the percentage of energy gain (which must be the highest possible)
- the number of :ref:`SLA violations <sla_violation_definition>`
(which must be the lowest possible)
- the number of virtual machine migrations (which must be the lowest possible)
All those indicators (KPIs) are computed within a given timeframe, which is the
time taken to execute the whole :ref:`Action Plan <action_plan_definition>`.
The efficiency also enables the :ref:`Administrator <administrator_definition>`
to objectively compare different :ref:`Strategies <strategy_definition>` for
the same goal and same workload of the :ref:`Cluster <cluster_definition>`.
.. _project_definition:
Project
=======
:ref:`Projects <project_definition>` represent the base unit of “ownership”
in OpenStack, in that all :ref:`resources <managed_resource_definition>` in
OpenStack should be owned by a specific :ref:`project <project_definition>`.
In OpenStack Identity, a :ref:`project <project_definition>` must be owned by a
specific domain.
Please, read `the official OpenStack definition of a Project <http://docs.openstack.org/glossary/content/glossary.html>`_.
.. _sla_definition:
SLA
===
:ref:`SLA <sla_definition>` means Service Level Agreement.
The resources are negotiated between the :ref:`Customer <customer_definition>`
and the Cloud Provider in a contract.
Most of the time, this contract is composed of two documents:
- :ref:`SLA <sla_definition>` : Service Level Agreement
- :ref:`SLO <slo_definition>` : Service Level Objectives
Note that the :ref:`SLA <sla_definition>` is more general than the
:ref:`SLO <slo_definition>` in the sense that the former specifies what service
is to be provided, how it is supported, times, locations, costs, performance,
and responsibilities of the parties involved while the
:ref:`SLO <slo_definition>` focuses on more measurable characteristics such as
availability, throughput, frequency, response time or quality.
You can also read `the Wikipedia page for SLA <https://en.wikipedia.org/wiki/Service-level_agreement>`_
which provides a good definition.
.. _sla_violation_definition:
SLA violation
=============
A :ref:`SLA violation <sla_violation_definition>` happens when a :ref:`SLA <sla_definition>`
defined with a given :ref:`Customer <customer_definition>` could not be
respected by the cloud provider within the timeframe defined by the official
contract document.
.. _slo_definition:
SLO
===
A Service Level Objective (SLO) is a key element of a :ref:`SLA <sla_definition>`
between a service provider and a :ref:`Customer <customer_definition>`. SLOs
are agreed as a means of measuring the performance of the Service Provider and
are outlined as a way of avoiding disputes between the two parties based on
misunderstanding.
You can also read `the Wikipedia page for SLO <https://en.wikipedia.org/wiki/Service_level_objective>`_
which provides a good definition.
.. _solution_definition:
Solution
========
A :ref:`Solution <solution_definition>` is a set of :ref:`Actions <action_definition>`
generated by a :ref:`Strategy <strategy_definition>` (i.e., an algorithm) in
order to achieve the :ref:`Goal <goal_definition>` of an :ref:`Audit <audit_definition>`.
A :ref:`Solution <solution_definition>` is different from an
:ref:`Action Plan <action_plan_definition>` because it contains the
non-scheduled list of :ref:`Actions <action_definition>` which is produced by a
:ref:`Strategy <strategy_definition>`. In other words, the list of Actions in
a :ref:`Solution <solution_definition>` has not yet been re-ordered by the
:ref:`Watcher Planner <watcher_planner_definition>`.
Note that some algorithms (i.e. :ref:`Strategies <strategy_definition>`) may
generate several :ref:`Solutions <solution_definition>`. This gives rise to the
problem of determining which :ref:`Solution <solution_definition>` should be
applied.
Two approaches to dealing with this can be envisaged:
- **fully automated mode**: only the :ref:`Solution <solution_definition>` with
the highest ranking (i.e., the highest
:ref:`Optimization Efficiency <efficiency_definition>`)
will be sent to the :ref:`Watcher Planner <watcher_planner_definition>` and
translated into concrete :ref:`Actions <action_definition>`.
- **manual mode**: several :ref:`Solutions <solution_definition>` are proposed
to the :ref:`Administrator <administrator_definition>` with a detailed
measurement of the estimated
:ref:`Optimization Efficiency <efficiency_definition>` and he/she decides
which one will be launched.
.. _strategy_definition:
Strategy
========
A :ref:`Strategy <strategy_definition>` is an algorithm implementation which is
able to find a :ref:`Solution <solution_definition>` for a given :ref:`Goal <goal_definition>`.
There may be several potential strategies which are able to achieve the same
:ref:`Goal <goal_definition>`. This is why it is possible to configure which
specific :ref:`Strategy <strategy_definition>` should be used for each :ref:`Goal <goal_definition>`.
Some strategies may provide better optimization results but may take more time
to find an optimal :ref:`Solution <solution_definition>`.
When a new :ref:`Goal <goal_definition>` is added to the Watcher configuration,
at least one default associated :ref:`Strategy <strategy_definition>` should be
provided as well.
.. _watcher_applier_definition:
Watcher Applier
===============
This component is in charge of executing the :ref:`Action Plan <action_plan_definition>`
built by the :ref:`Watcher Decision Engine <watcher_decision_engine_definition>`.
See :doc:`architecture` for more details on this component.
.. _watcher_database_definition:
Watcher Database
================
This database stores all the Watcher domain objects which can be requested
by the Watcher API or the Watcher CLI:
- Audit templates
- Audits
- Action plans
- Actions
- Goals
The Watcher domain being here "*optimization of some resources provided by an
OpenStack system*".
See :doc:`architecture` for more details on this component.
.. _watcher_decision_engine_definition:
Watcher Decision Engine
=======================
This component is responsible for computing a set of potential optimization
:ref:`Actions <action_definition>` in order to fulfill the :ref:`Goal <goal_definition>`
of an :ref:`Audit <audit_definition>`.
It first reads the parameters of the :ref:`Audit <audit_definition>` from the
associated :ref:`Audit Template <audit_template_definition>` and knows the
:ref:`Goal <goal_definition>` to achieve.
It then selects the most appropriate :ref:`Strategy <strategy_definition>`
depending on how Watcher was configured for this :ref:`Goal <goal_definition>`.
The :ref:`Strategy <strategy_definition>` is then executed and generates a set
of :ref:`Actions <action_definition>` which are scheduled in time by the
:ref:`Watcher Planner <watcher_planner_definition>` (i.e., it generates an
:ref:`Action Plan <action_plan_definition>`).
See :doc:`architecture` for more details on this component.
.. _watcher_planner_definition:
Watcher Planner
===============
The :ref:`Watcher Planner <watcher_planner_definition>` is part of the
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>`.
This module takes the set of :ref:`Actions <action_definition>` generated by a
:ref:`Strategy <strategy_definition>` and builds the design of a workflow which
defines how-to schedule in time those different
:ref:`Actions <action_definition>` and for each
:ref:`Action <action_definition>` what are the prerequisite conditions.
It is important to schedule :ref:`Actions <action_definition>` in time in order
to prevent overload of the :ref:`Cluster <cluster_definition>` while applying
the :ref:`Action Plan <action_plan_definition>`. For example, it is important
not to migrate too many instances at the same time in order to avoid a network
congestion which may decrease the :ref:`SLA <sla_definition>` for
:ref:`Customers <customer_definition>`.
It is also important to schedule :ref:`Actions <action_definition>` in order to
avoid security issues such as denial of service on core OpenStack services.
See :doc:`architecture` for more details on this component.

View File

@@ -0,0 +1,159 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
==================
Build a new action
==================
Watcher Applier has an external :ref:`action <action_definition>` plugin
interface which gives anyone the ability to integrate an external
:ref:`action <action_definition>` in order to extend the initial set of actions
Watcher provides.
This section gives some guidelines on how to implement and integrate custom
actions with Watcher.
Creating a new plugin
=====================
First of all you have to extend the base :py:class:`BaseAction` class which
defines a set of abstract methods and/or properties that you will have to
implement:
- The :py:attr:`~.BaseAction.schema` is an abstract property that you have to
implement. This is the first function to be called by the
:ref:`applier <watcher_applier_definition>` before any further processing
and its role is to validate the input parameters that were provided to it.
- The :py:meth:`~.BaseAction.precondition` is called before the execution of
an action. This method is a hook that can be used to perform some
initializations or to make some more advanced validation on its input
parameters. If you wish to block the execution based on this factor, you
simply have to ``raise`` an exception.
- The :py:meth:`~.BaseAction.postcondition` is called after the execution of
an action. As this function is called regardless of whether an action
succeeded or not, this can prove itself useful to perform cleanup
operations.
- The :py:meth:`~.BaseAction.execute` is the main component of an action.
This is where you should implement the logic of your action.
- The :py:meth:`~.BaseAction.revert` allows you to roll back the targeted
resource to its original state following a faulty execution. Indeed, this
method is called by the workflow engine whenever an action raises an
exception.
Here is an example showing how you can write a plugin called ``DummyAction``:
.. code-block:: python
# Filepath = <PROJECT_DIR>/thirdparty/dummy.py
# Import path = thirdparty.dummy
from watcher.applier.actions import base
class DummyAction(baseBaseAction):
@property
def schema(self):
return Schema({})
def execute(self):
# Does nothing
pass # Only returning False is considered as a failure
def revert(self):
# Does nothing
pass
def precondition(self):
# No pre-checks are done here
pass
def postcondition(self):
# Nothing done here
pass
This implementation is the most basic one. So if you want to have more advanced
examples, have a look at the implementation of the actions already provided
by Watcher like.
To get a better understanding on how to implement a more advanced action,
have a look at the :py:class:`~watcher.applier.actions.migration.Migrate`
class.
Abstract Plugin Class
=====================
Here below is the abstract ``BaseAction`` class that every single action
should implement:
.. autoclass:: watcher.applier.actions.base.BaseAction
:members:
:noindex:
.. py:attribute:: schema
Defines a Schema that the input parameters shall comply to
:returns: A schema declaring the input parameters this action should be
provided along with their respective constraints
(e.g. type, value range, ...)
:rtype: :py:class:`voluptuous.Schema` instance
Register a new entry point
==========================
In order for the Watcher Applier to load your new action, the
action must be registered as a named entry point under the
``watcher_actions`` entry point of your ``setup.py`` file. If you are using
pbr_, this entry point should be placed in your ``setup.cfg`` file.
The name you give to your entry point has to be unique.
Here below is how you would proceed to register ``DummyAction`` using pbr_:
.. code-block:: ini
[entry_points]
watcher_actions =
dummy = thirdparty.dummy:DummyAction
.. _pbr: http://docs.openstack.org/developer/pbr/
Using action plugins
====================
The Watcher Applier service will automatically discover any installed plugins
when it is restarted. If a Python package containing a custom plugin is
installed within the same environment as Watcher, Watcher will automatically
make that plugin available for use.
At this point, you can use your new action plugin in your :ref:`strategy plugin
<implement_strategy_plugin>` if you reference it via the use of the
:py:meth:`~.Solution.add_action` method:
.. code-block:: python
# [...]
self.solution.add_action(
action_type="dummy", # Name of the entry point we registered earlier
applies_to="",
input_parameters={})
By doing so, your action will be saved within the Watcher Database, ready to be
processed by the planner for creating an action plan which can then be executed
by the Watcher Applier via its workflow engine.
Scheduling of an action plugin
==============================
Watcher provides a basic built-in :ref:`planner <watcher_planner_definition>`
which is only able to process the Watcher built-in actions. Therefore, you will
either have to use an existing third-party planner or :ref:`implement another
planner <implement_planner_plugin>` that will be able to take into account your
new action plugin.

View File

@@ -0,0 +1,90 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
.. _plugin-base_setup:
=======================================
Create a third-party plugin for Watcher
=======================================
Watcher provides a plugin architecture which allows anyone to extend the
existing functionalities by implementing third-party plugins. This process can
be cumbersome so this documentation is there to help you get going as quickly
as possible.
Pre-requisites
==============
We assume that you have set up a working Watcher development environment. So if
this not already the case, you can check out our documentation which explains
how to set up a :ref:`development environment
<watcher_developement_environment>`.
.. _development environment:
Third party project scaffolding
===============================
First off, we need to create the project structure. To do so, we can use
`cookiecutter`_ and the `OpenStack cookiecutter`_ project scaffolder to
generate the skeleton of our project::
$ virtualenv thirdparty
$ source thirdparty/bin/activate
$ pip install cookiecutter
$ cookiecutter https://github.com/openstack-dev/cookiecutter
The last command will ask you for many information, and If you set
``module_name`` and ``repo_name`` as ``thirdparty``, you should end up with a
structure that looks like this::
$ cd thirdparty
$ tree .
.
├── babel.cfg
├── CONTRIBUTING.rst
├── doc
│   └── source
│   ├── conf.py
│   ├── contributing.rst
│   ├── index.rst
│   ├── installation.rst
│   ├── readme.rst
│   └── usage.rst
├── HACKING.rst
├── LICENSE
├── MANIFEST.in
├── README.rst
├── requirements.txt
├── setup.cfg
├── setup.py
├── test-requirements.txt
├── thirdparty
│   ├── __init__.py
│   └── tests
│   ├── base.py
│   ├── __init__.py
│   └── test_thirdparty.py
└── tox.ini
.. _cookiecutter: https://github.com/audreyr/cookiecutter
.. _OpenStack cookiecutter: https://github.com/openstack-dev/cookiecutter
Implementing a plugin for Watcher
=================================
Now that the project skeleton has been created, you can start the
implementation of your plugin. As of now, you can implement the following
plugins for Watcher:
- A :ref:`strategy plugin <implement_strategy_plugin>`
- A :ref:`planner plugin <implement_planner_plugin>`
- An :ref:`action plugin <implement_strategy_plugin>`
- A :ref:`workflow engine plugin <implement_workflow_engine_plugin>`
If you want to learn more on how to implement them, you can refer to their
dedicated documentation.

View File

@@ -0,0 +1,127 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
.. _implement_planner_plugin:
===================
Build a new planner
===================
Watcher :ref:`Decision Engine <watcher_decision_engine_definition>` has an
external :ref:`planner <planner_definition>` plugin interface which gives
anyone the ability to integrate an external :ref:`planner <planner_definition>`
in order to extend the initial set of planners Watcher provides.
This section gives some guidelines on how to implement and integrate custom
planners with Watcher.
.. _Decision Engine: watcher_decision_engine_definition
Creating a new plugin
=====================
First of all you have to extend the base :py:class:`~.BasePlanner` class which
defines an abstract method that you will have to implement. The
:py:meth:`~.BasePlanner.schedule` is the method being called by the Decision
Engine to schedule a given solution (:py:class:`~.BaseSolution`) into an
:ref:`action plan <action_plan_definition>` by ordering/sequencing an unordered
set of actions contained in the proposed solution (for more details, see
:ref:`definition of a solution <solution_definition>`).
Here is an example showing how you can write a planner plugin called
``DummyPlanner``:
.. code-block:: python
# Filepath = third-party/third_party/dummy.py
# Import path = third_party.dummy
import uuid
from watcher.decision_engine.planner import base
class DummyPlanner(base.BasePlanner):
def _create_action_plan(self, context, audit_id):
action_plan_dict = {
'uuid': uuid.uuid4(),
'audit_id': audit_id,
'first_action_id': None,
'state': objects.action_plan.State.RECOMMENDED
}
new_action_plan = objects.ActionPlan(context, **action_plan_dict)
new_action_plan.create(context)
new_action_plan.save()
return new_action_plan
def schedule(self, context, audit_id, solution):
# Empty action plan
action_plan = self._create_action_plan(context, audit_id)
# todo: You need to create the workflow of actions here
# and attach it to the action plan
return action_plan
This implementation is the most basic one. So if you want to have more advanced
examples, have a look at the implementation of planners already provided by
Watcher like :py:class:`~.DefaultPlanner`. A list with all available planner
plugins can be found :ref:`here <watcher_planners>`.
Abstract Plugin Class
=====================
Here below is the abstract ``BasePlanner`` class that every single planner
should implement:
.. autoclass:: watcher.decision_engine.planner.base.BasePlanner
:members:
:noindex:
Register a new entry point
==========================
In order for the Watcher Decision Engine to load your new planner, the
latter must be registered as a new entry point under the
``watcher_planners`` entry point namespace of your ``setup.py`` file. If you
are using pbr_, this entry point should be placed in your ``setup.cfg`` file.
The name you give to your entry point has to be unique.
Here below is how you would proceed to register ``DummyPlanner`` using pbr_:
.. code-block:: ini
[entry_points]
watcher_planners =
dummy = third_party.dummy:DummyPlanner
.. _pbr: http://docs.openstack.org/developer/pbr/
Using planner plugins
=====================
The :ref:`Watcher Decision Engine <watcher_decision_engine_definition>` service
will automatically discover any installed plugins when it is started. This
means that if Watcher is already running when you install your plugin, you will
have to restart the related Watcher services. If a Python package containing a
custom plugin is installed within the same environment as Watcher, Watcher will
automatically make that plugin available for use.
At this point, Watcher will use your new planner if you referenced it in the
``planner`` option under the ``[watcher_planner]`` section of your
``watcher.conf`` configuration file when you started it. For example, if you
want to use the ``dummy`` planner you just installed, you would have to
select it as followed:
.. code-block:: ini
[watcher_planner]
planner = dummy
As you may have noticed, only a single planner implementation can be activated
at a time, so make sure it is generic enough to support all your strategies
and actions.

View File

@@ -0,0 +1,215 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
.. _implement_strategy_plugin:
=================================
Build a new optimization strategy
=================================
Watcher Decision Engine has an external :ref:`strategy <strategy_definition>`
plugin interface which gives anyone the ability to integrate an external
strategy in order to make use of placement algorithms.
This section gives some guidelines on how to implement and integrate custom
strategies with Watcher.
Pre-requisites
==============
Before using any strategy, you should make sure you have your Telemetry service
configured so that it would provide you all the metrics you need to be able to
use your strategy.
Creating a new plugin
=====================
First of all you have to:
- Extend :py:class:`~.BaseStrategy`
- Implement its :py:meth:`~.BaseStrategy.execute` method
Here is an example showing how you can write a plugin called ``DummyStrategy``:
.. code-block:: python
import uuid
class DummyStrategy(BaseStrategy):
DEFAULT_NAME = "dummy"
DEFAULT_DESCRIPTION = "Dummy Strategy"
def __init__(self, name=DEFAULT_NAME, description=DEFAULT_DESCRIPTION):
super(DummyStrategy, self).__init__(name, description)
def execute(self, model):
migration_type = 'live'
src_hypervisor = 'compute-host-1'
dst_hypervisor = 'compute-host-2'
instance_id = uuid.uuid4()
parameters = {'migration_type': migration_type,
'src_hypervisor': src_hypervisor,
'dst_hypervisor': dst_hypervisor}
self.solution.add_action(action_type="migration",
resource_id=instance_id,
input_parameters=parameters)
# Do some more stuff here ...
return self.solution
As you can see in the above example, the :py:meth:`~.BaseStrategy.execute`
method returns a :py:class:`~.BaseSolution` instance as required. This solution
is what wraps the abstract set of actions the strategy recommends to you. This
solution is then processed by a :ref:`planner <planner_definition>` to produce
an action plan which shall contain the sequenced flow of actions to be
executed by the :ref:`Watcher Applier <watcher_applier_definition>`.
Please note that your strategy class will be instantiated without any
parameter. Therefore, you should make sure not to make any of them required in
your ``__init__`` method.
Abstract Plugin Class
=====================
Here below is the abstract :py:class:`~.BaseStrategy` class that every single
strategy should implement:
.. autoclass:: watcher.decision_engine.strategy.strategies.base.BaseStrategy
:members:
:noindex:
Add a new entry point
=====================
In order for the Watcher Decision Engine to load your new strategy, the
strategy must be registered as a named entry point under the
``watcher_strategies`` entry point of your ``setup.py`` file. If you are using
pbr_, this entry point should be placed in your ``setup.cfg`` file.
The name you give to your entry point has to be unique.
Here below is how you would proceed to register ``DummyStrategy`` using pbr_:
.. code-block:: ini
[entry_points]
watcher_strategies =
dummy = thirdparty.dummy:DummyStrategy
To get a better understanding on how to implement a more advanced strategy,
have a look at the :py:class:`~.BasicConsolidation` class.
.. _pbr: http://docs.openstack.org/developer/pbr/
Using strategy plugins
======================
The Watcher Decision Engine service will automatically discover any installed
plugins when it is restarted. If a Python package containing a custom plugin is
installed within the same environment as Watcher, Watcher will automatically
make that plugin available for use.
At this point, Watcher will use your new strategy if you reference it in the
``goals`` under the ``[watcher_goals]`` section of your ``watcher.conf``
configuration file. For example, if you want to use a ``dummy`` strategy you
just installed, you would have to associate it to a goal like this:
.. code-block:: ini
[watcher_goals]
goals = BALANCE_LOAD:basic,MINIMIZE_ENERGY_CONSUMPTION:dummy
You should take care when installing strategy plugins. By their very nature,
there are no guarantees that utilizing them as is will be supported, as
they may require a set of metrics which is not yet available within the
Telemetry service. In such a case, please do make sure that you first
check/configure the latter so your new strategy can be fully functional.
Querying metrics
----------------
A large set of metrics, generated by OpenStack modules, can be used in your
strategy implementation. To collect these metrics, Watcher provides a
`Helper`_ to the Ceilometer API, which makes this API reusable and easier
to used.
If you want to use your own metrics database backend, please refer to the
`Ceilometer developer guide`_. Indeed, Ceilometer's pluggable model allows
for various types of backends. A list of the available backends is located
here_. The Ceilosca project is a good example of how to create your own
pluggable backend.
Finally, if your strategy requires new metrics not covered by Ceilometer, you
can add them through a Ceilometer `plugin`_.
.. _`Helper`: https://github.com/openstack/watcher/blob/master/watcher/metrics_engine/cluster_history/ceilometer.py#L31
.. _`Ceilometer developer guide`: http://docs.openstack.org/developer/ceilometer/architecture.html#storing-the-data
.. _`here`: http://docs.openstack.org/developer/ceilometer/install/dbreco.html#choosing-a-database-backend
.. _`plugin`: http://docs.openstack.org/developer/ceilometer/plugins.html
.. _`Ceilosca`: https://github.com/openstack/monasca-ceilometer/blob/master/ceilosca/ceilometer/storage/impl_monasca.py
Read usage metrics using the Python binding
-------------------------------------------
You can find the information about the Ceilometer Python binding on the
OpenStack `ceilometer client python API documentation
<http://docs.openstack.org/developer/python-ceilometerclient/api.html>`_
To facilitate the process, Watcher provides the ``osc`` attribute to every
strategy which includes clients to major OpenStack services, including
Ceilometer. So to access it within your strategy, you can do the following:
.. code-block:: py
# Within your strategy "execute()"
cclient = self.osc.ceilometer
# TODO: Do something here
Using that you can now query the values for that specific metric:
.. code-block:: py
query = None # e.g. [{'field': 'foo', 'op': 'le', 'value': 34},]
value_cpu = cclient.samples.list(
meter_name='cpu_util',
limit=10, q=query)
Read usage metrics using the Watcher Cluster History Helper
-----------------------------------------------------------
Here below is the abstract ``BaseClusterHistory`` class of the Helper.
.. autoclass:: watcher.metrics_engine.cluster_history.api.BaseClusterHistory
:members:
:noindex:
The following code snippet shows how to create a Cluster History class:
.. code-block:: py
from watcher.metrics_engine.cluster_history import ceilometer as ceil
query_history = ceil.CeilometerClusterHistory()
Using that you can now query the values for that specific metric:
.. code-block:: py
query_history.statistic_aggregation(resource_id=hypervisor.uuid,
meter_name='compute.node.cpu.percent',
period="7200",
aggregate='avg'
)

View File

@@ -1,183 +1,38 @@
===============
Watcher plugins
===============
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
Writing a Watcher Decision Engine plugin
========================================
Watcher has an external :ref:`strategy <strategy_definition>` plugin interface
which gives anyone the ability to integrate an external :ref:`strategy
<strategy_definition>` in order to make use of placement algorithms.
This section gives some guidelines on how to implement and integrate custom
Stategies with Watcher.
Pre-requisites
--------------
Before using any strategy, you should make sure you have your Telemetry service
configured so that it would provide you all the metrics you need to be able to
use your strategy.
https://creativecommons.org/licenses/by/3.0/
Creating a new plugin
---------------------
=================
Available Plugins
=================
First of all you have to:
.. _watcher_strategies:
- Extend the base ``BaseStrategy`` class
- Implement its ``execute`` method
Strategies
==========
Here is an example showing how you can write a plugin called ``DummyStrategy``:
.. drivers-doc:: watcher_strategies
.. code-block:: python
.. _watcher_actions:
# Filepath = third-party/third_party/dummy.py
# Import path = third_party.dummy
Actions
=======
class DummyStrategy(BaseStrategy):
.. drivers-doc:: watcher_actions
DEFAULT_NAME = "dummy"
DEFAULT_DESCRIPTION = "Dummy Strategy"
.. _watcher_workflow_engines:
def __init__(self, name=DEFAULT_NAME, description=DEFAULT_DESCRIPTION):
super(DummyStrategy, self).__init__(name, description)
Workflow Engines
================
def execute(self, model):
self.solution.add_change_request(
Migrate(vm=my_vm, source_hypervisor=src, dest_hypervisor=dest)
)
# Do some more stuff here ...
return self.solution
.. drivers-doc:: watcher_workflow_engines
As you can see in the above example, the ``execute()`` method returns a
solution as required.
.. _watcher_planners:
Please note that your strategy class will be instantiated without any
parameter. Therefore, you should make sure not to make any of them required in
your ``__init__`` method.
Abstract Plugin Class
---------------------
Here below is the abstract ``BaseStrategy`` class that every single strategy
should implement:
.. automodule:: watcher.decision_engine.strategy.base
:noindex:
.. autoclass:: BaseStrategy
:members:
Add a new entry point
---------------------
In order for the Watcher Decision Engine to load your new strategy, the
strategy must be registered as a named entry point under the
``watcher_strategies`` entry point of your ``setup.py`` file. If you are using
pbr_, this entry point should be placed in your ``setup.cfg`` file.
The name you give to your entry point has to be unique.
Here below is how you would proceed to register ``DummyStrategy`` using pbr_:
.. code-block:: ini
[entry_points]
watcher_strategies =
dummy = third_party.dummy:DummyStrategy
To get a better understanding on how to implement a more advanced strategy,
have a look at the :py:class:`BasicConsolidation` class.
.. _pbr: http://docs.openstack.org/developer/pbr/
Using strategy plugins
----------------------
The Watcher Decision Engine service will automatically discover any installed
plugins when it is run. If a Python package containing a custom plugin is
installed within the same environment as Watcher, Watcher will automatically
make that plugin available for use.
At this point, the way Watcher will use your new strategy if you reference it
in the ``goals`` under the ``[watcher_goals]`` section of your ``watcher.conf``
configuration file. For example, if you want to use a ``dummy`` strategy you
just installed, you would have to associate it to a goal like this:
.. code-block:: ini
[watcher_goals]
goals = BALANCE_LOAD:basic,MINIMIZE_ENERGY_CONSUMPTION:dummy
You should take care when installing strategy plugins. By their very nature,
there are no guarantees that utilizing them as is will be supported, as
they may require a set of metrics which is not yet available within the
Telemetry service. In such a case, please do make sure that you first
check/configure the latter so your new strategy can be fully functional.
Querying metrics
~~~~~~~~~~~~~~~~
The metrics available depend on the hypervisors that OpenStack manages on
the specific implementation. You can find the metrics available per hypervisor
and OpenStack release on the OpenStack site.
There are different possible ways to obtain usage metrics in Watcher, you can
use the default Ceilometer API or our Helper.
The Helper attempted to make the Ceilometer API more reusable and easy to use.
Read usage metrics using the Python binding
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can find the information about the Ceilometer Python binding on the
OpenStack `ceilometer client python API documentation
<http://docs.openstack.org/developer/python-ceilometerclient/api.html>`_
The first step is to authenticate against the Ceilometer service
(assuming that you already imported the Ceilometer client for Python)
with this call:
.. code-block:: py
cclient = ceilometerclient.client.get_client(VERSION, os_username=USERNAME,
os_password=PASSWORD, os_tenant_name=PROJECT_NAME, os_auth_url=AUTH_URL)
Using that you can now query the values for that specific metric:
.. code-block:: py
value_cpu = cclient.samples.list(meter_name='cpu_util', limit=10, q=query)
Read usage metrics using the Watcher Cluster History Helper
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here below is the abstract ``BaseClusterHistory`` class of the Helper.
.. automodule:: watcher.metrics_engine.cluster_history.api
:noindex:
.. autoclass:: BaseClusterHistory
:members:
The following snippet code shows how to create a Cluster History class:
.. code-block:: py
query_history = CeilometerClusterHistory()
Using that you can now query the values for that specific metric:
.. code-block:: py
query_history.statistic_aggregation(resource_id=hypervisor.uuid,
meter_name='compute.node.cpu.percent',
period="7200",
aggregate='avg'
)
Planners
========
.. drivers-doc:: watcher_planners

View File

@@ -0,0 +1,50 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
=======
Testing
=======
.. _unit_tests:
Unit tests
==========
All unit tests should be run using `tox`_. To run the same unit tests that are
executing onto `Gerrit`_ which includes ``py34``, ``py27`` and ``pep8``, you
can issue the following command::
$ workon watcher
(watcher) $ pip install tox
(watcher) $ cd watcher
(watcher) $ tox
If you want to only run one of the aforementioned, you can then issue one of
the following::
$ workon watcher
(watcher) $ tox -e py34
(watcher) $ tox -e py27
(watcher) $ tox -e pep8
.. _tox: https://tox.readthedocs.org/
.. _Gerrit: http://review.openstack.org/
You may pass options to the test programs using positional arguments. To run a
specific unit test, you can pass extra options to `os-testr`_ after putting
the ``--`` separator. So using the ``-r`` option followed by a regex string,
you can run the desired test::
$ workon watcher
(watcher) $ tox -e py27 -- -r watcher.tests.api
.. _os-testr: http://docs.openstack.org/developer/os-testr/
When you're done, deactivate the virtualenv::
$ deactivate
.. include:: ../../../watcher_tempest_plugin/README.rst

367
doc/source/glossary.rst Normal file
View File

@@ -0,0 +1,367 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
========
Glossary
========
.. glossary::
:sorted:
This page explains the different terms used in the Watcher system.
They are sorted in alphabetical order.
.. _action_definition:
Action
======
.. watcher-term:: watcher.api.controllers.v1.action
.. _action_plan_definition:
Action Plan
===========
.. watcher-term:: watcher.api.controllers.v1.action_plan
.. _administrator_definition:
Administrator
=============
The :ref:`Administrator <administrator_definition>` is any user who has admin
access on the OpenStack cluster. This user is allowed to create new projects
for tenants, create new users and assign roles to each user.
The :ref:`Administrator <administrator_definition>` usually has remote access
to any host of the cluster in order to change the configuration and restart any
OpenStack service, including Watcher.
In the context of Watcher, the :ref:`Administrator <administrator_definition>`
is a role for users which allows them to run any Watcher commands, such as:
- Create/Delete an :ref:`Audit Template <audit_template_definition>`
- Launch an :ref:`Audit <audit_definition>`
- Get the :ref:`Action Plan <action_plan_definition>`
- Launch a recommended :ref:`Action Plan <action_plan_definition>` manually
- Archive previous :ref:`Audits <audit_definition>` and
:ref:`Action Plans <action_plan_definition>`
The :ref:`Administrator <administrator_definition>` is also allowed to modify
any Watcher configuration files and to restart Watcher services.
.. _audit_definition:
Audit
=====
.. watcher-term:: watcher.api.controllers.v1.audit
.. _audit_template_definition:
Audit Template
==============
.. watcher-term:: watcher.api.controllers.v1.audit_template
.. _availability_zone_definition:
Availability Zone
=================
Please, read `the official OpenStack definition of an Availability Zone <http://docs.openstack.org/developer/nova/aggregates.html#availability-zones-azs>`_.
.. _cluster_definition:
Cluster
=======
A :ref:`Cluster <cluster_definition>` is a set of physical machines which
provide compute, storage and networking resources and are managed by the same
OpenStack Controller node.
A :ref:`Cluster <cluster_definition>` represents a set of resources that a
cloud provider is able to offer to his/her
:ref:`customers <customer_definition>`.
A data center may contain several clusters.
The :ref:`Cluster <cluster_definition>` may be divided in one or several
:ref:`Availability Zone(s) <availability_zone_definition>`.
.. _cluster_data_model_definition:
Cluster Data Model
==================
.. watcher-term:: watcher.metrics_engine.cluster_model_collector.api
.. _cluster_history_definition:
Cluster History
===============
.. watcher-term:: watcher.metrics_engine.cluster_history.api
.. _controller_node_definition:
Controller Node
===============
A controller node is a machine that typically runs the following core OpenStack
services:
- Keystone: for identity and service management
- Cinder scheduler: for volumes management
- Glance controller: for image management
- Neutron controller: for network management
- Nova controller: for global compute resources management with services
such as nova-scheduler, nova-conductor and nova-network.
In many configurations, Watcher will reside on a controller node even if it
can potentially be hosted on a dedicated machine.
.. _compute_node_definition:
Compute node
============
Please, read `the official OpenStack definition of a Compute Node <http://docs.openstack.org/openstack-ops/content/compute_nodes.html>`_.
.. _customer_definition:
Customer
========
A :ref:`Customer <customer_definition>` is the person or company which
subscribes to the cloud provider offering. A customer may have several
:ref:`Project(s) <project_definition>`
hosted on the same :ref:`Cluster <cluster_definition>` or dispatched on
different clusters.
In the private cloud context, the :ref:`Customers <customer_definition>` are
different groups within the same organization (different departments, project
teams, branch offices and so on). Cloud infrastructure includes the ability to
precisely track each customer's service usage so that it can be charged back to
them, or at least reported to them.
.. _goal_definition:
Goal
====
.. watcher-term:: watcher.api.controllers.v1.goal
.. _host_aggregates_definition:
Host Aggregate
==============
Please, read `the official OpenStack definition of a Host Aggregate <http://docs.openstack.org/developer/nova/aggregates.html>`_.
.. _instance_definition:
Instance
========
A running virtual machine, or a virtual machine in a known state such as
suspended, that can be used like a hardware server.
.. _managed_resource_definition:
Managed resource
================
A :ref:`Managed resource <managed_resource_definition>` is one instance of
:ref:`Managed resource type <managed_resource_type_definition>` in a topology
with particular properties and dependencies on other
:ref:`Managed resources <managed_resource_definition>` (relationships).
For example, a :ref:`Managed resource <managed_resource_definition>` can be one
virtual machine (i.e., an :ref:`instance <instance_definition>`) hosted on a
:ref:`compute node <compute_node_definition>` and connected to another virtual
machine through a network link (represented also as a
:ref:`Managed resource <managed_resource_definition>` in the
:ref:`Cluster Data Model <cluster_data_model_definition>`).
.. _managed_resource_type_definition:
Managed resource type
=====================
A :ref:`Managed resource type <managed_resource_definition>` is a type of
hardware or software element of the :ref:`Cluster <cluster_definition>` that
the Watcher system can act on.
Here are some examples of
:ref:`Managed resource types <managed_resource_definition>`:
- `Nova Host Aggregates <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Nova::HostAggregate>`_
- `Nova Servers <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Nova::Server>`_
- `Cinder Volumes <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Cinder::Volume>`_
- `Neutron Routers <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Neutron::Router>`_
- `Neutron Networks <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Neutron::Net>`_
- `Neutron load-balancers <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Neutron::LoadBalancer>`_
- `Sahara Hadoop Cluster <http://docs.openstack.org/developer/heat/template_guide/openstack.html#OS::Sahara::Cluster>`_
- ...
It can be any of the `the official list of available resource types defined in OpenStack for HEAT <http://docs.openstack.org/developer/heat/template_guide/openstack.html>`_.
.. _efficiency_definition:
Optimization Efficiency
=======================
The :ref:`Optimization Efficiency <efficiency_definition>` is the objective
measure of how much of the :ref:`Goal <goal_definition>` has been achieved in
respect with constraints and :ref:`SLAs <sla_definition>` defined by the
:ref:`Customer <customer_definition>`.
The way efficiency is evaluated will depend on the
:ref:`Goal <goal_definition>` to achieve.
Of course, the efficiency will be relevant only as long as the
:ref:`Action Plan <action_plan_definition>` is relevant
(i.e., the current state of the :ref:`Cluster <cluster_definition>`
has not changed in a way that a new :ref:`Audit <audit_definition>` would need
to be launched).
For example, if the :ref:`Goal <goal_definition>` is to lower the energy
consumption, the :ref:`Efficiency <efficiency_definition>` will be computed
using several indicators (KPIs):
- the percentage of energy gain (which must be the highest possible)
- the number of :ref:`SLA violations <sla_violation_definition>`
(which must be the lowest possible)
- the number of virtual machine migrations (which must be the lowest possible)
All those indicators (KPIs) are computed within a given timeframe, which is the
time taken to execute the whole :ref:`Action Plan <action_plan_definition>`.
The efficiency also enables the :ref:`Administrator <administrator_definition>`
to objectively compare different :ref:`Strategies <strategy_definition>` for
the same goal and same workload of the :ref:`Cluster <cluster_definition>`.
.. _project_definition:
Project
=======
:ref:`Projects <project_definition>` represent the base unit of “ownership”
in OpenStack, in that all :ref:`resources <managed_resource_definition>` in
OpenStack should be owned by a specific :ref:`project <project_definition>`.
In OpenStack Identity, a :ref:`project <project_definition>` must be owned by a
specific domain.
Please, read `the official OpenStack definition of a Project <http://docs.openstack.org/glossary/content/glossary.html>`_.
.. _sla_definition:
SLA
===
:ref:`SLA <sla_definition>` means Service Level Agreement.
The resources are negotiated between the :ref:`Customer <customer_definition>`
and the Cloud Provider in a contract.
Most of the time, this contract is composed of two documents:
- :ref:`SLA <sla_definition>` : Service Level Agreement
- :ref:`SLO <slo_definition>` : Service Level Objectives
Note that the :ref:`SLA <sla_definition>` is more general than the
:ref:`SLO <slo_definition>` in the sense that the former specifies what service
is to be provided, how it is supported, times, locations, costs, performance,
and responsibilities of the parties involved while the
:ref:`SLO <slo_definition>` focuses on more measurable characteristics such as
availability, throughput, frequency, response time or quality.
You can also read `the Wikipedia page for SLA <https://en.wikipedia.org/wiki/Service-level_agreement>`_
which provides a good definition.
.. _sla_violation_definition:
SLA violation
=============
A :ref:`SLA violation <sla_violation_definition>` happens when a
:ref:`SLA <sla_definition>` defined with a given
:ref:`Customer <customer_definition>` could not be respected by the
cloud provider within the timeframe defined by the official contract document.
.. _slo_definition:
SLO
===
A Service Level Objective (SLO) is a key element of a
:ref:`SLA <sla_definition>` between a service provider and a
:ref:`Customer <customer_definition>`. SLOs are agreed as a means of measuring
the performance of the Service Provider and are outlined as a way of avoiding
disputes between the two parties based on misunderstanding.
You can also read `the Wikipedia page for SLO <https://en.wikipedia.org/wiki/Service_level_objective>`_
which provides a good definition.
.. _solution_definition:
Solution
========
.. watcher-term:: watcher.decision_engine.solution.base
.. _strategy_definition:
Strategy
========
.. watcher-term:: watcher.decision_engine.strategy.strategies.base
.. _watcher_applier_definition:
Watcher Applier
===============
.. watcher-term:: watcher.applier.base
.. _watcher_database_definition:
Watcher Database
================
This database stores all the Watcher domain objects which can be requested
by the Watcher API or the Watcher CLI:
- Audit templates
- Audits
- Action plans
- Actions
- Goals
The Watcher domain being here "*optimization of some resources provided by an
OpenStack system*".
See :doc:`architecture` for more details on this component.
.. _watcher_decision_engine_definition:
Watcher Decision Engine
=======================
.. watcher-term:: watcher.decision_engine.manager
.. _watcher_planner_definition:
Watcher Planner
===============
.. watcher-term:: watcher.decision_engine.planner.base

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,16 @@
@startuml
[*] --> RECOMMENDED: The Watcher Planner\ncreates the Action Plan
RECOMMENDED --> PENDING: Adminisrator launches\nthe Action Plan
PENDING --> ONGOING: The Watcher Applier receives the request\nto launch the Action Plan
ONGOING --> FAILED: Something failed while executing\nthe Action Plan in the Watcher Applier
ONGOING --> SUCCEEDED: The Watcher Applier executed\nthe Action Plan successfully
FAILED --> DELETED : Administrator removes\nAction Plan
SUCCEEDED --> DELETED : Administrator removes\nAction Plan
ONGOING --> CANCELLED : Administrator cancels\nAction Plan
RECOMMENDED --> CANCELLED : Administrator cancels\nAction Plan
PENDING --> CANCELLED : Administrator cancels\nAction Plan
CANCELLED --> DELETED
DELETED --> [*]
@enduml

View File

@@ -0,0 +1,14 @@
@startuml
[*] --> PENDING: Audit requested by Administrator
PENDING --> ONGOING: Audit request is received\nby the Watcher Decision Engine
ONGOING --> FAILED: Audit fails\n(no solution found, technical error, ...)
ONGOING --> SUCCEEDED: The Watcher Decision Engine\ncould find at least one Solution
FAILED --> DELETED : Administrator wants to\narchive/delete the Audit
SUCCEEDED --> DELETED : Administrator wants to\narchive/delete the Audit
PENDING --> CANCELLED : Administrator cancels\nthe Audit
ONGOING --> CANCELLED : Administrator cancels\nthe Audit
CANCELLED --> DELETED : Administrator wants to\narchive/delete the Audit
DELETED --> [*]
@enduml

View File

@@ -0,0 +1,24 @@
@startuml
actor Administrator
Administrator -> "Watcher CLI" : watcher audit-create -a <audit_template_uuid>
"Watcher CLI" -> "Watcher API" : POST audit(parameters)
"Watcher API" -> "Watcher Database" : create new audit in database (status=PENDING)
"Watcher API" <-- "Watcher Database" : new audit uuid
"Watcher CLI" <-- "Watcher API" : return new audit URL
Administrator <-- "Watcher CLI" : new audit uuid
"Watcher API" -> "AMQP Bus" : trigger_audit(new_audit.uuid)
"AMQP Bus" -> "Watcher Decision Engine" : trigger_audit(new_audit.uuid)
ref over "Watcher Decision Engine"
Trigger audit in the
Watcher Decision Engine
end ref
@enduml

View File

@@ -0,0 +1,16 @@
@startuml
actor Administrator
Administrator -> "Watcher CLI" : watcher audit-template-create <name> <goal>
"Watcher CLI" -> "Watcher API" : POST audit_template(parameters)
"Watcher API" -> "Watcher API" : make sure goal exist in configuration
"Watcher API" -> "Watcher Database" : create new audit_template in database
"Watcher API" <-- "Watcher Database" : new audit template uuid
"Watcher CLI" <-- "Watcher API" : return new audit template URL in HTTP Location Header
Administrator <-- "Watcher CLI" : new audit template uuid
@enduml

View File

@@ -0,0 +1,23 @@
@startuml
actor Administrator
Administrator -> "Watcher CLI" : watcher action-plan-start <action_plan_uuid>
"Watcher CLI" -> "Watcher API" : PATCH action_plan(state=TRIGGERED)
"Watcher API" -> "Watcher Database" : action_plan.state=TRIGGERED
"Watcher CLI" <-- "Watcher API" : HTTP 200
Administrator <-- "Watcher CLI" : OK
"Watcher API" -> "AMQP Bus" : launch_action_plan(action_plan.uuid)
"AMQP Bus" -> "Watcher Applier" : launch_action_plan(action_plan.uuid)
ref over "Watcher Applier"
Launch Action Plan in the
Watcher Applier
end ref
@enduml

View File

@@ -0,0 +1,31 @@
@startuml
"AMQP Bus" -> "Watcher Applier" : launch_action_plan(action_plan.uuid)
"Watcher Applier" -> "Watcher Database" : action_plan.state=ONGOING
"Watcher Applier" -[#blue]> "AMQP Bus" : notify action plan state = ONGOING
"Watcher Applier" -> "Watcher Database" : get_action_list(action_plan.uuid)
"Watcher Applier" <-- "Watcher Database" : actions
loop for each action of the action flow
create Action
"Watcher Applier" -> Action : instantiate Action object with target resource id\n and input parameters
"Watcher Applier" -> Action : validate_parameters()
"Watcher Applier" <-- Action : OK
"Watcher Applier" -[#blue]> "AMQP Bus" : notify action state = ONGOING
"Watcher Applier" -> Action : preconditions()
"Watcher Applier" <-- Action : OK
"Watcher Applier" -> Action : execute()
alt action is "migrate instance"
Action -> "Nova API" : migrate(instance_id, dest_host_id)
Action <-- "Nova API" : OK
else action is "disable hypervisor"
Action -> "Nova API" : host-update(host_id, maintenance=true)
Action <-- "Nova API" : OK
end
"Watcher Applier" <-- Action : OK
"Watcher Applier" -> "Watcher Database" : action.state=SUCCEEDED
"Watcher Applier" -[#blue]> "AMQP Bus" : notify action state = SUCCEEDED
end
"Watcher Applier" -> "Watcher Database" : action_plan.state=SUCCEEDED
"Watcher Applier" -[#blue]> "AMQP Bus" : notify action plan state = SUCCEEDED
@enduml

View File

@@ -0,0 +1,37 @@
@startuml
actor Administrator
== Create some Audit settings ==
Administrator -> Watcher : create new Audit Template (i.e. Audit settings : goal, scope, deadline,...)
Watcher -> Watcher : save Audit Template in database
Administrator <-- Watcher : Audit Template UUID
== Launch a new Audit ==
Administrator -> Watcher : launch new Audit of the Openstack infrastructure resources\nwith a previously created Audit Template
Administrator <-- Watcher : Audit UUID
Administrator -> Watcher : get the Audit state
Administrator <-- Watcher : ONGOING
Watcher -> Watcher : compute a solution to achieve optimization goal
Administrator -> Watcher : get the Audit state
Administrator <-- Watcher : SUCCEEDED
== Get the result of the Audit ==
Administrator -> Watcher : get Action Plan
Administrator <-- Watcher : recommended Action Plan and estimated efficacy
Administrator -> Administrator : verify the recommended actions\nand evaluate the estimated gain vs aggressiveness of the solution
== Launch the recommended Action Plan ==
Administrator -> Watcher : launch the Action Plan
Administrator <-- Watcher : Action Plan has been launched
Watcher -> Watcher : trigger Actions on Openstack services
Administrator -> Watcher : get the Action Plan state
Administrator <-- Watcher : ONGOING
Administrator -> Watcher : get the Action Plan state
Administrator <-- Watcher : SUCCEEDED
@enduml

View File

@@ -0,0 +1,31 @@
@startuml
"AMQP Bus" -> "Watcher Decision Engine" : trigger_audit(new_audit.uuid)
"Watcher Decision Engine" -> "Watcher Database" : update audit.state = ONGOING
"AMQP Bus" <[#blue]- "Watcher Decision Engine" : notify new audit state = ONGOING
"Watcher Decision Engine" -> "Watcher Database" : get audit parameters(goal, ...)
"Watcher Decision Engine" <-- "Watcher Database" : audit parameters(goal, ...)
create Strategy
"Watcher Decision Engine" -[#red]> "Strategy": select appropriate\noptimization strategy
loop while enough data to build cluster data model
"Watcher Decision Engine" -> "Nova API" : get resource state (host, instance, ...)
"Watcher Decision Engine" <-- "Nova API" : resource state
end
"Watcher Decision Engine" -[#red]> "Watcher Decision Engine": build cluster_data_model
"Watcher Decision Engine" -> "Strategy" : execute(cluster_data_model)
loop while enough history data for the strategy
"Strategy" -> "Ceilometer API": get_aggregated_metrics\n(resource_id,meter_name,period,aggregate_method)
"Strategy" <-- "Ceilometer API": aggregated metrics
end
"Strategy" -> "Strategy" : compute solution to achieve goal
"Watcher Decision Engine" <-- "Strategy" : solution = array of actions (i.e. not scheduled yet)
create "Watcher Planner"
"Watcher Decision Engine" -[#red]> "Watcher Planner": select appropriate actions scheduler (i.e. Planner implementation)
"Watcher Decision Engine" -> "Watcher Planner": schedule(audit_id, solution)
"Watcher Planner" -> "Watcher Planner": schedule actions according to\nscheduling rules/policies
"Watcher Decision Engine" <-- "Watcher Planner": new action_plan
"Watcher Decision Engine" -> "Watcher Database" : save new action_plan in database
"Watcher Decision Engine" -> "Watcher Database" : update audit.state = SUCCEEDED
"AMQP Bus" <[#blue]- "Watcher Decision Engine" : notify new audit state = SUCCEEDED
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -0,0 +1,302 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="60cm" height="27cm" viewBox="-181 -239 1196 535" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<rect style="fill: #b3d6c6" x="-169.614" y="-237" width="1173" height="532"/>
<path style="fill: #b3d6c6" d="M -169.614,-237 A 10,10 0 0 0 -179.614,-227 L -169.614,-227 z"/>
<path style="fill: #b3d6c6" d="M 1013.39,-227 A 10,10 0 0 0 1003.39,-237 L 1003.39,-227 z"/>
<rect style="fill: #b3d6c6" x="-179.614" y="-227" width="1193" height="512"/>
<path style="fill: #b3d6c6" d="M -179.614,285 A 10,10 0 0 0 -169.614,295 L -169.614,285 z"/>
<path style="fill: #b3d6c6" d="M 1003.39,295 A 10,10 0 0 0 1013.39,285 L 1003.39,285 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ffffff" x1="-169.614" y1="-237" x2="1003.39" y2="-237"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ffffff" x1="-169.614" y1="295" x2="1003.39" y2="295"/>
<path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ffffff" d="M -169.614,-237 A 10,10 0 0 0 -179.614,-227"/>
<path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ffffff" d="M 1013.39,-227 A 10,10 0 0 0 1003.39,-237"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ffffff" x1="-179.614" y1="-227" x2="-179.614" y2="285"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ffffff" x1="1013.39" y1="-227" x2="1013.39" y2="285"/>
<path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ffffff" d="M -179.614,285 A 10,10 0 0 0 -169.614,295"/>
<path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ffffff" d="M 1003.39,295 A 10,10 0 0 0 1013.39,285"/>
<text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="416.886" y="32.9002">
<tspan x="416.886" y="32.9002"></tspan>
</text>
</g>
<g>
<rect style="fill: #00cd78" x="244.1" y="-158" width="82.287" height="260"/>
<path style="fill: #00cd78" d="M 244.1,-158 A 10,10 0 0 0 234.1,-148 L 244.1,-148 z"/>
<path style="fill: #00cd78" d="M 336.387,-148 A 10,10 0 0 0 326.387,-158 L 326.387,-148 z"/>
<rect style="fill: #00cd78" x="234.1" y="-148" width="102.287" height="240"/>
<path style="fill: #00cd78" d="M 234.1,92 A 10,10 0 0 0 244.1,102 L 244.1,92 z"/>
<path style="fill: #00cd78" d="M 326.387,102 A 10,10 0 0 0 336.387,92 L 326.387,92 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="244.1" y1="-158" x2="326.387" y2="-158"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="244.1" y1="102" x2="326.387" y2="102"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 244.1,-158 A 10,10 0 0 0 234.1,-148"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 336.387,-148 A 10,10 0 0 0 326.387,-158"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="234.1" y1="-148" x2="234.1" y2="92"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="336.387" y1="-148" x2="336.387" y2="92"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 234.1,92 A 10,10 0 0 0 244.1,102"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 326.387,102 A 10,10 0 0 0 336.387,92"/>
<text font-size="12.8" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="285.243" y="-32.1">
<tspan x="285.243" y="-32.1">AMQP</tspan>
<tspan x="285.243" y="-16.1">bus</tspan>
</text>
</g>
<g>
<rect style="fill: #ffb400" x="407" y="-38.25" width="325.386" height="126.418"/>
<path style="fill: #ffb400" d="M 407,-38.25 A 10,10 0 0 0 397,-28.25 L 407,-28.25 z"/>
<path style="fill: #ffb400" d="M 742.386,-28.25 A 10,10 0 0 0 732.386,-38.25 L 732.386,-28.25 z"/>
<rect style="fill: #ffb400" x="397" y="-28.25" width="345.386" height="106.418"/>
<path style="fill: #ffb400" d="M 397,78.168 A 10,10 0 0 0 407,88.168 L 407,78.168 z"/>
<path style="fill: #ffb400" d="M 732.386,88.168 A 10,10 0 0 0 742.386,78.168 L 732.386,78.168 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="407" y1="-38.25" x2="732.386" y2="-38.25"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="407" y1="88.168" x2="732.386" y2="88.168"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 407,-38.25 A 10,10 0 0 0 397,-28.25"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 742.386,-28.25 A 10,10 0 0 0 732.386,-38.25"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="397" y1="-28.25" x2="397" y2="78.168"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="742.386" y1="-28.25" x2="742.386" y2="78.168"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 397,78.168 A 10,10 0 0 0 407,88.168"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 732.386,88.168 A 10,10 0 0 0 742.386,78.168"/>
<text font-size="14.1111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="569.693" y="20.4201">
<tspan x="569.693" y="20.4201">Watcher</tspan>
<tspan x="569.693" y="38.059">Decision Engine</tspan>
</text>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 6; stroke: #000000" x1="346.122" y1="37.0905" x2="387.264" y2="37.4729"/>
<polygon style="fill: #000000" points="338.622,37.0208 348.668,32.1139 346.122,37.0905 348.575,42.1135 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="338.622,37.0208 348.668,32.1139 346.122,37.0905 348.575,42.1135 "/>
<polygon style="fill: #000000" points="394.764,37.5426 384.718,42.4495 387.264,37.4729 384.811,32.4499 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="394.764,37.5426 384.718,42.4495 387.264,37.4729 384.811,32.4499 "/>
</g>
<g>
<rect style="fill: #ffb400" x="407" y="-159" width="160" height="89"/>
<path style="fill: #ffb400" d="M 407,-159 A 10,10 0 0 0 397,-149 L 407,-149 z"/>
<path style="fill: #ffb400" d="M 577,-149 A 10,10 0 0 0 567,-159 L 567,-149 z"/>
<rect style="fill: #ffb400" x="397" y="-149" width="180" height="69"/>
<path style="fill: #ffb400" d="M 397,-80 A 10,10 0 0 0 407,-70 L 407,-80 z"/>
<path style="fill: #ffb400" d="M 567,-70 A 10,10 0 0 0 577,-80 L 567,-80 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="407" y1="-159" x2="567" y2="-159"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="407" y1="-70" x2="567" y2="-70"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 407,-159 A 10,10 0 0 0 397,-149"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 577,-149 A 10,10 0 0 0 567,-159"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="397" y1="-149" x2="397" y2="-80"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="577" y1="-149" x2="577" y2="-80"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 397,-80 A 10,10 0 0 0 407,-70"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 567,-70 A 10,10 0 0 0 577,-80"/>
<text font-size="14.1111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="487" y="-119.039">
<tspan x="487" y="-119.039">Watcher</tspan>
<tspan x="487" y="-101.4">Actions Applier</tspan>
</text>
</g>
<g>
<rect style="fill: #ffb400" x="63.086" y="-136.5" width="70.7" height="157"/>
<path style="fill: #ffb400" d="M 63.086,-136.5 A 10,10 0 0 0 53.086,-126.5 L 63.086,-126.5 z"/>
<path style="fill: #ffb400" d="M 143.786,-126.5 A 10,10 0 0 0 133.786,-136.5 L 133.786,-126.5 z"/>
<rect style="fill: #ffb400" x="53.086" y="-126.5" width="90.7" height="137"/>
<path style="fill: #ffb400" d="M 53.086,10.5 A 10,10 0 0 0 63.086,20.5 L 63.086,10.5 z"/>
<path style="fill: #ffb400" d="M 133.786,20.5 A 10,10 0 0 0 143.786,10.5 L 133.786,10.5 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="63.086" y1="-136.5" x2="133.786" y2="-136.5"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="63.086" y1="20.5" x2="133.786" y2="20.5"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 63.086,-136.5 A 10,10 0 0 0 53.086,-126.5"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 143.786,-126.5 A 10,10 0 0 0 133.786,-136.5"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="53.086" y1="-126.5" x2="53.086" y2="10.5"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="143.786" y1="-126.5" x2="143.786" y2="10.5"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 53.086,10.5 A 10,10 0 0 0 63.086,20.5"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 133.786,20.5 A 10,10 0 0 0 143.786,10.5"/>
<text font-size="14.1111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="98.436" y="-62.5389">
<tspan x="98.436" y="-62.5389">Watcher</tspan>
<tspan x="98.436" y="-44.9">API</tspan>
</text>
</g>
<g>
<rect style="fill: #ffb400" x="-138.614" y="-124.5" width="105" height="69.1"/>
<path style="fill: #ffb400" d="M -138.614,-124.5 A 10,10 0 0 0 -148.614,-114.5 L -138.614,-114.5 z"/>
<path style="fill: #ffb400" d="M -23.614,-114.5 A 10,10 0 0 0 -33.614,-124.5 L -33.614,-114.5 z"/>
<rect style="fill: #ffb400" x="-148.614" y="-114.5" width="125" height="49.1"/>
<path style="fill: #ffb400" d="M -148.614,-65.4 A 10,10 0 0 0 -138.614,-55.4 L -138.614,-65.4 z"/>
<path style="fill: #ffb400" d="M -33.614,-55.4 A 10,10 0 0 0 -23.614,-65.4 L -33.614,-65.4 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="-138.614" y1="-124.5" x2="-33.614" y2="-124.5"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="-138.614" y1="-55.4" x2="-33.614" y2="-55.4"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M -138.614,-124.5 A 10,10 0 0 0 -148.614,-114.5"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M -23.614,-114.5 A 10,10 0 0 0 -33.614,-124.5"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="-148.614" y1="-114.5" x2="-148.614" y2="-65.4"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="-23.614" y1="-114.5" x2="-23.614" y2="-65.4"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M -148.614,-65.4 A 10,10 0 0 0 -138.614,-55.4"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M -33.614,-55.4 A 10,10 0 0 0 -23.614,-65.4"/>
<text font-size="14.1111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="-86.114" y="-94.4889">
<tspan x="-86.114" y="-94.4889">Watcher</tspan>
<tspan x="-86.114" y="-76.85">CLI</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff" x="-155.614" y="-29.5" width="117" height="56"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="-155.614" y="-29.5" width="117" height="56"/>
<text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="-97.114" y="2.4">
<tspan x="-97.114" y="2.4">Horizon</tspan>
</text>
</g>
<g>
<rect style="fill: #ffb400" x="-111.614" y="12.6" width="90" height="59.9"/>
<path style="fill: #ffb400" d="M -111.614,12.6 A 10,10 0 0 0 -121.614,22.6 L -111.614,22.6 z"/>
<path style="fill: #ffb400" d="M -11.614,22.6 A 10,10 0 0 0 -21.614,12.6 L -21.614,22.6 z"/>
<rect style="fill: #ffb400" x="-121.614" y="22.6" width="110" height="39.9"/>
<path style="fill: #ffb400" d="M -121.614,62.5 A 10,10 0 0 0 -111.614,72.5 L -111.614,62.5 z"/>
<path style="fill: #ffb400" d="M -21.614,72.5 A 10,10 0 0 0 -11.614,62.5 L -21.614,62.5 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="-111.614" y1="12.6" x2="-21.614" y2="12.6"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="-111.614" y1="72.5" x2="-21.614" y2="72.5"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M -111.614,12.6 A 10,10 0 0 0 -121.614,22.6"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M -11.614,22.6 A 10,10 0 0 0 -21.614,12.6"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="-121.614" y1="22.6" x2="-121.614" y2="62.5"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="-11.614" y1="22.6" x2="-11.614" y2="62.5"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M -121.614,62.5 A 10,10 0 0 0 -111.614,72.5"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M -21.614,72.5 A 10,10 0 0 0 -11.614,62.5"/>
<text font-size="14.1111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="-66.614" y="38.0111">
<tspan x="-66.614" y="38.0111">Watcher</tspan>
<tspan x="-66.614" y="55.65">plugin</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff" x="644.986" y="-121.4" width="117" height="56"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="644.986" y="-121.4" width="117" height="56"/>
<text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="703.486" y="-89.5">
<tspan x="703.486" y="-89.5">Nova</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff" x="822.786" y="122.5" width="167.6" height="59.501"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="822.786" y="122.5" width="167.6" height="59.501"/>
<text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="906.586" y="156.151">
<tspan x="906.586" y="156.151">MariaDB Database</tspan>
</text>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="337" y1="-121" x2="387.264" y2="-121"/>
<polygon style="fill: #000000" points="394.764,-121 384.764,-116 387.264,-121 384.764,-126 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="394.764,-121 384.764,-116 387.264,-121 384.764,-126 "/>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 6; stroke: #000000" x1="346.121" y1="-92.8795" x2="387.265" y2="-92.3705"/>
<polygon style="fill: #000000" points="338.622,-92.9723 348.683,-97.8482 346.121,-92.8795 348.559,-87.849 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="338.622,-92.9723 348.683,-97.8482 346.121,-92.8795 348.559,-87.849 "/>
<polygon style="fill: #000000" points="394.764,-92.2777 384.703,-87.4018 387.265,-92.3705 384.827,-97.401 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="394.764,-92.2777 384.703,-87.4018 387.265,-92.3705 384.827,-97.401 "/>
</g>
<g>
<rect style="fill: #ffffff" x="522.61" y="138.001" width="137.55" height="38"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="522.61" y="138.001" width="137.55" height="38"/>
<text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="591.385" y="160.901">
<tspan x="591.385" y="160.901">Ceilometer API</tspan>
</text>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="409.386" y1="106.75" x2="374.606" y2="138.218"/>
<polygon style="fill: #000000" points="369.044,143.25 373.105,132.833 374.606,138.218 379.814,140.248 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="369.044,143.25 373.105,132.833 374.606,138.218 379.814,140.248 "/>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="-23.614" y1="-89.95" x2="44.0985" y2="-61.7438"/>
<polygon style="fill: #000000" points="51.0219,-58.8598 39.8681,-58.0896 44.0985,-61.7438 43.7134,-67.3207 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="51.0219,-58.8598 39.8681,-58.0896 44.0985,-61.7438 43.7134,-67.3207 "/>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="-11.614" y1="42.55" x2="46.0184" y2="-12.0538"/>
<polygon style="fill: #000000" points="51.4628,-17.2121 47.6424,-6.70471 46.0184,-12.0538 40.7647,-13.9639 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="51.4628,-17.2121 47.6424,-6.70471 46.0184,-12.0538 40.7647,-13.9639 "/>
</g>
<g>
<rect style="fill: #ffa200" x="422.36" y="60.1008" width="92.025" height="49.9"/>
<path style="fill: #ffa200" d="M 422.36,60.1008 A 10,10 0 0 0 412.36,70.1008 L 422.36,70.1008 z"/>
<path style="fill: #ffa200" d="M 524.385,70.1008 A 10,10 0 0 0 514.385,60.1008 L 514.385,70.1008 z"/>
<rect style="fill: #ffa200" x="412.36" y="70.1008" width="112.025" height="29.9"/>
<path style="fill: #ffa200" d="M 412.36,100.001 A 10,10 0 0 0 422.36,110.001 L 422.36,100.001 z"/>
<path style="fill: #ffa200" d="M 514.385,110.001 A 10,10 0 0 0 524.385,100.001 L 514.385,100.001 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="422.36" y1="60.1008" x2="514.385" y2="60.1008"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="422.36" y1="110.001" x2="514.385" y2="110.001"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 422.36,60.1008 A 10,10 0 0 0 412.36,70.1008"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 524.385,70.1008 A 10,10 0 0 0 514.385,60.1008"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="412.36" y1="70.1008" x2="412.36" y2="100.001"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="524.385" y1="70.1008" x2="524.385" y2="100.001"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 412.36,100.001 A 10,10 0 0 0 422.36,110.001"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 514.385,110.001 A 10,10 0 0 0 524.385,100.001"/>
<text font-size="14.1111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="468.372" y="89.3314">
<tspan x="468.372" y="89.3314">Strategy</tspan>
</text>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="529.386" y1="109.75" x2="550.79" y2="126.911"/>
<polygon style="fill: #000000" points="556.641,131.602 545.712,129.248 550.79,126.911 551.967,121.446 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="556.641,131.602 545.712,129.248 550.79,126.911 551.967,121.446 "/>
</g>
<g>
<rect style="fill: #ffa200" x="625.488" y="60.1008" width="80.5125" height="49.9"/>
<path style="fill: #ffa200" d="M 625.488,60.1008 A 10,10 0 0 0 615.488,70.1008 L 625.488,70.1008 z"/>
<path style="fill: #ffa200" d="M 716,70.1008 A 10,10 0 0 0 706,60.1008 L 706,70.1008 z"/>
<rect style="fill: #ffa200" x="615.488" y="70.1008" width="100.512" height="29.9"/>
<path style="fill: #ffa200" d="M 615.488,100.001 A 10,10 0 0 0 625.488,110.001 L 625.488,100.001 z"/>
<path style="fill: #ffa200" d="M 706,110.001 A 10,10 0 0 0 716,100.001 L 706,100.001 z"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="625.488" y1="60.1008" x2="706" y2="60.1008"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="625.488" y1="110.001" x2="706" y2="110.001"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 625.488,60.1008 A 10,10 0 0 0 615.488,70.1008"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 716,70.1008 A 10,10 0 0 0 706,60.1008"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="615.488" y1="70.1008" x2="615.488" y2="100.001"/>
<line style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" x1="716" y1="70.1008" x2="716" y2="100.001"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 615.488,100.001 A 10,10 0 0 0 625.488,110.001"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 706,110.001 A 10,10 0 0 0 716,100.001"/>
<text font-size="14.1111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="665.744" y="89.3314">
<tspan x="665.744" y="89.3314">Planner</tspan>
</text>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="337" y1="4" x2="387.264" y2="4"/>
<polygon style="fill: #000000" points="394.764,4 384.764,9 387.264,4 384.764,-1 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="394.764,4 384.764,9 387.264,4 384.764,-1 "/>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="148.386" y1="-60.25" x2="218.65" y2="-60.25"/>
<polygon style="fill: #000000" points="226.15,-60.25 216.15,-55.25 218.65,-60.25 216.15,-65.25 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="226.15,-60.25 216.15,-55.25 218.65,-60.25 216.15,-65.25 "/>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="581.486" y1="-96.9139" x2="633.15" y2="-96.4987"/>
<polygon style="fill: #000000" points="640.65,-96.4384 630.61,-91.519 633.15,-96.4987 630.691,-101.519 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="640.65,-96.4384 630.61,-91.519 633.15,-96.4987 630.691,-101.519 "/>
</g>
<g>
<path style="fill: #ffb400" d="M 844 38.3333 C 865.877,23.0833 876.816,18 898.693,18 C 920.57,18 931.509,23.0833 953.386,38.3333 L 953.386,119.667 C 931.509,134.917 920.57,140 898.693,140 C 876.816,140 865.877,134.917 844,119.667 L 844,38.3333z"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 844 38.3333 C 865.877,23.0833 876.816,18 898.693,18 C 920.57,18 931.509,23.0833 953.386,38.3333 L 953.386,119.667 C 931.509,134.917 920.57,140 898.693,140 C 876.816,140 865.877,134.917 844,119.667 L 844,38.3333"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 844 38.3333 C 865.877,53.5833 876.816,58.6667 898.693,58.6667 C 920.57,58.6667 931.509,53.5833 953.386,38.3333"/>
<text font-size="12.8" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="898.693" y="93.1667">
<tspan x="898.693" y="93.1667">Watcher DB</tspan>
</text>
</g>
<g>
<path style="fill: #ff5050" d="M 285.054 172.933 C 312.92,157.683 326.854,152.6 354.72,152.6 C 382.586,152.6 396.52,157.683 424.386,172.933 L 424.386,254.267 C 396.52,269.517 382.586,274.6 354.72,274.6 C 326.854,274.6 312.92,269.517 285.054,254.267 L 285.054,172.933z"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 285.054 172.933 C 312.92,157.683 326.854,152.6 354.72,152.6 C 382.586,152.6 396.52,157.683 424.386,172.933 L 424.386,254.267 C 396.52,269.517 382.586,274.6 354.72,274.6 C 326.854,274.6 312.92,269.517 285.054,254.267 L 285.054,172.933"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 285.054 172.933 C 312.92,188.183 326.854,193.267 354.72,193.267 C 382.586,193.267 396.52,188.183 424.386,172.933"/>
<text font-size="12.8" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="354.72" y="227.767">
<tspan x="354.72" y="227.767">Cluster Model DB</tspan>
</text>
</g>
<g>
<path style="fill: #ff5050" d="M 522.176 185.933 C 551.618,170.683 566.339,165.6 595.78,165.6 C 625.222,165.6 639.943,170.683 669.385,185.933 L 669.385,267.267 C 639.943,282.517 625.222,287.6 595.78,287.6 C 566.339,287.6 551.618,282.517 522.176,267.267 L 522.176,185.933z"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 522.176 185.933 C 551.618,170.683 566.339,165.6 595.78,165.6 C 625.222,165.6 639.943,170.683 669.385,185.933 L 669.385,267.267 C 639.943,282.517 625.222,287.6 595.78,287.6 C 566.339,287.6 551.618,282.517 522.176,267.267 L 522.176,185.933"/>
<path style="fill: none; fill-opacity:0; stroke-width: 4; stroke: #ffffff" d="M 522.176 185.933 C 551.618,201.183 566.339,206.267 595.78,206.267 C 625.222,206.267 639.943,201.183 669.385,185.933"/>
<text font-size="12.8" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="595.78" y="240.767">
<tspan x="595.78" y="240.767">Cluster History DB</tspan>
</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="96.386,-142.999 96.386,-205.998 898.692,-205.998 898.692,8.26393 "/>
<polygon style="fill: #000000" points="898.692,15.7639 893.692,5.76393 898.692,8.26393 903.692,5.76393 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="898.692,15.7639 893.692,5.76393 898.692,8.26393 903.692,5.76393 "/>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="583,-142.25 583,-141.25 870,-141.25 870,7.01393 "/>
<polygon style="fill: #000000" points="870,14.5139 865,4.51393 870,7.01393 875,4.51393 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="870,14.5139 865,4.51393 870,7.01393 875,4.51393 "/>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="752.386" y1="60.75" x2="824.64" y2="60.9708"/>
<polygon style="fill: #000000" points="832.14,60.9938 822.125,65.9632 824.64,60.9708 822.156,55.9632 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="832.14,60.9938 822.125,65.9632 824.64,60.9708 822.156,55.9632 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="58cm" height="28cm" viewBox="26 8 1147 549" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<rect style="fill: #ffffff" x="570" y="99" width="148.6" height="28"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="570" y="99" width="148.6" height="28"/>
<text font-size="16" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="644.3" y="118">Audit Template</text>
</g>
<g>
<rect style="fill: #ffffff" x="212" y="140" width="177.5" height="28"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="212" y="140" width="177.5" height="28"/>
<text font-size="16" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="300.75" y="159">OpenStack Cluster</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="569.006,113 446,113 446,154 397.19,154 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="409.838,149 393.838,154 409.838,159 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="448" y="130.5">Applies to</text>
</g>
<g>
<rect style="fill: #ffffff" x="615" y="227" width="58.4" height="28"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="615" y="227" width="58.4" height="28"/>
<text font-size="16" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="644.2" y="246">Audit</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="644.2,225.996 644.2,188.497 644.3,188.497 644.3,133.705 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="649.3,146.353 644.3,130.353 639.3,146.353 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="644.25" y="185.497">gets configuration from</text>
</g>
<g>
<rect style="fill: #ffffff" x="916" y="9" width="50.45" height="28"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="916" y="9" width="50.45" height="28"/>
<text font-size="16" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="941.225" y="28">Goal</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="644.3,97.9927 644.3,72 941.225,72 941.225,44.7125 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="946.225,57.3599 941.225,41.3599 936.225,57.3599 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="792.763" y="69">Achieves</text>
</g>
<g>
<rect style="fill: #ffffff" x="495" y="367" width="112.45" height="28"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="495" y="367" width="112.45" height="28"/>
<text font-size="16" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="551.225" y="386">Action Plan</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="644.2,256 644.2,298.5 523,298.5 523,356.295 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="518,343.647 523,359.647 528,343.647 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="583.6" y="295.5">Generates</text>
</g>
<g>
<rect style="fill: #ffffff" x="682" y="471" width="67.45" height="28"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="682" y="471" width="67.45" height="28"/>
<text font-size="16" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="715.725" y="490">Action</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="551.225,420.945 551.225,443 715.725,443 715.725,470.029 "/>
<polygon style="fill: #000000" points="551.225,395.773 556.025,409.773 551.225,423.773 546.425,409.773 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="551.225,395.773 556.025,409.773 551.225,423.773 546.425,409.773 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="633.475" y="440">is composed of</text>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="562.225" y="407.773"></text>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="719.725" y="466.029"></text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="749.45,499 749.45,517 862,517 862,485 749.45,485 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="805.725" y="514">Next action</text>
<polygon style="fill: #000000" points="850.075,514 850.075,506 858.075,510 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="753.45" y="511"></text>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="753.45" y="482"></text>
</g>
<g>
<ellipse style="fill: #ffffff" cx="1036" cy="219" rx="6" ry="6"/>
<ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" cx="1036" cy="219" rx="6" ry="6"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" x1="1012" y1="231" x2="1060" y2="231"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" x1="1036" y1="225" x2="1036" y2="255"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" x1="1036" y1="255" x2="1012" y2="281"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" x1="1036" y1="255" x2="1060" y2="281"/>
<text font-size="12.8" style="fill: #ff0000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="1036" y="304.9">
<tspan x="1036" y="304.9">Administrator</tspan>
</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="985.869,255 834.138,255 834.138,241 681.113,241 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="693.76,236 677.76,241 693.76,246 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="836.138" y="245">Triggers</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="1038,192.993 882.3,192.993 882.3,113 725.305,113 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="737.953,108 721.953,113 737.953,118 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="884.3" y="149.997">Defines Audit configuration in</text>
</g>
<g>
<ellipse style="fill: #ffffff" cx="103" cy="28" rx="6" ry="6"/>
<ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" cx="103" cy="28" rx="6" ry="6"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" x1="79" y1="40" x2="127" y2="40"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" x1="103" y1="34" x2="103" y2="64"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" x1="103" y1="64" x2="79" y2="90"/>
<line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #ff0000" x1="103" y1="64" x2="127" y2="90"/>
<text font-size="12.8" style="fill: #ff0000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="103" y="113.9">
<tspan x="103" y="113.9">Customer</tspan>
</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="137.683,64 300.75,64 300.75,133.295 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="295.75,120.647 300.75,136.647 305.75,120.647 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="219.217" y="61">Consumes resources</text>
</g>
<g>
<rect style="fill: #ffffff" x="27" y="258" width="102.8" height="28"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="27" y="258" width="102.8" height="28"/>
<text font-size="16" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="78.4" y="277">Resources</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="186.828,154 78.4,154 78.4,258 "/>
<polygon style="fill: #000000" points="212,154 198,158.8 184,154 198,149.2 "/>
<polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="212,154 198,158.8 184,154 198,149.2 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="145.2" y="151"></text>
<text font-size="12.7998" style="fill: #000000;text-anchor:end;font-family:monospace;font-style:normal;font-weight:normal" x="180" y="151"></text>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="82.4" y="254"></text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="715.724,499 715.724,540 78.4,540 78.4,292.705 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="83.4,305.353 78.4,289.353 73.4,305.353 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="397.062" y="537">Modifies</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="1036,309.94 1036,381 614.155,381 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="626.803,376 610.803,381 626.803,386 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="821.725" y="378">Launches</text>
</g>
<g>
<rect style="fill: #ffffff" x="1082.9" y="43.1" width="88.25" height="28"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="1082.9" y="43.1" width="88.25" height="28"/>
<text font-size="16" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="1127.02" y="62.1">Strategy</text>
</g>
<g>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke-dasharray: 8; stroke: #000000" points="966.45,23 1020.17,23 1020.17,57.1 1075.22,57.1 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="1062.57,62.1 1078.57,57.1 1062.57,52.1 "/>
<text font-size="12.7998" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="1022.17" y="37.05">uses</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -1,17 +1,37 @@
============================================
Welcome to Watcher's developer documentation
============================================
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
Mission
=======
https://creativecommons.org/licenses/by/3.0/
Provide an auditing service for OpenStack to improve workload placement
on-the-go.
================================
Welcome to Watcher documentation
================================
The developer documentation provided here is continually kept up-to-date based
OpenStack Watcher provides a flexible and scalable resource optimization
service for multi-tenant OpenStack-based clouds.
Watcher provides a complete optimization loop—including everything from a
metrics receiver, complex event processor and profiler, optimization processor
and an action plan applier. This provides a robust framework to realize a wide
range of cloud optimization goals, including the reduction of data center
operating costs, increased system performance via intelligent virtual machine
migration, increased energy efficiency—and more!
Watcher project consists of several source code repositories:
* `watcher`_ - is the main repository. It contains code for Watcher API server,
Watcher Decision Engine and Watcher Applier.
* `python-watcherclient`_ - Client library and CLI client for Watcher.
* `watcher-dashboard`_ - Watcher Horizon plugin.
The documentation provided here is continually kept up-to-date based
on the latest code, and may not represent the state of the project at any
specific prior release.
.. _watcher: https://git.openstack.org/cgit/openstack/watcher/
.. _python-watcherclient: https://git.openstack.org/cgit/openstack/python-watcherclient/
.. _watcher-dashboard: https://git.openstack.org/cgit/openstack/watcher-dashboard/
Developer Guide
===============
@@ -21,11 +41,21 @@ Introduction
.. toctree::
:maxdepth: 1
dev/glossary
dev/architecture
dev/environment
glossary
architecture
dev/contributing
dev/plugins
Getting Started
---------------
.. toctree::
:maxdepth: 1
dev/environment
dev/devstack
deploy/configuration
dev/testing
API References
@@ -36,25 +66,48 @@ API References
webapi/v1
Plugins
-------
.. toctree::
:maxdepth: 1
dev/plugin/base-setup
dev/plugin/strategy-plugin
dev/plugin/action-plugin
dev/plugin/planner-plugin
dev/plugins
Admin Guide
===========
Overview
--------
Introduction
------------
.. toctree::
:maxdepth: 1
deploy/user-guide
deploy/installation
deploy/user-guide
Commands
--------
Watcher Manual Pages
====================
.. toctree::
:maxdepth: 1
:glob:
:maxdepth: 1
cmds/watcher-db-manage
man/*
.. # NOTE(mriedem): This is the section where we hide things that we don't
# actually want in the table of contents but sphinx build would fail if
# they aren't in the toctree somewhere. For example, we hide api/autoindex
# since that's already covered with modindex below.
.. toctree::
:hidden:
api/autoindex
Indices and tables

View File

@@ -0,0 +1,5 @@
BUGS
====
* Watcher bugs are tracked in Launchpad at `OpenStack Watcher
<http://bugs.launchpad.net/watcher>`__

View File

@@ -0,0 +1,66 @@
**-h, --help**
Show the help message and exit
**--version**
Print the version number and exit
**-v, --verbose**
Print more verbose output
**--noverbose**
Disable verbose output
**-d, --debug**
Print debugging output (set logging level to DEBUG instead of
default WARNING level)
**--nodebug**
Disable debugging output
**--use-syslog**
Use syslog for logging
**--nouse-syslog**
Disable the use of syslog for logging
**--syslog-log-facility SYSLOG_LOG_FACILITY**
syslog facility to receive log lines
**--config-dir DIR**
Path to a config directory to pull \*.conf files from. This
file set is sorted, to provide a predictable parse order
if individual options are over-ridden. The set is parsed after
the file(s) specified via previous --config-file, arguments hence
over-ridden options in the directory take precedence. This means
that configuration from files in a specified config-dir will
always take precedence over configuration from files specified
by --config-file, regardless to argument order.
**--config-file PATH**
Path to a config file to use. Multiple config files can be
specified by using this flag multiple times, for example,
--config-file <file1> --config-file <file2>. Values in latter
files take precedence.
**--log-config-append PATH** **--log-config PATH**
The name of logging configuration file. It does not
disable existing loggers, but just appends specified
logging configuration to any other existing logging
options. Please see the Python logging module documentation
for details on logging configuration files. The log-config
name for this option is depcrecated.
**--log-format FORMAT**
A logging.Formatter log message format string which may use any
of the available logging.LogRecord attributes. Default: None
**--log-date-format DATE_FORMAT**
Format string for %(asctime)s in log records. Default: None
**--log-file PATH, --logfile PATH**
(Optional) Name of log file to output to. If not set, logging
will go to stdout.
**--log-dir LOG_DIR, --logdir LOG_DIR**
(Optional) The directory to keep log files in (will be prepended
to --log-file)

View File

@@ -0,0 +1,39 @@
===========
watcher-api
===========
---------------------------
Service for the Watcher API
---------------------------
:Author: openstack@lists.launchpad.net
:Date:
:Copyright: OpenStack Foundation
:Version:
:Manual section: 1
:Manual group: cloud computing
SYNOPSIS
========
watcher-api [options]
DESCRIPTION
===========
watcher-api is a server daemon that serves the Watcher API
OPTIONS
=======
**General options**
.. include:: general-options.rst
FILES
=====
**/etc/watcher/watcher.conf**
Default configuration file for Watcher API
.. include:: footer.rst

View File

@@ -0,0 +1,39 @@
===============
watcher-applier
===============
-------------------------------
Service for the Watcher Applier
-------------------------------
:Author: openstack@lists.launchpad.net
:Date:
:Copyright: OpenStack Foundation
:Version:
:Manual section: 1
:Manual group: cloud computing
SYNOPSIS
========
watcher-applier [options]
DESCRIPTION
===========
:ref:`Watcher Applier <watcher_applier_definition>`
OPTIONS
=======
**General options**
.. include:: general-options.rst
FILES
=====
**/etc/watcher/watcher.conf**
Default configuration file for Watcher Applier
.. include:: footer.rst

View File

@@ -1,8 +1,14 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
.. _watcher-db-manage:
=============
=================
watcher-db-manage
=============
=================
The :command:`watcher-db-manage` utility is used to create the database schema
tables that the watcher services will use for storage. It can also be used to
@@ -54,7 +60,8 @@ Usage
=====
Options for the various :ref:`commands <db-manage_cmds>` for
:command:`watcher-db-manage` are listed when the :option:`-h` or :option:`--help`
:command:`watcher-db-manage` are listed when the :option:`-h` or
:option:`--help`
option is used after the command.
For example::
@@ -81,8 +88,9 @@ If no configuration file is specified with the :option:`--config-file` option,
Command Options
===============
:command:`watcher-db-manage` is given a command that tells the utility what actions
to perform. These commands can take arguments. Several commands are available:
:command:`watcher-db-manage` is given a command that tells the utility
what actions to perform.
These commands can take arguments. Several commands are available:
.. _create_schema:

View File

@@ -0,0 +1,39 @@
=======================
watcher-decision-engine
=======================
---------------------------------------
Service for the Watcher Decision Engine
---------------------------------------
:Author: openstack@lists.launchpad.net
:Date:
:Copyright: OpenStack Foundation
:Version:
:Manual section: 1
:Manual group: cloud computing
SYNOPSIS
========
watcher-decision-engine [options]
DESCRIPTION
===========
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>`
OPTIONS
=======
**General options**
.. include:: general-options.rst
FILES
=====
**/etc/watcher/watcher.conf**
Default configuration file for Watcher Decision Engine
.. include:: footer.rst

View File

@@ -1 +0,0 @@
.. include:: ../../README.rst

View File

@@ -1,7 +0,0 @@
========
Usage
========
To use watcher in a project::
import watcher

View File

@@ -1,12 +1,31 @@
=====================
RESTful Web API (v1)
=====================
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
====================
RESTful Web API (v1)
====================
Goals
=====
.. rest-controller:: watcher.api.controllers.v1.goal:GoalsController
:webprefix: /v1/goal
.. autotype:: watcher.api.controllers.v1.goal.GoalCollection
:members:
.. autotype:: watcher.api.controllers.v1.goal.Goal
:members:
Audit Templates
===============
.. rest-controller:: watcher.api.controllers.v1.audit_template:AuditTemplatesController
:webprefix: /v1/audit_template
:webprefix: /v1/audit_templates
.. autotype:: watcher.api.controllers.v1.audit_template.AuditTemplateCollection
:members:
@@ -14,7 +33,6 @@ Audit Templates
.. autotype:: watcher.api.controllers.v1.audit_template.AuditTemplate
:members:
Audits
======
@@ -27,24 +45,22 @@ Audits
.. autotype:: watcher.api.controllers.v1.audit.Audit
:members:
Links
=====
.. autotype:: watcher.api.controllers.link.Link
:members:
ActionPlans
===========
Action Plans
============
.. rest-controller:: watcher.api.controllers.v1.action_plan:ActionPlansController
:webprefix: /v1/action_plans
.. autotype:: watcher.api.controllers.v1.action_plan.ActionPlan
.. autotype:: watcher.api.controllers.v1.action_plan.ActionPlanCollection
:members:
.. autotype:: watcher.api.controllers.v1.action_plan.ActionPlanCollection
.. autotype:: watcher.api.controllers.v1.action_plan.ActionPlan
:members:

File diff suppressed because it is too large Load Diff

View File

@@ -2,28 +2,32 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
enum34;python_version=='2.7' or python_version=='2.6'
jsonpatch>=1.1
keystonemiddleware>=2.0.0,!=2.4.0
oslo.config>=2.3.0 # Apache-2.0
oslo.db>=2.4.1 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0
oslo.log>=1.8.0 # Apache-2.0
oslo.messaging>=1.16.0,!=1.17.0,!=1.17.1,!=2.6.0,!=2.6.1 # Apache-2.0
oslo.policy>=0.5.0 # Apache-2.0
oslo.service>=0.7.0 # Apache-2.0
oslo.utils>=2.0.0,!=2.6.0 # Apache-2.0
PasteDeploy>=1.5.0
pbr>=1.6
pecan>=1.0.0
python-ceilometerclient>=1.5.0
python-cinderclient>=1.3.1
python-glanceclient>=0.18.0
python-keystoneclient>=1.6.0,!=1.8.0
python-neutronclient>=2.6.0
python-novaclient>=2.28.1,!=2.33.0
python-openstackclient>=1.5.0
six>=1.9.0
SQLAlchemy>=0.9.9,<1.1.0
stevedore>=1.5.0 # Apache-2.0
WSME>=0.7
enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
jsonpatch>=1.1 # BSD
keystoneauth1>=2.1.0 # Apache-2.0
keystonemiddleware!=4.1.0,>=4.0.0 # Apache-2.0
oslo.config>=3.7.0 # Apache-2.0
oslo.db>=4.1.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0
oslo.messaging>=4.0.0 # Apache-2.0
oslo.policy>=0.5.0 # Apache-2.0
oslo.service>=1.0.0 # Apache-2.0
oslo.utils>=3.5.0 # Apache-2.0
PasteDeploy>=1.5.0 # MIT
pbr>=1.6 # Apache-2.0
pecan>=1.0.0 # BSD
voluptuous>=0.8.6 # BSD License
python-ceilometerclient>=2.2.1 # Apache-2.0
python-cinderclient>=1.3.1 # Apache-2.0
python-glanceclient>=1.2.0 # Apache-2.0
python-keystoneclient!=1.8.0,!=2.1.0,>=1.6.0 # Apache-2.0
python-neutronclient>=2.6.0 # Apache-2.0
python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
python-openstackclient>=2.1.0 # Apache-2.0
six>=1.9.0 # MIT
SQLAlchemy<1.1.0,>=1.0.10 # MIT
stevedore>=1.5.0 # Apache-2.0
taskflow>=1.26.0 # Apache-2.0
WebOb>=1.2.3 # MIT
WSME>=0.8 # MIT

View File

@@ -16,12 +16,12 @@ classifier =
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 =
watcher
watcher_tempest_plugin
data_files =
etc/ = etc/*
@@ -39,16 +39,44 @@ console_scripts =
watcher-decision-engine = watcher.cmd.decisionengine:main
watcher-applier = watcher.cmd.applier:main
tempest.test_plugins =
watcher_tests = watcher_tempest_plugin.plugin:WatcherTempestPlugin
watcher.database.migration_backend =
sqlalchemy = watcher.db.sqlalchemy.migration
watcher_strategies =
dummy = watcher.decision_engine.strategy.dummy_strategy:DummyStrategy
basic = watcher.decision_engine.strategy.basic_consolidation:BasicConsolidation
dummy = watcher.decision_engine.strategy.strategies.dummy_strategy:DummyStrategy
basic = watcher.decision_engine.strategy.strategies.basic_consolidation:BasicConsolidation
outlet_temp_control = watcher.decision_engine.strategy.strategies.outlet_temp_control:OutletTempControl
watcher_actions =
migrate = watcher.applier.actions.migration:Migrate
nop = watcher.applier.actions.nop:Nop
sleep = watcher.applier.actions.sleep:Sleep
change_nova_service_state = watcher.applier.actions.change_nova_service_state:ChangeNovaServiceState
watcher_workflow_engines =
taskflow = watcher.applier.workflow_engine.default:DefaultWorkFlowEngine
watcher_planners =
default = watcher.decision_engine.planner.default:DefaultPlanner
[pbr]
warnerrors = true
autodoc_index_modules = true
autodoc_exclude_modules =
watcher.db.sqlalchemy.alembic.env
watcher.db.sqlalchemy.alembic.versions.*
watcher.tests.*
watcher_tempest_plugin.*
watcher.doc
[build_sphinx]
source-dir = doc/source
build-dir = doc/build
fresh_env = 1
all_files = 1
[upload_sphinx]
@@ -65,6 +93,6 @@ output_dir = watcher/locale
input_file = watcher/locale/watcher.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
keywords = _ gettext ngettext l_ lazy_gettext _LI _LW _LE _LC
mapping_file = babel.cfg
output_file = watcher/locale/watcher.pot

3
setup.py Executable file → Normal file
View File

@@ -1,4 +1,3 @@
#!/usr/bin/env python
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
setup_requires=['pbr'],
setup_requires=['pbr>=1.8'],
pbr=True)

View File

@@ -2,20 +2,19 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
hacking<0.11,>=0.10
coverage>=3.6
discover
python-subunit>=0.0.18
oslotest>=1.10.0 # Apache-2.0
testrepository>=0.0.18
testscenarios>=0.4
testtools>=1.4.0
mock>=1.2
coverage>=3.6 # Apache-2.0
discover # BSD
doc8 # Apache-2.0
hacking<0.11,>=0.10.2
mock>=1.2 # BSD
oslotest>=1.10.0 # Apache-2.0
os-testr>=0.4.1 # Apache-2.0
python-subunit>=0.0.18 # Apache-2.0/BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
# Doc requirements
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
oslosphinx>=2.5.0 # Apache-2.0
sphinxcontrib-pecanwsme>=0.8
# For PyPI distribution
twine
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
sphinxcontrib-pecanwsme>=0.8 # Apache-2.0

41
tox.ini
View File

@@ -1,31 +1,41 @@
[tox]
minversion = 1.6
envlist = py33,py34,py27,pypy,pep8
envlist = py34,py27,pep8
skipsdist = True
[testenv]
usedevelop = True
whitelist_externals = find
install_command = pip install -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = python setup.py testr --slowest --testr-args='{posargs}'
commands =
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
ostestr --concurrency=6 {posargs}
[testenv:pep8]
commands = flake8
commands =
doc8 doc/source/ CONTRIBUTING.rst HACKING.rst README.rst
flake8
[testenv:venv]
setenv = PYTHONHASHSEED=0
commands = {posargs}
[testenv:cover]
commands = python setup.py testr --coverage --omit="watcher/tests/*,watcher/openstack/*" --testr-args='{posargs}'
commands = python setup.py testr --coverage --testr-args='{posargs}'
[testenv:docs]
commands = python setup.py build_sphinx
setenv = PYTHONHASHSEED=0
commands =
doc8 doc/source/ CONTRIBUTING.rst HACKING.rst README.rst
python setup.py build_sphinx
[testenv:debug]
commands = oslo_debug_helper {posargs}
commands = oslo_debug_helper -t watcher/tests {posargs}
[testenv:config]
sitepackages = False
@@ -38,17 +48,18 @@ commands =
--output-file etc/watcher/watcher.conf.sample
[flake8]
# E123, E125 skipped as they are invalid PEP-8.
show-source=True
ignore=E123,E125,H404,H405,H305
ignore=
builtins= _
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,*sqlalchemy/alembic/versions/*,demo/
[testenv:pypi]
commands =
python setup.py sdist bdist_wheel
twine upload --config-file .pypirc {posargs} dist/*
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,*sqlalchemy/alembic/versions/*,demo/
[testenv:wheel]
commands = python setup.py bdist_wheel
[hacking]
import_exceptions = watcher._i18n
[doc8]
extension=.rst
# todo: stop ignoring doc/source/man when https://bugs.launchpad.net/doc8/+bug/1502391 is fixed
ignore-path=doc/source/image_src,doc/source/man,doc/source/api

46
watcher/_i18n.py Normal file
View File

@@ -0,0 +1,46 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# 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 oslo_i18n
# The domain is the name of the App which is used to generate the folder
# containing the translation files (i.e. the .pot file and the various locales)
DOMAIN = "watcher"
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
# The primary translation function using the well-known name "_"
_ = _translators.primary
# The contextual translation function using the name "_C"
_C = _translators.contextual_form
# The plural translation function using the name "_P"
_P = _translators.plural_form
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
def get_available_languages():
return oslo_i18n.get_available_languages(DOMAIN)

View File

@@ -1,6 +0,0 @@
# Watcher API
This component implements the REST API provided by the Watcher system to the external world. It enables a cluster administrator to control and monitor the Watcher system via any interaction mechanism connected to this API :
* CLI
* Horizon plugin
* Python SDK

View File

@@ -22,7 +22,7 @@ import pecan
from watcher.api import acl
from watcher.api import config as api_config
from watcher.api import middleware
from watcher.decision_engine.strategy.selector import default \
from watcher.decision_engine.strategy.selection import default \
as strategy_selector
# Register options for the service

View File

@@ -15,6 +15,42 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
An :ref:`Action <action_definition>` is what enables Watcher to transform the
current state of a :ref:`Cluster <cluster_definition>` after an
:ref:`Audit <audit_definition>`.
An :ref:`Action <action_definition>` is an atomic task which changes the
current state of a target :ref:`Managed resource <managed_resource_definition>`
of the OpenStack :ref:`Cluster <cluster_definition>` such as:
- Live migration of an instance from one compute node to another compute
node with Nova
- Changing the power level of a compute node (ACPI level, ...)
- Changing the current state of an hypervisor (enable or disable) with Nova
In most cases, an :ref:`Action <action_definition>` triggers some concrete
commands on an existing OpenStack module (Nova, Neutron, Cinder, Ironic, etc.).
An :ref:`Action <action_definition>` has a life-cycle and its current state may
be one of the following:
- **PENDING** : the :ref:`Action <action_definition>` has not been executed
yet by the :ref:`Watcher Applier <watcher_applier_definition>`
- **ONGOING** : the :ref:`Action <action_definition>` is currently being
processed by the :ref:`Watcher Applier <watcher_applier_definition>`
- **SUCCEEDED** : the :ref:`Action <action_definition>` has been executed
successfully
- **FAILED** : an error occured while trying to execute the
:ref:`Action <action_definition>`
- **DELETED** : the :ref:`Action <action_definition>` is still stored in the
:ref:`Watcher database <watcher_database_definition>` but is not returned
any more through the Watcher APIs.
- **CANCELLED** : the :ref:`Action <action_definition>` was in **PENDING** or
**ONGOING** state and was cancelled by the
:ref:`Administrator <administrator_definition>`
"""
import datetime
import pecan
@@ -87,29 +123,17 @@ class Action(base.APIBase):
mandatory=True)
"""The action plan this action belongs to """
description = wtypes.text
"""Description of this action"""
state = wtypes.text
"""This audit state"""
alarm = types.uuid
"""An alarm UUID related to this action"""
applies_to = wtypes.text
"""Applies to"""
src = wtypes.text
"""Hypervisor source"""
dst = wtypes.text
"""Hypervisor source"""
action_type = wtypes.text
"""Action type"""
parameter = wtypes.text
"""Additionnal parameter"""
input_parameters = types.jsontype
"""One or more key/value pairs """
next_uuid = wsme.wsproperty(types.uuid, _get_next_uuid,
_set_next_uuid,
@@ -199,7 +223,7 @@ class ActionCollection(collection.Collection):
reverse = True if kwargs['sort_dir'] == 'desc' else False
collection.actions = sorted(
collection.actions,
key=lambda action: action.next_uuid,
key=lambda action: action.next_uuid or '',
reverse=reverse)
collection.next = collection.get_next(limit, url=url, **kwargs)
@@ -229,9 +253,8 @@ class ActionsController(rest.RestController):
sort_key, sort_dir, expand=False,
resource_url=None,
action_plan_uuid=None, audit_uuid=None):
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
@@ -262,10 +285,10 @@ class ActionsController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@wsme_pecan.wsexpose(ActionCollection, types.uuid, types.uuid,
int, wtypes.text, wtypes.text, types.uuid,
@wsme_pecan.wsexpose(ActionCollection, types.uuid, int,
wtypes.text, wtypes.text, types.uuid,
types.uuid)
def get_all(self, action_uuid=None, marker=None, limit=None,
def get_all(self, marker=None, limit=None,
sort_key='id', sort_dir='asc', action_plan_uuid=None,
audit_uuid=None):
"""Retrieve a list of actions.
@@ -286,16 +309,14 @@ class ActionsController(rest.RestController):
marker, limit, sort_key, sort_dir,
action_plan_uuid=action_plan_uuid, audit_uuid=audit_uuid)
@wsme_pecan.wsexpose(ActionCollection, types.uuid,
types.uuid, int, wtypes.text, wtypes.text,
types.uuid, types.uuid)
def detail(self, action_uuid=None, marker=None, limit=None,
@wsme_pecan.wsexpose(ActionCollection, types.uuid, int,
wtypes.text, wtypes.text, types.uuid,
types.uuid)
def detail(self, marker=None, limit=None,
sort_key='id', sort_dir='asc', action_plan_uuid=None,
audit_uuid=None):
"""Retrieve a list of actions with detail.
:param action_uuid: UUID of a action, to get only actions for that
action.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
:param sort_key: column to sort results by. Default: id.

View File

@@ -15,6 +15,60 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
An :ref:`Action Plan <action_plan_definition>` is a flow of
:ref:`Actions <action_definition>` that should be executed in order to satisfy
a given :ref:`Goal <goal_definition>`.
An :ref:`Action Plan <action_plan_definition>` is generated by Watcher when an
:ref:`Audit <audit_definition>` is successful which implies that the
:ref:`Strategy <strategy_definition>`
which was used has found a :ref:`Solution <solution_definition>` to achieve the
:ref:`Goal <goal_definition>` of this :ref:`Audit <audit_definition>`.
In the default implementation of Watcher, an
:ref:`Action Plan <action_plan_definition>`
is only composed of successive :ref:`Actions <action_definition>`
(i.e., a Workflow of :ref:`Actions <action_definition>` belonging to a unique
branch).
However, Watcher provides abstract interfaces for many of its components,
allowing other implementations to generate and handle more complex
:ref:`Action Plan(s) <action_plan_definition>`
composed of two types of Action Item(s):
- simple :ref:`Actions <action_definition>`: atomic tasks, which means it
can not be split into smaller tasks or commands from an OpenStack point of
view.
- composite Actions: which are composed of several simple
:ref:`Actions <action_definition>`
ordered in sequential and/or parallel flows.
An :ref:`Action Plan <action_plan_definition>` may be described using
standard workflow model description formats such as
`Business Process Model and Notation 2.0 (BPMN 2.0) <http://www.omg.org/spec/BPMN/2.0/>`_
or `Unified Modeling Language (UML) <http://www.uml.org/>`_.
An :ref:`Action Plan <action_plan_definition>` has a life-cycle and its current
state may be one of the following:
- **RECOMMENDED** : the :ref:`Action Plan <action_plan_definition>` is waiting
for a validation from the :ref:`Administrator <administrator_definition>`
- **ONGOING** : the :ref:`Action Plan <action_plan_definition>` is currently
being processed by the :ref:`Watcher Applier <watcher_applier_definition>`
- **SUCCEEDED** : the :ref:`Action Plan <action_plan_definition>` has been
executed successfully (i.e. all :ref:`Actions <action_definition>` that it
contains have been executed successfully)
- **FAILED** : an error occured while executing the
:ref:`Action Plan <action_plan_definition>`
- **DELETED** : the :ref:`Action Plan <action_plan_definition>` is still
stored in the :ref:`Watcher database <watcher_database_definition>` but is
not returned any more through the Watcher APIs.
- **CANCELLED** : the :ref:`Action Plan <action_plan_definition>` was in
**PENDING** or **ONGOING** state and was cancelled by the
:ref:`Administrator <administrator_definition>`
""" # noqa
import datetime
import pecan
@@ -23,21 +77,45 @@ import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from watcher._i18n import _
from watcher.api.controllers import base
from watcher.api.controllers import link
from watcher.api.controllers.v1 import collection
from watcher.api.controllers.v1 import types
from watcher.api.controllers.v1 import utils as api_utils
from watcher.applier.rpcapi import ApplierAPI
from watcher.applier import rpcapi
from watcher.common import exception
from watcher import objects
from watcher.objects import action_plan as ap_objects
class ActionPlanPatchType(types.JsonPatchType):
@staticmethod
def _validate_state(patch):
serialized_patch = {'path': patch.path, 'op': patch.op}
if patch.value is not wsme.Unset:
serialized_patch['value'] = patch.value
# todo: use state machines to handle state transitions
state_value = patch.value
if state_value and not hasattr(ap_objects.State, state_value):
msg = _("Invalid state: %(state)s")
raise exception.PatchError(
patch=serialized_patch, reason=msg % dict(state=state_value))
@staticmethod
def validate(patch):
if patch.path == "/state":
ActionPlanPatchType._validate_state(patch)
return types.JsonPatchType.validate(patch)
@staticmethod
def internal_attrs():
return types.JsonPatchType.internal_attrs()
@staticmethod
def mandatory_attrs():
return []
return ["audit_id", "state", "first_action_id"]
class ActionPlan(base.APIBase):
@@ -103,7 +181,6 @@ class ActionPlan(base.APIBase):
self.fields = []
fields = list(objects.ActionPlan.fields)
fields.append('audit_uuid')
for field in fields:
# Skip fields we do not expose.
if not hasattr(self, field):
@@ -111,14 +188,19 @@ class ActionPlan(base.APIBase):
self.fields.append(field)
setattr(self, field, kwargs.get(field, wtypes.Unset))
self.fields.append('audit_id')
self.fields.append('audit_uuid')
self.fields.append('first_action_uuid')
setattr(self, 'audit_uuid', kwargs.get('audit_id', wtypes.Unset))
setattr(self, 'first_action_uuid',
kwargs.get('first_action_id', wtypes.Unset))
@staticmethod
def _convert_with_links(action_plan, url, expand=True):
if not expand:
action_plan.unset_fields_except(['uuid', 'state', 'updated_at',
'audit_uuid'])
action_plan.unset_fields_except(
['uuid', 'state', 'updated_at',
'audit_uuid', 'first_action_uuid'])
action_plan.links = [link.Link.make_link(
'self', url,
@@ -201,7 +283,7 @@ class ActionPlansController(rest.RestController):
resource_url=None, audit_uuid=None):
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
@@ -230,9 +312,9 @@ class ActionPlansController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, types.uuid,
int, wtypes.text, wtypes.text, types.uuid)
def get_all(self, action_plan_uuid=None, marker=None, limit=None,
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, int, wtypes.text,
wtypes.text, types.uuid)
def get_all(self, marker=None, limit=None,
sort_key='id', sort_dir='asc', audit_uuid=None):
"""Retrieve a list of action plans.
@@ -241,25 +323,23 @@ class ActionPlansController(rest.RestController):
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
:param audit_uuid: Optional UUID of an audit, to get only actions
for that audit.
for that audit.
"""
return self._get_action_plans_collection(
marker, limit, sort_key, sort_dir, audit_uuid=audit_uuid)
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, types.uuid,
int, wtypes.text, wtypes.text, types.uuid)
def detail(self, action_plan_uuid=None, marker=None, limit=None,
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, int, wtypes.text,
wtypes.text, types.uuid)
def detail(self, marker=None, limit=None,
sort_key='id', sort_dir='asc', audit_uuid=None):
"""Retrieve a list of action_plans with detail.
:param action_plan_uuid: UUID of a action plan, to get only
:action_plans for that action.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
:param audit_uuid: Optional UUID of an audit, to get only actions
for that audit.
for that audit.
"""
# NOTE(lucasagomes): /detail should only work agaist collections
parent = pecan.request.path.split('/')[:-1][-1]
@@ -302,10 +382,10 @@ class ActionPlansController(rest.RestController):
@wsme_pecan.wsexpose(ActionPlan, types.uuid,
body=[ActionPlanPatchType])
def patch(self, action_plan_uuid, patch):
"""Update an existing audit template.
"""Update an existing action plan.
:param audit template_uuid: UUID of a audit template.
:param patch: a json PATCH document to apply to this audit template.
:param action_plan_uuid: UUID of a action plan.
:param patch: a json PATCH document to apply to this action plan.
"""
launch_action_plan = True
if self.from_actionsPlans:
@@ -322,6 +402,34 @@ class ActionPlansController(rest.RestController):
raise exception.PatchError(patch=patch, reason=e)
launch_action_plan = False
# transitions that are allowed via PATCH
allowed_patch_transitions = [
(ap_objects.State.RECOMMENDED,
ap_objects.State.PENDING),
(ap_objects.State.RECOMMENDED,
ap_objects.State.CANCELLED),
(ap_objects.State.ONGOING,
ap_objects.State.CANCELLED),
(ap_objects.State.PENDING,
ap_objects.State.CANCELLED),
]
# todo: improve this in blueprint watcher-api-validation
if hasattr(action_plan, 'state'):
transition = (action_plan_to_update.state, action_plan.state)
if transition not in allowed_patch_transitions:
error_message = _("State transition not allowed: "
"(%(initial_state)s -> %(new_state)s)")
raise exception.PatchError(
patch=patch,
reason=error_message % dict(
initial_state=action_plan_to_update.state,
new_state=action_plan.state))
if action_plan.state == ap_objects.State.PENDING:
launch_action_plan = True
# Update only the fields that have changed
for field in objects.ActionPlan.fields:
try:
@@ -334,13 +442,14 @@ class ActionPlansController(rest.RestController):
if action_plan_to_update[field] != patch_val:
action_plan_to_update[field] = patch_val
if field == 'state' and patch_val == 'STARTING':
if (field == 'state'
and patch_val == objects.action_plan.State.PENDING):
launch_action_plan = True
action_plan_to_update.save()
if launch_action_plan:
applier_client = ApplierAPI()
applier_client = rpcapi.ApplierAPI()
applier_client.launch_action_plan(pecan.request.context,
action_plan.uuid)

View File

@@ -15,6 +15,40 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
In the Watcher system, an :ref:`Audit <audit_definition>` is a request for
optimizing a :ref:`Cluster <cluster_definition>`.
The optimization is done in order to satisfy one :ref:`Goal <goal_definition>`
on a given :ref:`Cluster <cluster_definition>`.
For each :ref:`Audit <audit_definition>`, the Watcher system generates an
:ref:`Action Plan <action_plan_definition>`.
An :ref:`Audit <audit_definition>` has a life-cycle and its current state may
be one of the following:
- **PENDING** : a request for an :ref:`Audit <audit_definition>` has been
submitted (either manually by the
:ref:`Administrator <administrator_definition>` or automatically via some
event handling mechanism) and is in the queue for being processed by the
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>`
- **ONGOING** : the :ref:`Audit <audit_definition>` is currently being
processed by the
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>`
- **SUCCEEDED** : the :ref:`Audit <audit_definition>` has been executed
successfully (note that it may not necessarily produce a
:ref:`Solution <solution_definition>`).
- **FAILED** : an error occured while executing the
:ref:`Audit <audit_definition>`
- **DELETED** : the :ref:`Audit <audit_definition>` is still stored in the
:ref:`Watcher database <watcher_database_definition>` but is not returned
any more through the Watcher APIs.
- **CANCELLED** : the :ref:`Audit <audit_definition>` was in **PENDING** or
**ONGOING** state and was cancelled by the
:ref:`Administrator <administrator_definition>`
"""
import datetime
import pecan
@@ -23,6 +57,7 @@ import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from watcher._i18n import _
from watcher.api.controllers import base
from watcher.api.controllers import link
from watcher.api.controllers.v1 import collection
@@ -30,7 +65,7 @@ from watcher.api.controllers.v1 import types
from watcher.api.controllers.v1 import utils as api_utils
from watcher.common import exception
from watcher.common import utils
from watcher.decision_engine.rpcapi import DecisionEngineAPI
from watcher.decision_engine import rpcapi
from watcher import objects
@@ -181,6 +216,7 @@ class AuditCollection(collection.Collection):
"""A list containing audits objects"""
def __init__(self, **kwargs):
super(AuditCollection, self).__init__()
self._type = 'audits'
@staticmethod
@@ -227,7 +263,7 @@ class AuditsController(rest.RestController):
sort_key, sort_dir, expand=False,
resource_url=None, audit_template=None):
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
@@ -257,10 +293,9 @@ class AuditsController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@wsme_pecan.wsexpose(AuditCollection, types.uuid,
types.uuid, int, wtypes.text,
@wsme_pecan.wsexpose(AuditCollection, types.uuid, int, wtypes.text,
wtypes.text, wtypes.text)
def get_all(self, audit_uuid=None, marker=None, limit=None,
def get_all(self, marker=None, limit=None,
sort_key='id', sort_dir='asc', audit_template=None):
"""Retrieve a list of audits.
@@ -275,13 +310,12 @@ class AuditsController(rest.RestController):
sort_dir,
audit_template=audit_template)
@wsme_pecan.wsexpose(AuditCollection, types.uuid,
types.uuid, int, wtypes.text, wtypes.text)
def detail(self, audit_uuid=None, marker=None, limit=None,
@wsme_pecan.wsexpose(AuditCollection, types.uuid, int, wtypes.text,
wtypes.text)
def detail(self, marker=None, limit=None,
sort_key='id', sort_dir='asc'):
"""Retrieve a list of audits with detail.
:param audit_uuid: UUID of a audit, to get only audits for that audit.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
:param sort_key: column to sort results by. Default: id.
@@ -320,6 +354,11 @@ class AuditsController(rest.RestController):
if self.from_audits:
raise exception.OperationNotPermitted
if not audit._audit_template_uuid:
raise exception.Invalid(
message=_('The audit template UUID or name specified is '
'invalid'))
audit_dict = audit.as_dict()
context = pecan.request.context
new_audit = objects.Audit(context, **audit_dict)
@@ -330,7 +369,7 @@ class AuditsController(rest.RestController):
# trigger decision-engine to run the audit
dc_client = DecisionEngineAPI()
dc_client = rpcapi.DecisionEngineAPI()
dc_client.trigger_audit(context, new_audit.uuid)
return Audit.convert_with_links(new_audit)

View File

@@ -15,6 +15,39 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
An :ref:`Audit <audit_definition>` may be launched several times with the same
settings (:ref:`Goal <goal_definition>`, thresholds, ...). Therefore it makes
sense to save those settings in some sort of Audit preset object, which is
known as an :ref:`Audit Template <audit_template_definition>`.
An :ref:`Audit Template <audit_template_definition>` contains at least the
:ref:`Goal <goal_definition>` of the :ref:`Audit <audit_definition>`.
It may also contain some error handling settings indicating whether:
- :ref:`Watcher Applier <watcher_applier_definition>` stops the
entire operation
- :ref:`Watcher Applier <watcher_applier_definition>` performs a rollback
and how many retries should be attempted before failure occurs (also the latter
can be complex: for example the scenario in which there are many first-time
failures on ultimately successful :ref:`Actions <action_definition>`).
Moreover, an :ref:`Audit Template <audit_template_definition>` may contain some
settings related to the level of automation for the
:ref:`Action Plan <action_plan_definition>` that will be generated by the
:ref:`Audit <audit_definition>`.
A flag will indicate whether the :ref:`Action Plan <action_plan_definition>`
will be launched automatically or will need a manual confirmation from the
:ref:`Administrator <administrator_definition>`.
Last but not least, an :ref:`Audit Template <audit_template_definition>` may
contain a list of extra parameters related to the
:ref:`Strategy <strategy_definition>` configuration. These parameters can be
provided as a list of key-value pairs.
"""
import datetime
import pecan
@@ -116,7 +149,7 @@ class AuditTemplate(base.APIBase):
name='My Audit Template',
description='Description of my audit template',
host_aggregate=5,
goal='SERVERS_CONSOLIDATION',
goal='DUMMY',
extra={'automatic': True},
created_at=datetime.datetime.utcnow(),
deleted_at=None,
@@ -131,6 +164,7 @@ class AuditTemplateCollection(collection.Collection):
"""A list containing audit templates objects"""
def __init__(self, **kwargs):
super(AuditTemplateCollection, self).__init__()
self._type = 'audit_templates'
@staticmethod
@@ -163,12 +197,13 @@ class AuditTemplatesController(rest.RestController):
'detail': ['GET'],
}
def _get_audit_templates_collection(self, marker, limit,
def _get_audit_templates_collection(self, filters, marker, limit,
sort_key, sort_dir, expand=False,
resource_url=None):
api_utils.validate_search_filters(
filters, objects.audit_template.AuditTemplate.fields.keys())
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
@@ -178,6 +213,7 @@ class AuditTemplatesController(rest.RestController):
audit_templates = objects.AuditTemplate.list(
pecan.request.context,
filters,
limit,
marker_obj, sort_key=sort_key,
sort_dir=sort_dir)
@@ -189,26 +225,30 @@ class AuditTemplatesController(rest.RestController):
sort_key=sort_key,
sort_dir=sort_dir)
@wsme_pecan.wsexpose(AuditTemplateCollection, types.uuid, int,
wtypes.text, wtypes.text)
def get_all(self, marker=None, limit=None,
@wsme_pecan.wsexpose(AuditTemplateCollection, wtypes.text,
types.uuid, int, wtypes.text, wtypes.text)
def get_all(self, goal=None, marker=None, limit=None,
sort_key='id', sort_dir='asc'):
"""Retrieve a list of audit templates.
:param goal: goal name to filter by (case sensitive)
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
return self._get_audit_templates_collection(marker, limit, sort_key,
sort_dir)
filters = api_utils.as_filters_dict(goal=goal)
@wsme_pecan.wsexpose(AuditTemplateCollection, types.uuid, int,
return self._get_audit_templates_collection(
filters, marker, limit, sort_key, sort_dir)
@wsme_pecan.wsexpose(AuditTemplateCollection, wtypes.text, types.uuid, int,
wtypes.text, wtypes.text)
def detail(self, marker=None, limit=None,
def detail(self, goal=None, marker=None, limit=None,
sort_key='id', sort_dir='asc'):
"""Retrieve a list of audit templates with detail.
:param goal: goal name to filter by (case sensitive)
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
:param sort_key: column to sort results by. Default: id.
@@ -219,9 +259,11 @@ class AuditTemplatesController(rest.RestController):
if parent != "audit_templates":
raise exception.HTTPNotFound
filters = api_utils.as_filters_dict(goal=goal)
expand = True
resource_url = '/'.join(['audit_templates', 'detail'])
return self._get_audit_templates_collection(marker, limit,
return self._get_audit_templates_collection(filters, marker, limit,
sort_key, sort_dir, expand,
resource_url)
@@ -229,7 +271,7 @@ class AuditTemplatesController(rest.RestController):
def get_one(self, audit_template):
"""Retrieve information about the given audit template.
:param audit template_uuid: UUID or name of an audit template.
:param audit audit_template: UUID or name of an audit template.
"""
if self.from_audit_templates:
raise exception.OperationNotPermitted

View File

@@ -15,6 +15,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
A :ref:`Goal <goal_definition>` is a human readable, observable and measurable
end result having one objective to be achieved.
Here are some examples of :ref:`Goals <goal_definition>`:
- minimize the energy consumption
- minimize the number of compute nodes (consolidation)
- balance the workload among compute nodes
- minimize the license cost (some softwares have a licensing model which is
based on the number of sockets or cores where the software is deployed)
- find the most appropriate moment for a planned maintenance on a
given group of host (which may be an entire availability zone):
power supply replacement, cooling system replacement, hardware
modification, ...
"""
from oslo_config import cfg
import pecan
@@ -94,6 +111,7 @@ class GoalCollection(collection.Collection):
"""A list containing goals objects"""
def __init__(self, **kwargs):
super(GoalCollection, self).__init__()
self._type = 'goals'
@staticmethod
@@ -141,7 +159,7 @@ class GoalsController(rest.RestController):
resource_url=None, goal_name=None):
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
api_utils.validate_sort_dir(sort_dir)
goals = []

View File

@@ -21,8 +21,8 @@ import six
import wsme
from wsme import types as wtypes
from watcher._i18n import _
from watcher.common import exception
from watcher.common.i18n import _
from watcher.common import utils
@@ -181,8 +181,9 @@ class MultiType(wtypes.UserType):
return value
else:
raise ValueError(
_("Wrong type. Expected '%(type)s', got '%(value)s'")
% {'type': self.types, 'value': type(value)})
_("Wrong type. Expected '%(type)s', got '%(value)s'"),
type=self.types, value=type(value)
)
class JsonPatchType(wtypes.Base):
@@ -217,7 +218,7 @@ class JsonPatchType(wtypes.Base):
@staticmethod
def validate(patch):
_path = '/' + patch.path.split('/')[1]
_path = '/{0}'.format(patch.path.split('/')[1])
if _path in patch.internal_attrs():
msg = _("'%s' is an internal attribute and can not be updated")
raise wsme.exc.ClientSideError(msg % patch.path)

View File

@@ -17,7 +17,7 @@ import jsonpatch
from oslo_config import cfg
import wsme
from watcher.common.i18n import _
from watcher._i18n import _
CONF = cfg.CONF
@@ -28,10 +28,18 @@ JSONPATCH_EXCEPTIONS = (jsonpatch.JsonPatchException,
def validate_limit(limit):
if limit is not None and limit <= 0:
if limit is None:
return CONF.api.max_limit
if limit <= 0:
# Case where we don't a valid limit value
raise wsme.exc.ClientSideError(_("Limit must be positive"))
return min(CONF.api.max_limit, limit) or CONF.api.max_limit
if limit and not CONF.api.max_limit:
# Case where we don't have an upper limit
return limit
return min(CONF.api.max_limit, limit)
def validate_sort_dir(sort_dir):
@@ -39,7 +47,15 @@ def validate_sort_dir(sort_dir):
raise wsme.exc.ClientSideError(_("Invalid sort direction: %s. "
"Acceptable values are "
"'asc' or 'desc'") % sort_dir)
return sort_dir
def validate_search_filters(filters, allowed_fields):
# Very leightweight validation for now
# todo: improve this (e.g. https://www.parse.com/docs/rest/guide/#queries)
for filter_name in filters.keys():
if filter_name not in allowed_fields:
raise wsme.exc.ClientSideError(
_("Invalid filter: %s") % filter_name)
def apply_jsonpatch(doc, patch):
@@ -50,3 +66,12 @@ def apply_jsonpatch(doc, patch):
' the resource is not allowed')
raise wsme.exc.ClientSideError(msg % p['path'])
return jsonpatch.apply_patch(doc, jsonpatch.JsonPatch(patch))
def as_filters_dict(**filters):
filters_dict = {}
for filter_name, filter_value in filters.items():
if filter_value:
filters_dict[filter_name] = filter_value
return filters_dict

View File

@@ -19,8 +19,8 @@ from oslo_log import log
from keystonemiddleware import auth_token
from watcher._i18n import _
from watcher.common import exception
from watcher.common.i18n import _
from watcher.common import utils
LOG = log.getLogger(__name__)
@@ -40,10 +40,9 @@ class AuthTokenMiddleware(auth_token.AuthProtocol):
self.public_api_routes = [re.compile(route_pattern_tpl % route_tpl)
for route_tpl in public_api_routes]
except re.error as e:
msg = _('Cannot compile public API routes: %s') % e
LOG.error(msg)
raise exception.ConfigInvalid(error_msg=msg)
LOG.exception(e)
raise exception.ConfigInvalid(
error_msg=_('Cannot compile public API routes'))
super(AuthTokenMiddleware, self).__init__(app, conf)

View File

@@ -13,7 +13,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Middleware to replace the plain text message body of an error
response with one formatted so the client can parse it.
@@ -24,12 +23,12 @@ Based on pecan.middleware.errordocument
import json
from xml import etree as et
from oslo_log import log
import six
import webob
from oslo_log import log
from watcher.common.i18n import _
from watcher.common.i18n import _LE
from watcher._i18n import _
from watcher._i18n import _LE
LOG = log.getLogger(__name__)
@@ -69,23 +68,27 @@ class ParsableErrorMiddleware(object):
app_iter = self.app(environ, replacement_start_response)
if (state['status_code'] // 100) not in (2, 3):
req = webob.Request(environ)
if (req.accept.best_match(['application/json', 'application/xml'])
== 'application/xml'):
if (req.accept.best_match(['application/json', 'application/xml']
) == 'application/xml'):
try:
# simple check xml is valid
body = [et.ElementTree.tostring(
et.ElementTree.fromstring('<error_message>'
+ '\n'.join(app_iter)
+ '</error_message>'))]
et.ElementTree.Element('error_message',
text='\n'.join(app_iter)))]
except et.ElementTree.ParseError as err:
LOG.error(_LE('Error parsing HTTP response: %s'), err)
body = ['<error_message>%s' % state['status_code']
+ '</error_message>']
body = [et.ElementTree.tostring(
et.ElementTree.Element('error_message',
text=state['status_code']))]
state['headers'].append(('Content-Type', 'application/xml'))
else:
if six.PY3:
app_iter = [i.decode('utf-8') for i in app_iter]
body = [json.dumps({'error_message': '\n'.join(app_iter)})]
if six.PY3:
body = [item.encode('utf-8') for item in body]
state['headers'].append(('Content-Type', 'application/json'))
state['headers'].append(('Content-Length', len(body[0])))
state['headers'].append(('Content-Length', str(len(body[0]))))
else:
body = app_iter
return body

View File

@@ -1,11 +0,0 @@
# Watcher Actions Applier
This component is in charge of executing the plan of actions built by the Watcher Actions Planner.
For each action of the workflow, this component may call directly the component responsible for this kind of action (Example : Nova API for an instance migration) or via some publish/subscribe pattern on the message bus.
It notifies continuously of the current progress of the Action Plan (and atomic Actions), sending status messages on the bus. Those events may be used by the CEP to trigger new actions.
This component is also connected to the Watcher MySQL database in order to:
* get the description of the action plan to execute
* persist its current state so that if it is restarted, it can restore each Action plan context and restart from the last known safe point of each ongoing workflow.

View File

@@ -22,8 +22,7 @@ import six
@six.add_metaclass(abc.ABCMeta)
class ApplierCommand(object):
class BaseActionPlanHandler(object):
@abc.abstractmethod
def execute(self):
raise NotImplementedError(
"Should have implemented this") # pragma:no cover
raise NotImplementedError()

View File

@@ -0,0 +1,68 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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_log import log
from watcher.applier.action_plan import base
from watcher.applier import default
from watcher.applier.messaging import event_types
from watcher.common.messaging.events import event
from watcher.objects import action_plan as ap_objects
LOG = log.getLogger(__name__)
class DefaultActionPlanHandler(base.BaseActionPlanHandler):
def __init__(self, context, applier_manager, action_plan_uuid):
super(DefaultActionPlanHandler, self).__init__()
self.ctx = context
self.action_plan_uuid = action_plan_uuid
self.applier_manager = applier_manager
def notify(self, uuid, event_type, state):
action_plan = ap_objects.ActionPlan.get_by_uuid(self.ctx, uuid)
action_plan.state = state
action_plan.save()
ev = event.Event()
ev.type = event_type
ev.data = {}
payload = {'action_plan__uuid': uuid,
'action_plan_state': state}
self.applier_manager.status_topic_handler.publish_event(
ev.type.name, payload)
def execute(self):
try:
# update state
self.notify(self.action_plan_uuid,
event_types.EventTypes.LAUNCH_ACTION_PLAN,
ap_objects.State.ONGOING)
applier = default.DefaultApplier(self.ctx, self.applier_manager)
result = applier.execute(self.action_plan_uuid)
except Exception as e:
LOG.exception(e)
result = False
finally:
if result is True:
status = ap_objects.State.SUCCEEDED
else:
status = ap_objects.State.FAILED
# update state
self.notify(self.action_plan_uuid,
event_types.EventTypes.LAUNCH_ACTION_PLAN,
status)

View File

@@ -0,0 +1,116 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 abc
import six
from watcher.common import clients
@six.add_metaclass(abc.ABCMeta)
class BaseAction(object):
# NOTE(jed) by convention we decided
# that the attribute "resource_id" is the unique id of
# the resource to which the Action applies to allow us to use it in the
# watcher dashboard and will be nested in input_parameters
RESOURCE_ID = 'resource_id'
def __init__(self, osc=None):
""":param osc: an OpenStackClients instance"""
self._input_parameters = {}
self._osc = osc
@property
def osc(self):
if not self._osc:
self._osc = clients.OpenStackClients()
return self._osc
@property
def input_parameters(self):
return self._input_parameters
@input_parameters.setter
def input_parameters(self, p):
self._input_parameters = p
@property
def resource_id(self):
return self.input_parameters[self.RESOURCE_ID]
@abc.abstractmethod
def execute(self):
"""Executes the main logic of the action
This method can be used to perform an action on a given set of input
parameters to accomplish some type of operation. This operation may
return a boolean value as a result of its execution. If False, this
will be considered as an error and will then trigger the reverting of
the actions.
:returns: A flag indicating whether or not the action succeeded
:rtype: bool
"""
raise NotImplementedError()
@abc.abstractmethod
def revert(self):
"""Revert this action
This method should rollback the resource to its initial state in the
event of a faulty execution. This happens when the action raised an
exception during its :py:meth:`~.BaseAction.execute`.
"""
raise NotImplementedError()
@abc.abstractmethod
def precondition(self):
"""Hook: called before the execution of an action
This method can be used to perform some initializations or to make
some more advanced validation on its input parameters. So if you wish
to block its execution based on this factor, `raise` the related
exception.
"""
raise NotImplementedError()
@abc.abstractmethod
def postcondition(self):
"""Hook: called after the execution of an action
This function is called regardless of whether an action succeded or
not. So you can use it to perform cleanup operations.
"""
raise NotImplementedError()
@abc.abstractproperty
def schema(self):
"""Defines a Schema that the input parameters shall comply to
:returns: A schema declaring the input parameters this action should be
provided along with their respective constraints
:rtype: :py:class:`voluptuous.Schema` instance
"""
raise NotImplementedError()
def validate_parameters(self):
self.schema(self.input_parameters)
return True

View File

@@ -0,0 +1,102 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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
import voluptuous
from watcher._i18n import _
from watcher.applier.actions import base
from watcher.common import exception
from watcher.common import nova_helper
from watcher.decision_engine.model import hypervisor_state as hstate
class ChangeNovaServiceState(base.BaseAction):
"""Disables or enables the nova-compute service, deployed on a host
By using this action, you will be able to update the state of a
nova-compute service. A disabled nova-compute service can not be selected
by the nova scheduler for future deployment of server.
The action schema is::
schema = Schema({
'resource_id': str,
'state': str,
})
The `resource_id` references a nova-compute service name (list of available
nova-compute services is returned by this command: ``nova service-list
--binary nova-compute``).
The `state` value should either be `ONLINE` or `OFFLINE`.
"""
STATE = 'state'
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.RESOURCE_ID):
voluptuous.All(
voluptuous.Any(*six.string_types),
voluptuous.Length(min=1)),
voluptuous.Required(self.STATE):
voluptuous.Any(*[state.value
for state in list(hstate.HypervisorState)]),
})
@property
def host(self):
return self.resource_id
@property
def state(self):
return self.input_parameters.get(self.STATE)
def execute(self):
target_state = None
if self.state == hstate.HypervisorState.DISABLED.value:
target_state = False
elif self.state == hstate.HypervisorState.ENABLED.value:
target_state = True
return self._nova_manage_service(target_state)
def revert(self):
target_state = None
if self.state == hstate.HypervisorState.DISABLED.value:
target_state = True
elif self.state == hstate.HypervisorState.ENABLED.value:
target_state = False
return self._nova_manage_service(target_state)
def _nova_manage_service(self, state):
if state is None:
raise exception.IllegalArgumentException(
message=_("The target state is not defined"))
nova = nova_helper.NovaHelper(osc=self.osc)
if state is True:
return nova.enable_service_nova_compute(self.host)
else:
return nova.disable_service_nova_compute(self.host)
def precondition(self):
pass
def postcondition(self):
pass

View File

@@ -0,0 +1,42 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# 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 __future__ import unicode_literals
from oslo_log import log
from watcher.applier.actions.loading import default
LOG = log.getLogger(__name__)
class ActionFactory(object):
def __init__(self):
self.action_loader = default.DefaultActionLoader()
def make_action(self, object_action, osc=None):
LOG.debug("Creating instance of %s", object_action.action_type)
loaded_action = self.action_loader.load(name=object_action.action_type,
osc=osc)
loaded_action.input_parameters = object_action.input_parameters
LOG.debug("Checking the input parameters")
# NOTE(jed) if we change the schema of an action and we try to reload
# an older version of the Action, the validation can fail.
# We need to add the versioning of an Action or a migration tool.
# We can also create an new Action which extends the previous one.
loaded_action.validate_parameters()
return loaded_action

View File

@@ -5,7 +5,7 @@
# 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
# 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,
@@ -13,17 +13,17 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import logging
from __future__ import unicode_literals
from oslo_config import cfg
from oslo_log import log
from watcher.common.loader import default
LOG = log.getLogger(__name__)
def prepare_service(args=None, conf=cfg.CONF):
log.register_options(conf)
log.setup(conf, 'watcher')
conf(args, project='python-watcher')
conf.log_opt_values(LOG, logging.DEBUG)
class DefaultActionLoader(default.DefaultLoader):
def __init__(self):
super(DefaultActionLoader, self).__init__(namespace='watcher_actions')

View File

@@ -0,0 +1,159 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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_log import log
import six
import voluptuous
from watcher._i18n import _, _LC
from watcher.applier.actions import base
from watcher.common import exception
from watcher.common import nova_helper
from watcher.common import utils
LOG = log.getLogger(__name__)
class Migrate(base.BaseAction):
"""Live-Migrates a server to a destination nova-compute host
This action will allow you to migrate a server to another compute
destination host. As of now, only live migration can be performed using
this action.
.. If either host uses shared storage, you can use ``live``
.. as ``migration_type``. If both source and destination hosts provide
.. local disks, you can set the block_migration parameter to True (not
.. supported for yet).
The action schema is::
schema = Schema({
'resource_id': str, # should be a UUID
'migration_type': str, # choices -> "live" only
'dst_hypervisor': str,
'src_hypervisor': str,
})
The `resource_id` is the UUID of the server to migrate. Only live migration
is supported.
The `src_hypervisor` and `dst_hypervisor` parameters are respectively the
source and the destination compute hostname (list of available compute
hosts is returned by this command: ``nova service-list --binary
nova-compute``).
"""
# input parameters constants
MIGRATION_TYPE = 'migration_type'
LIVE_MIGRATION = 'live'
DST_HYPERVISOR = 'dst_hypervisor'
SRC_HYPERVISOR = 'src_hypervisor'
def check_resource_id(self, value):
if (value is not None and
len(value) > 0 and not
utils.is_uuid_like(value)):
raise voluptuous.Invalid(_("The parameter"
" resource_id is invalid."))
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.RESOURCE_ID): self.check_resource_id,
voluptuous.Required(self.MIGRATION_TYPE,
default=self.LIVE_MIGRATION):
voluptuous.Any(*[self.LIVE_MIGRATION]),
voluptuous.Required(self.DST_HYPERVISOR):
voluptuous.All(voluptuous.Any(*six.string_types),
voluptuous.Length(min=1)),
voluptuous.Required(self.SRC_HYPERVISOR):
voluptuous.All(voluptuous.Any(*six.string_types),
voluptuous.Length(min=1)),
})
@property
def instance_uuid(self):
return self.resource_id
@property
def migration_type(self):
return self.input_parameters.get(self.MIGRATION_TYPE)
@property
def dst_hypervisor(self):
return self.input_parameters.get(self.DST_HYPERVISOR)
@property
def src_hypervisor(self):
return self.input_parameters.get(self.SRC_HYPERVISOR)
def _live_migrate_instance(self, nova, destination):
result = None
try:
result = nova.live_migrate_instance(instance_id=self.instance_uuid,
dest_hostname=destination)
except nova_helper.nvexceptions.ClientException as e:
if e.code == 400:
LOG.debug("Live migration of instance %s failed. "
"Trying to live migrate using block migration."
% self.instance_uuid)
result = nova.live_migrate_instance(
instance_id=self.instance_uuid,
dest_hostname=destination,
block_migration=True)
else:
LOG.debug("Nova client exception occured while live migrating "
"instance %s.Exception: %s" %
(self.instance_uuid, e))
except Exception:
LOG.critical(_LC("Unexpected error occured. Migration failed for"
"instance %s. Leaving instance on previous "
"host."), self.instance_uuid)
return result
def migrate(self, destination):
nova = nova_helper.NovaHelper(osc=self.osc)
LOG.debug("Migrate instance %s to %s", self.instance_uuid,
destination)
instance = nova.find_instance(self.instance_uuid)
if instance:
if self.migration_type == 'live':
return self._live_migrate_instance(nova, destination)
else:
raise exception.Invalid(
message=(_('Migration of type %(migration_type)s is not '
'supported.') %
{'migration_type': self.migration_type}))
else:
raise exception.InstanceNotFound(name=self.instance_uuid)
def execute(self):
return self.migrate(destination=self.dst_hypervisor)
def revert(self):
return self.migrate(destination=self.src_hypervisor)
def precondition(self):
# todo(jed) check if the instance exist/ check if the instance is on
# the src_hypervisor
pass
def postcondition(self):
# todo(jed) we can image to check extra parameters (nework reponse,ect)
pass

View File

@@ -0,0 +1,67 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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_log import log
import six
import voluptuous
from watcher.applier.actions import base
LOG = log.getLogger(__name__)
class Nop(base.BaseAction):
"""logs a message
The action schema is::
schema = Schema({
'message': str,
})
The `message` is the actual message that will be logged.
"""
MESSAGE = 'message'
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.MESSAGE): voluptuous.Any(
voluptuous.Any(*six.string_types), None)
})
@property
def message(self):
return self.input_parameters.get(self.MESSAGE)
def execute(self):
LOG.debug("executing action NOP message:%s ", self.message)
return True
def revert(self):
LOG.debug("revert action NOP")
return True
def precondition(self):
pass
def postcondition(self):
pass

View File

@@ -0,0 +1,68 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 time
from oslo_log import log
import voluptuous
from watcher.applier.actions import base
LOG = log.getLogger(__name__)
class Sleep(base.BaseAction):
"""Makes the executor of the action plan wait for a given duration
The action schema is::
schema = Schema({
'duration': float,
})
The `duration` is expressed in seconds.
"""
DURATION = 'duration'
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.DURATION, default=1):
voluptuous.All(float, voluptuous.Range(min=0))
})
@property
def duration(self):
return int(self.input_parameters.get(self.DURATION))
def execute(self):
LOG.debug("Starting action Sleep duration:%s ", self.duration)
time.sleep(self.duration)
return True
def revert(self):
LOG.debug("revert action Sleep")
return True
def precondition(self):
pass
def postcondition(self):
pass

View File

@@ -17,13 +17,20 @@
# limitations under the License.
#
"""
This component is in charge of executing the
:ref:`Action Plan <action_plan_definition>` built by the
:ref:`Watcher Decision Engine <watcher_decision_engine_definition>`.
See: :doc:`../architecture` for more details on this component.
"""
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class Applier(object):
class BaseApplier(object):
@abc.abstractmethod
def execute(self, action_plan_uuid):
raise NotImplementedError(
"Should have implemented this") # pragma:no cover
raise NotImplementedError()

View File

@@ -16,24 +16,49 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from watcher.applier.base import Applier
from watcher.applier.execution.executor import CommandExecutor
from watcher.objects import Action
from watcher.objects import ActionPlan
from oslo_config import cfg
from oslo_log import log
from watcher.applier import base
from watcher.applier.workflow_engine.loading import default
from watcher import objects
LOG = log.getLogger(__name__)
CONF = cfg.CONF
class DefaultApplier(Applier):
def __init__(self, manager_applier, context):
class DefaultApplier(base.BaseApplier):
def __init__(self, context, applier_manager):
super(DefaultApplier, self).__init__()
self.manager_applier = manager_applier
self.context = context
self.executor = CommandExecutor(manager_applier, context)
self._applier_manager = applier_manager
self._loader = default.DefaultWorkFlowEngineLoader()
self._engine = None
self._context = context
@property
def context(self):
return self._context
@property
def applier_manager(self):
return self._applier_manager
@property
def engine(self):
if self._engine is None:
selected_workflow_engine = CONF.watcher_applier.workflow_engine
LOG.debug("Loading workflow engine %s ", selected_workflow_engine)
self._engine = self._loader.load(
name=selected_workflow_engine,
context=self.context,
applier_manager=self.applier_manager)
return self._engine
def execute(self, action_plan_uuid):
action_plan = ActionPlan.get_by_uuid(self.context, action_plan_uuid)
LOG.debug("Executing action plan %s ", action_plan_uuid)
action_plan = objects.ActionPlan.get_by_uuid(self.context,
action_plan_uuid)
# todo(jed) remove direct access to dbapi need filter in object
actions = Action.dbapi.get_action_list(self.context,
filters={
'action_plan_id':
action_plan.id})
return self.executor.execute(actions)
filters = {'action_plan_id': action_plan.id}
actions = objects.Action.dbapi.get_action_list(self.context, filters)
return self.engine.execute(actions)

View File

@@ -1,50 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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_log import log
LOG = log.getLogger(__name__)
class DeployPhase(object):
def __init__(self, executor):
# todo(jed) oslo_conf 10 secondes
self.maxTimeout = 100000
self.commands = []
self.executor = executor
def set_max_time(self, mt):
self.maxTimeout = mt
def get_max_time(self):
return self.maxTimeout
def populate(self, action):
self.commands.append(action)
def execute_primitive(self, primitive):
futur = primitive.execute(primitive)
return futur.result(self.get_max_time())
def rollback(self):
reverted = sorted(self.commands, reverse=True)
for primitive in reverted:
try:
self.execute_primitive(primitive)
except Exception as e:
LOG.error(e)

View File

@@ -1,78 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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_log import log
from watcher.applier.mapper.default import DefaultCommandMapper
from watcher.applier.execution.deploy_phase import DeployPhase
from watcher.applier.messaging.events import Events
from watcher.common.messaging.events.event import Event
from watcher.objects import Action
from watcher.objects.action_plan import Status
LOG = log.getLogger(__name__)
class CommandExecutor(object):
def __init__(self, manager_applier, context):
self.manager_applier = manager_applier
self.context = context
self.deploy = DeployPhase(self)
self.mapper = DefaultCommandMapper()
def get_primitive(self, action):
return self.mapper.build_primitive_command(action)
def notify(self, action, state):
db_action = Action.get_by_uuid(self.context, action.uuid)
db_action.state = state
db_action.save()
event = Event()
event.set_type(Events.LAUNCH_ACTION)
event.set_data({})
payload = {'action_uuid': action.uuid,
'action_status': state}
self.manager_applier.topic_status.publish_event(event.get_type().name,
payload)
def execute(self, actions):
for action in actions:
try:
self.notify(action, Status.ONGOING)
primitive = self.get_primitive(action)
result = self.deploy.execute_primitive(primitive)
if result is False:
self.notify(action, Status.FAILED)
self.deploy.rollback()
return False
else:
self.deploy.populate(primitive)
self.notify(action, Status.SUCCEEDED)
except Exception as e:
LOG.debug(
'The applier module failed to execute the action{0} with '
'the exception {1} '.format(
action,
unicode(e)))
LOG.error("Trigger a rollback")
self.notify(action, Status.FAILED)
self.deploy.rollback()
return False
return True

View File

@@ -16,29 +16,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from concurrent.futures import ThreadPoolExecutor
from oslo_config import cfg
from oslo_log import log
from watcher.applier.messaging.trigger import TriggerActionPlan
from watcher.common.messaging.messaging_core import MessagingCore
from watcher.common.messaging.notification_handler import NotificationHandler
from watcher.decision_engine.messaging.events import Events
from watcher.applier.messaging import trigger
from watcher.common.messaging import messaging_core
LOG = log.getLogger(__name__)
CONF = cfg.CONF
# Register options
APPLIER_MANAGER_OPTS = [
cfg.IntOpt('applier_worker', default='1', help='The number of worker'),
cfg.StrOpt('topic_control',
cfg.IntOpt('workers',
default='1',
min=1,
required=True,
help='Number of workers for applier, default value is 1.'),
cfg.StrOpt('conductor_topic',
default='watcher.applier.control',
help='The topic name used for'
'control events, this topic '
'used for rpc call '),
cfg.StrOpt('topic_status',
cfg.StrOpt('status_topic',
default='watcher.applier.status',
help='The topic name used for '
'status events, this topic '
@@ -48,7 +49,11 @@ APPLIER_MANAGER_OPTS = [
cfg.StrOpt('publisher_id',
default='watcher.applier.api',
help='The identifier used by watcher '
'module on the message broker')
'module on the message broker'),
cfg.StrOpt('workflow_engine',
default='taskflow',
required=True,
help='Select the engine to use to execute the workflow')
]
opt_group = cfg.OptGroup(name='watcher_applier',
@@ -57,46 +62,18 @@ opt_group = cfg.OptGroup(name='watcher_applier',
CONF.register_group(opt_group)
CONF.register_opts(APPLIER_MANAGER_OPTS, opt_group)
CONF.import_opt('admin_user', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('admin_tenant_name', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('admin_password', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('auth_uri', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
class ApplierManager(MessagingCore):
# todo(jed) need workflow
class ApplierManager(messaging_core.MessagingCore):
def __init__(self):
super(ApplierManager, self).__init__(
CONF.watcher_applier.publisher_id,
CONF.watcher_applier.topic_control,
CONF.watcher_applier.topic_status,
CONF.watcher_applier.conductor_topic,
CONF.watcher_applier.status_topic,
api_version=self.API_VERSION,
)
# shared executor of the workflow
self.executor = ThreadPoolExecutor(max_workers=1)
self.handler = NotificationHandler(self.publisher_id)
self.handler.register_observer(self)
self.add_event_listener(Events.ALL, self.event_receive)
# trigger action_plan
self.topic_control.add_endpoint(TriggerActionPlan(self))
self.conductor_topic_handler.add_endpoint(
trigger.TriggerActionPlan(self))
def join(self):
self.topic_control.join()
self.topic_status.join()
def event_receive(self, event):
try:
request_id = event.get_request_id()
event_type = event.get_type()
data = event.get_data()
LOG.debug("request id => %s" % request_id)
LOG.debug("type_event => %s" % str(event_type))
LOG.debug("data => %s" % str(data))
except Exception as e:
LOG.error("evt %s" % e.message)
raise e
self.conductor_topic_handler.join()
self.status_topic_handler.join()

View File

@@ -1,47 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 watcher.applier.mapper.base import CommandMapper
from watcher.applier.primitive.hypervisor_state import HypervisorStateCommand
from watcher.applier.primitive.migration import MigrateCommand
from watcher.applier.primitive.nop import NopCommand
from watcher.applier.primitive.power_state import PowerStateCommand
from watcher.common.exception import ActionNotFound
from watcher.decision_engine.planner.default import Primitives
class DefaultCommandMapper(CommandMapper):
def build_primitive_command(self, action):
if action.action_type == Primitives.COLD_MIGRATE.value:
return MigrateCommand(action.applies_to, Primitives.COLD_MIGRATE,
action.src,
action.dst)
elif action.action_type == Primitives.LIVE_MIGRATE.value:
return MigrateCommand(action.applies_to, Primitives.COLD_MIGRATE,
action.src,
action.dst)
elif action.action_type == Primitives.HYPERVISOR_STATE.value:
return HypervisorStateCommand(action.applies_to, action.parameter)
elif action.action_type == Primitives.POWER_STATE.value:
return PowerStateCommand()
elif action.action_type == Primitives.NOP.value:
return NopCommand()
else:
raise ActionNotFound()

View File

@@ -17,9 +17,9 @@
# limitations under the License.
#
from enum import Enum
import enum
class Events(Enum):
class EventTypes(enum.Enum):
LAUNCH_ACTION_PLAN = "launch_action_plan"
LAUNCH_ACTION = "launch_action"

View File

@@ -1,68 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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_log import log
from watcher.applier.default import DefaultApplier
from watcher.applier.messaging.applier_command import ApplierCommand
from watcher.applier.messaging.events import Events
from watcher.common.messaging.events.event import Event
from watcher.objects.action_plan import ActionPlan
from watcher.objects.action_plan import Status
LOG = log.getLogger(__name__)
class LaunchActionPlanCommand(ApplierCommand):
def __init__(self, context, manager_applier, action_plan_uuid):
super(LaunchActionPlanCommand, self).__init__()
self.ctx = context
self.action_plan_uuid = action_plan_uuid
self.manager_applier = manager_applier
def notify(self, uuid, event_type, status):
action_plan = ActionPlan.get_by_uuid(self.ctx, uuid)
action_plan.state = status
action_plan.save()
event = Event()
event.set_type(event_type)
event.set_data({})
payload = {'action_plan__uuid': uuid,
'action_plan_status': status}
self.manager_applier.topic_status.publish_event(event.get_type().name,
payload)
def execute(self):
try:
# update state
self.notify(self.action_plan_uuid,
Events.LAUNCH_ACTION_PLAN,
Status.ONGOING)
applier = DefaultApplier(self.manager_applier, self.ctx)
result = applier.execute(self.action_plan_uuid)
except Exception as e:
result = False
LOG.error("Launch Action Plan " + unicode(e))
finally:
if result is True:
status = Status.SUCCEEDED
else:
status = Status.FAILED
# update state
self.notify(self.action_plan_uuid, Events.LAUNCH_ACTION_PLAN,
status)

View File

@@ -16,30 +16,35 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from concurrent import futures
from oslo_config import cfg
from oslo_log import log
from watcher.applier.messaging.launcher import LaunchActionPlanCommand
from watcher.applier.action_plan import default
LOG = log.getLogger(__name__)
CONF = cfg.CONF
class TriggerActionPlan(object):
def __init__(self, manager_applier):
self.manager_applier = manager_applier
def __init__(self, applier_manager):
self.applier_manager = applier_manager
workers = CONF.watcher_applier.workers
self.executor = futures.ThreadPoolExecutor(max_workers=workers)
def do_launch_action_plan(self, context, action_plan_uuid):
try:
cmd = LaunchActionPlanCommand(context,
self.manager_applier,
action_plan_uuid)
cmd = default.DefaultActionPlanHandler(context,
self.applier_manager,
action_plan_uuid)
cmd.execute()
except Exception as e:
LOG.error("do_launch_action_plan " + unicode(e))
LOG.exception(e)
def launch_action_plan(self, context, action_plan_uuid):
LOG.debug("Trigger ActionPlan %s" % action_plan_uuid)
LOG.debug("Trigger ActionPlan %s", action_plan_uuid)
# submit
self.manager_applier.executor.submit(self.do_launch_action_plan,
context,
action_plan_uuid)
self.executor.submit(self.do_launch_action_plan, context,
action_plan_uuid)
return action_plan_uuid

View File

@@ -1,36 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 abc
import six
from watcher.applier.promise import Promise
@six.add_metaclass(abc.ABCMeta)
class PrimitiveCommand(object):
@Promise
@abc.abstractmethod
def execute(self):
raise NotImplementedError(
"Should have implemented this") # pragma:no cover
@Promise
@abc.abstractmethod
def undo(self):
raise NotImplementedError(
"Should have implemented this") # pragma:no cover

View File

@@ -1,61 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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
from watcher.applier.primitive.base import PrimitiveCommand
from watcher.applier.primitive.wrapper.nova_wrapper import NovaWrapper
from watcher.applier.promise import Promise
from watcher.common.keystone import KeystoneClient
from watcher.decision_engine.model.hypervisor_state import HypervisorState
CONF = cfg.CONF
class HypervisorStateCommand(PrimitiveCommand):
def __init__(self, host, status):
self.host = host
self.status = status
def nova_manage_service(self, status):
keystone = KeystoneClient()
wrapper = NovaWrapper(keystone.get_credentials(),
session=keystone.get_session())
if status is True:
return wrapper.enable_service_nova_compute(self.host)
else:
return wrapper.disable_service_nova_compute(self.host)
@Promise
def execute(self):
if self.status == HypervisorState.OFFLINE.value:
state = False
elif self.status == HypervisorState.ONLINE.value:
state = True
return self.nova_manage_service(state)
@Promise
def undo(self):
if self.status == HypervisorState.OFFLINE.value:
state = True
elif self.status == HypervisorState.ONLINE.value:
state = False
return self.nova_manage_service(state)

View File

@@ -1,87 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 keystoneclient.auth.identity import v3
from keystoneclient import session
from oslo_config import cfg
from watcher.applier.primitive.base import PrimitiveCommand
from watcher.applier.primitive.wrapper.nova_wrapper import NovaWrapper
from watcher.applier.promise import Promise
from watcher.common.keystone import KeystoneClient
from watcher.decision_engine.planner.default import Primitives
CONF = cfg.CONF
class MigrateCommand(PrimitiveCommand):
def __init__(self, vm_uuid=None,
migration_type=None,
source_hypervisor=None,
destination_hypervisor=None):
self.instance_uuid = vm_uuid
self.migration_type = migration_type
self.source_hypervisor = source_hypervisor
self.destination_hypervisor = destination_hypervisor
def migrate(self, destination):
keystone = KeystoneClient()
wrapper = NovaWrapper(keystone.get_credentials(),
session=keystone.get_session())
instance = wrapper.find_instance(self.instance_uuid)
if instance:
project_id = getattr(instance, "tenant_id")
creds2 = \
{'auth_url': CONF.keystone_authtoken.auth_uri,
'username': CONF.keystone_authtoken.admin_user,
'password': CONF.keystone_authtoken.admin_password,
'project_id': project_id,
'user_domain_name': "default",
'project_domain_name': "default"}
auth2 = v3.Password(auth_url=creds2['auth_url'],
username=creds2['username'],
password=creds2['password'],
project_id=creds2['project_id'],
user_domain_name=creds2[
'user_domain_name'],
project_domain_name=creds2[
'project_domain_name'])
sess2 = session.Session(auth=auth2)
wrapper2 = NovaWrapper(creds2, session=sess2)
# todo(jed) remove Primitves
if self.migration_type is Primitives.COLD_MIGRATE:
return wrapper2.live_migrate_instance(
instance_id=self.instance_uuid,
dest_hostname=destination,
block_migration=True)
elif self.migration_type is Primitives.LIVE_MIGRATE:
return wrapper2.live_migrate_instance(
instance_id=self.instance_uuid,
dest_hostname=destination,
block_migration=False)
@Promise
def execute(self):
return self.migrate(self.destination_hypervisor)
@Promise
def undo(self):
return self.migrate(self.source_hypervisor)

View File

@@ -1,50 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 concurrent.futures import Future
from concurrent.futures import ThreadPoolExecutor
class Promise(object):
executor = ThreadPoolExecutor(
max_workers=10)
def __init__(self, func):
self.func = func
def resolve(self, *args, **kwargs):
resolved_args = []
resolved_kwargs = {}
for i, arg in enumerate(args):
if isinstance(arg, Future):
resolved_args.append(arg.result())
else:
resolved_args.append(arg)
for kw, arg in kwargs.items():
if isinstance(arg, Future):
resolved_kwargs[kw] = arg.result()
else:
resolved_kwargs[kw] = arg
return self.func(*resolved_args, **resolved_kwargs)
def __call__(self, *args, **kwargs):
return self.executor.submit(self.resolve, *args, **kwargs)

Some files were not shown because too many files have changed in this diff Show More