Compare commits

..

231 Commits

Author SHA1 Message Date
Prudhvi Rao Shedimbi
695ddf8ae7 Implemented clients and auth config module
Implemented clients and auth config module

Implements: blueprint centralise-config-opts

Change-Id: I28ea8376aa34114331cbae61596839ebae6cf7eb
2016-12-14 20:13:23 +00:00
Jenkins
8fd5057cd0 Merge "Implemented wacther decision engine config module" 2016-12-14 18:48:14 +00:00
Jenkins
3db81564f4 Merge "Documentation for Uniform Airflow Migration Strategy Fixed issues" 2016-12-14 17:51:11 +00:00
Jenkins
10066ed8fd Merge "Documentation for Workload Balance Migration Strategy Fixed comments and added the doc primitive call" 2016-12-14 17:51:04 +00:00
Jenkins
e4c5f4f050 Merge "update strategy table when parameters_spec changes" 2016-12-14 17:48:27 +00:00
Prudhvi Rao Shedimbi
53c896dd24 Implemented wacther decision engine config module
Implemented wacther decision engine config module

Partially Implements: blueprint centralise-config-opts

Change-Id: Ie4e9dd7d902fa85044d1859974cbd75d54c8b6cc
2016-12-14 17:40:57 +00:00
Susanne Balle
40a46c6663 Documentation for Uniform Airflow Migration Strategy
Fixed issues

Closes-Bug: #1623486
Change-Id: If303283949ef39a26c91bbff7b4664e81687d169
2016-12-14 11:01:27 -05:00
Prudhvi Rao Shedimbi
ed21e452e0 Implemented applier config module
Implemented applier config module

Partially Implements: blueprint centralise-config-opts

Change-Id: I237596b06dc3bee318414346cfa58ae4cb81079b
2016-12-14 15:50:11 +00:00
Prudhvi Rao Shedimbi
80e77a5b81 Implemented planner config module
Implemented planner config module

Partially Implements: blueprint centralise-config-opts

Change-Id: I4d710c8552ef211c6a9c38dd8f5515f68a6d36c4
2016-12-14 14:52:37 +00:00
Prudhvi Rao Shedimbi
74112dd7cf Implemented db config module
Implemented db config module

Partially Implements: blueprint centralise-config-opts. Also moved
basedir_def, bindir_def and state_path_def to watcher.conf.paths

Change-Id: I73d201f6a23bbdb1c6189434b11314a66620e85c
2016-12-14 15:13:31 +01:00
Prudhvi Rao Shedimbi
9e4bf718da Implemented exception config module
Implemented exception config module

Partially Implements: blueprint centralise-config-opts

Change-Id: Ic1b94e28a960a7306f15afbf69382edc15b5999e
2016-12-14 11:05:01 +00:00
Prudhvi Rao Shedimbi
5c79074e9c Implemented paths config module
Implemented paths config module

Partially Implements: blueprint centralise-config-opts

Change-Id: I2b779fb1ce552567feac678cb5bd78aad0d53d52
2016-12-14 10:56:58 +00:00
Jenkins
ac6848dad3 Merge "Implemented utils config module" 2016-12-14 10:31:07 +00:00
Jenkins
648715eb5c Merge "Implemented api config module" 2016-12-14 10:20:48 +00:00
Jenkins
3b5ef5d625 Merge "Specific exception for stale cluster state was added." 2016-12-13 15:35:41 +00:00
Jenkins
7fd486bd65 Merge "Unnecessary exception" 2016-12-13 12:00:23 +00:00
Jenkins
3cf4b315d3 Merge "improve statistic_aggregation" 2016-12-13 11:02:21 +00:00
Susanne Balle
25d84ba662 Documentation for Workload Balance Migration Strategy
Fixed comments and added the doc primitive call

Closes-Bug: #1623486

Change-Id: I704536530c576de702434008aa30a7fbbaddff25
2016-12-12 14:17:08 -05:00
Anton Khaldin
7908af3150 Specific exception for stale cluster state was added.
Specific exception should be thrown when cluster state
is stale. Current usage is to raise this exception if
compute_model.state is True.
Bug was describeid by Jean-Emile DARTOIS.

Change-Id: Iaddb4cc8007c51bb14759c9da829751e834499d0
Closes-Bug: #1621855
2016-12-12 18:04:51 +00:00
Prudhvi Rao Shedimbi
04fdea2aa0 Implemented utils config module
Implemented utils config module

Partially Implements: blueprint centralise-config-opts

Change-Id: Ic09ecba60022b69ec4031608716e34209d3fe578
2016-12-12 16:36:04 +00:00
Jenkins
cee9cfb62c Merge "Updated from global requirements" 2016-12-12 13:31:35 +00:00
Jenkins
e1912fe03e Merge "Modify the variable assignment errors" 2016-12-12 11:57:57 +00:00
licanwei
d859f3ac1f Fix CI failures
reference to:
http://osdir.com/ml/openstack-dev/2016-12/msg00420.html
/nova/tox.ini

Change-Id: Ie566f4b7c1c3cd3c1654281c0cad028c3886d9f7
2016-12-12 18:01:25 +08:00
licanwei
7a72371df8 improve statistic_aggregation
improve statistic_aggregation

Change-Id: Ic1fb19780fa4a39c5eb74e5ed30db0e4c06d0e09
2016-12-12 17:10:38 +08:00
licanwei
82bb097e9f Unnecessary exception
If the instance state is not ACTIVE,
There is no need to throw an exception.

Change-Id: I88ba3ae9c92b75ed57fc9647e33c4a10801b2c18
Closes-Bug: #1648309
2016-12-08 15:06:26 +08:00
licanwei
a9ef9f3a94 update strategy table when parameters_spec changes
At present, In the sync function, there is no check about the
parameters_spec field in strategy table, if the parameters_spec
field content has changed, Such as increased 'periods' parameter,
strategy table will not be updated, the program will run abnormal.

exception msg:
2016-12-05 11:11:39.138 TRACE watcher.decision_engine.audit.base
raise AttributeError(name)
2016-12-05 11:11:39.138 TRACE watcher.decision_engine.audit.base
AttributeError: periods

Change-Id: I84709c246acbdf44ccac257b07a74084962bb628
Closes-Bug: #1647521
2016-12-08 13:34:03 +08:00
Prudhvi Rao Shedimbi
8e7ba3c44a Implemented api config module
Implemented api config module

Partially Implements: blueprint centralise-config-opts

Change-Id: I055618e546bb1bfa2c1764bcff1a1f94e5adea96
2016-12-07 21:46:28 +00:00
OpenStack Proposal Bot
9a2ca8c4b7 Updated from global requirements
Change-Id: Ib5668cd281665477968b5a9acadf65765b5a06a1
2016-12-07 13:41:44 +00:00
Jenkins
c08666b2fa Merge "[Doc] Fix example code of goal plugin" 2016-12-07 12:26:50 +00:00
OpenStack Proposal Bot
6638f921a3 Updated from global requirements
Change-Id: Idceb803efc01b3b2346cc15391517e4b527d43ac
2016-12-07 09:09:52 +00:00
licanwei
1f2a854d6a Repairing unit test failures
If fieldname is 'deleted', field.type.python_type raise
NotImplementedError.

Change-Id: I47246ce9a3b0c8d2a3ea44e825d9604f5b14ed38
Closes-Bug: #1647574
2016-12-06 17:15:19 +08:00
Hidekazu Nakamura
d0e46d81fc [Doc] Fix example code of goal plugin
An example code of goal plugin does not work.
This patch fixes it.

Change-Id: I75c2ffa74a003fad9e2d512927e4cb47554783c2
2016-12-02 12:47:34 +09:00
Jenkins
4e240b945b Merge "Fix one ref that does not work." 2016-11-30 11:43:28 +00:00
Jenkins
e09188d862 Merge "Updated from global requirements" 2016-11-30 11:42:39 +00:00
Jenkins
a4b1df2fce Merge "Fix rally gate test" 2016-11-30 11:32:46 +00:00
Jenkins
22933e4d79 Merge "Show team and repo badges on README" 2016-11-30 10:39:46 +00:00
Jenkins
40a5c98382 Merge "Use uuidutils instead of uuid.uuid4()." 2016-11-30 08:43:49 +00:00
Jenkins
5b21b9a17e Merge "Fix 'ImportError' when docbuild." 2016-11-30 08:42:47 +00:00
Jenkins
02d1850be7 Merge "Add periods input parameter" 2016-11-30 08:34:19 +00:00
Jenkins
5f1f10e3d3 Merge "Documentation for Outlet Temperature Based Strategy Fixed outstanding comments" 2016-11-30 08:20:43 +00:00
zhuzeyu
e4732e1375 Use uuidutils instead of uuid.uuid4().
Change-Id: I3f734b6c4d252f8eb73a49b447fd89e5e444002f
Closes-Bug: #1082248
2016-11-29 14:15:54 +08:00
licanwei
715f6fa1cd Modify the variable assignment errors
The values of 'released_compute_nodes_count' and
'instance_migrations_count' are upside down

Change-Id: I0662bdfce575de529eb8c12363be7fa196b1a88c
2016-11-29 10:20:23 +08:00
Flavio Percoco
6daa09f489 Show team and repo badges on README
This patch adds the team's and repository's badges to the README file.
The motivation behind this is to communicate the project status and
features at first glance.

For more information about this effort, please read this email thread:

http://lists.openstack.org/pipermail/openstack-dev/2016-October/105562.html

To see an example of how this would look like check:

https://gist.github.com/fb14a4269de717e9410ba91722027512

Change-Id: If7f9b36d45c431ecb6d0eb76d907d63573de4238
2016-11-25 17:21:54 +01:00
ericxiett
7feced419c Fix 'ImportError' when docbuild.
There is not module 'messaging.utils' in 'watcher.common'
but module 'synchronization' instead.

Change-Id: If2d585a4f416614cbe91e4ef61fc7473508d38af
Closes-Bug: #1643862
2016-11-24 01:34:51 +08:00
ericxiett
3319748367 Fix one ref that does not work.
One space was missed between 'the' and ':ref' in
'the:ref:Watcher Database <watcher_database_definition>'.

Change-Id: I3e46121dc7c30f73df4ca455e2c629929cdbd2ec
Closes-Bug: #1644388
2016-11-24 01:15:56 +08:00
OpenStack Proposal Bot
a84f52dfe3 Updated from global requirements
Change-Id: Id8935a3139541edb1dae894358f20c3cfc0ddd21
2016-11-23 11:05:56 +00:00
Jenkins
3f8e4451f5 Merge "Fix some typos in action.py & action_plan.py & audit.py" 2016-11-23 09:41:59 +00:00
Jenkins
d082c9ac41 Merge "Fix the wrong ref for 'Compute node'" 2016-11-23 09:41:09 +00:00
Jenkins
9080180309 Merge "Fix inconsistent descriptions in docstring in action_plan.py" 2016-11-23 08:59:25 +00:00
Jenkins
55893043df Merge "Solve some spelling mistakes." 2016-11-23 08:58:58 +00:00
Jenkins
19074f615a Merge "Remove redundan lines." 2016-11-23 08:58:49 +00:00
Alexander Chadin
295c8d914c Add periods input parameter
This patch set adds new periods strategy input
parameter that allows to specify the time length of
statistic aggregation.

Change-Id: Id6c7900e7b909b0b325281c4038e07dc695847a1
2016-11-23 11:55:14 +03:00
zte-hanrong
99735fa39a Solve some spelling mistakes.
Change-Id: Id7e8c4efbfc4203e63583b68c87be75f4a195b66
2016-11-23 11:40:51 +08:00
zte-hanrong
b80229f3d0 Remove redundan lines.
Change-Id: Iac10aea306e59eb91b192ec6e89f42851d9548a5
2016-11-23 11:31:26 +08:00
Susanne Balle
5e9ba463ee Documentation for Outlet Temperature Based Strategy
Fixed outstanding comments

Closes-Bug: #1623486

Change-Id: I2d327f472749c0e5a8b184eb426abebd757cc4f7
2016-11-21 11:15:01 -05:00
Jenkins
120c116655 Merge "[Doc] Fix default value in workload_stabilization" 2016-11-21 13:45:41 +00:00
Jenkins
c9cfd3bfbd Merge "Replaces uuid.uuid4 with uuidutils.generate_uuid()" 2016-11-21 13:36:21 +00:00
Jenkins
31f2b4172e Merge "Change hardware.cpu_util in workload_stabilization" 2016-11-21 13:32:23 +00:00
Hidekazu Nakamura
5151b666fd Change hardware.cpu_util in workload_stabilization
In this change set, hardware.cpu_util is changed to
compute.node.cpu.percent in workload_stabilization.
By doing so, one can run this strategy on a simple devstack
without having to setup the SNMP plugin.

Change-Id: I8df8921337ea3f4e751c0c822d823e64e3ca7e1c
2016-11-21 09:58:38 +09:00
YumengBao
578138e432 Fix inconsistent descriptions in docstring in action_plan.py
Change-Id: I4e74ac0ce7bdd17f1809ac1fbb8bf8110bfa290e
2016-11-19 17:58:46 +08:00
Jenkins
7e2fd7ed9a Merge "Removed nullable flag from audit_id in ActionPlan" 2016-11-18 14:40:53 +00:00
Vincent Françoise
74cb93fca8 Removed nullable flag from audit_id in ActionPlan
Partially Implements: blueprint watcher-versioned-objects

Change-Id: I0bf572e0756ef5d9bb73711a28225526dd044995
2016-11-18 14:16:32 +01:00
qinchunhua
876f3adb22 Replaces uuid.uuid4 with uuidutils.generate_uuid()
Change-Id: I38740842402841ae446603faacbbe969854f2396
Closes-Bug: #1082248
2016-11-18 00:55:39 +00:00
Vincent Françoise
06682fe7c3 Fixed update of WatcherObject fields on update
I this changeset, I fixed the issue whereby object auto fields are not
being updated within the WatcherObject after an update.

Change-Id: I7e65341b386a5c0c58c2109348e39e463cf2f668
Closes-Bug: #1641955
2016-11-17 17:41:13 +01:00
zhangyanxian
eaaa2b1b69 Fix some typos in action.py & action_plan.py & audit.py
Change-Id: I64909a6319a709dd8cb6a0e6b28bca714f5b4f6e
TrivialFix: "occured" should be "occurred"
2016-11-17 11:20:58 +00:00
Hidekazu Nakamura
88187a8ba9 [Doc] Fix default value in workload_stabilization
In this change set, Default value 'hardware.cpu_util' of instance_metrics
was changed to 'compute.node.cpu.percent'.

Change-Id: I02f87e5fea663e2e04c61cc36b7d55ff250bf8cc
2016-11-17 10:39:10 +09:00
ericxiett
0b6979b71c Fix the wrong ref for 'Compute node'
The refo of 'Compute node' in glossary.rst is wrong.
And modify it.

Change-Id: I3be61c95df2d538c5e49f169c428a605816d66e0
Closes-Bug: #1641405
2016-11-17 09:13:42 +08:00
Jenkins
3ebe8ab70f Merge "Added Tempest API tests for /scoring_engines" 2016-11-16 21:16:25 +00:00
Jenkins
240f849758 Merge "Implemented base + moved plugins & service conf" 2016-11-16 17:24:45 +00:00
Jenkins
46f23fac57 Merge "Add audit.planner events" 2016-11-16 16:52:13 +00:00
Jenkins
83ea0bff45 Merge "Add audit.strategy events" 2016-11-16 16:50:13 +00:00
Vincent Françoise
46f511a8c8 Implemented base + moved plugins & service conf
In this changeset, I implemented the main logic although this is
mainly a shameful copy/paste of Nova's blueprint
https://blueprints.launchpad.net/nova/+spec/centralize-config-options

Partially Implements: blueprint centralise-config-opts

Change-Id: Ib645ad5da5c706336bb6ac37e85b027d05665c32
2016-11-16 17:45:07 +01:00
Jenkins
2c5be7c974 Merge "Implemented audit.delete notification" 2016-11-16 16:43:43 +00:00
Jenkins
081ec077ad Merge "Implemented audit.create notification" 2016-11-16 16:43:37 +00:00
Jenkins
f32206d845 Merge "Implemented audit.update notification" 2016-11-16 16:43:31 +00:00
Jenkins
e476384425 Merge "Update devstack plugin to add notification param" 2016-11-16 16:42:36 +00:00
Jenkins
822fe78675 Merge "Added notification_level config option" 2016-11-16 16:30:18 +00:00
Jenkins
6fabfe2af1 Merge "Removed status_topic config parameter" 2016-11-16 16:30:13 +00:00
Jenkins
982a49b952 Merge "Remove stale notification code" 2016-11-16 16:27:09 +00:00
Jenkins
19da978a1c Merge "Added notifications documentation page" 2016-11-16 15:35:27 +00:00
Jenkins
f4bced5a79 Merge "Added support for versioned notifications" 2016-11-16 15:31:45 +00:00
Jenkins
168537a754 Merge "Added Model base class + related doc" 2016-11-16 13:55:06 +00:00
Vincent Françoise
25da1636b1 Add audit.planner events
In this changeset, I implemented the following notifications:

- audit.planner.start
- audit.planner.end
- audit.planner.error

Partially Implements: blueprint audit-versioned-notifications-api

Change-Id: I29d371f8cb3fd7948c87bec97186e24a88e8fdb6
2016-11-16 13:59:00 +01:00
Vincent Françoise
19fe0a0c56 Add audit.strategy events
In this changeset, I implemented the following notifications:

- audit.strategy.start
- audit.strategy.end
- audit.strategy.error

Partially Implements: blueprint audit-versioned-notifications-api

Change-Id: I6ae8468caf8d215bc8bc694813beb4dc94f53fdb
2016-11-16 13:59:00 +01:00
Vincent Françoise
6f9f67cacc Implemented audit.delete notification
In this changeset, I implemented the audit.delete notification.

Partially Implements: blueprint audit-versioned-notifications-api

Change-Id: I8aeb73f4b8d3273d6de7dc9c44674e3041b005ea
2016-11-16 13:59:00 +01:00
Vincent Françoise
0c0a9c84d6 Implemented audit.create notification
In this changeset, I implemented the audit.create notification.

Change-Id: Ia092ca3a3dc951e3313a07f15a98aec5818e9ab0
Partially-Implements: blueprint audit-versioned-notifications-api
2016-11-16 13:59:00 +01:00
Vincent Françoise
9405eb0806 Implemented audit.update notification
In this changeset, I implemented the sending of update notifications
whenever an audit is modified.

Change-Id: I5ccc2516ce896ae7d4ef542b133e8f052eaed602
Partially-Implements: blueprint audit-versioned-notifications-api
2016-11-16 13:59:00 +01:00
Vincent Françoise
54c45a2738 Update devstack plugin to add notification param
In this changeset, I updated the devstack plugin so it has the
necessary configuration options for it to emit the Watcher
notifications straight out of the box.

Partially Implements: blueprint watcher-notifications-ovo

Change-Id: I048e0b82cde40bd3699c90429bdf55ee235fff3b
2016-11-16 13:57:25 +01:00
Vincent Françoise
4bebf882d9 Added notification_level config option
In this changeset, I implemented the notification_level parameter
that allows you to set the level minimum level of notification to
be emitted by Watcher.

Partially Implements: blueprint watcher-notifications-ovo

Change-Id: I78b30ceb3ee7606078a87beb3a4ce5d9568af1e0
2016-11-16 13:57:25 +01:00
Vincent Françoise
cdda06c08c Removed status_topic config parameter
In this changeset, I removed the now obsolete status_topic config
option.

DocImpact
Partially Implements: blueprint watcher-notifications-ovo

Change-Id: Icfc03abd875b77fc456bfa286ac2b5774651e8fa
2016-11-16 13:57:25 +01:00
Vincent Françoise
395ccbd94c Remove stale notification code
In this changeset, I cleaned up the Watcher codebase to remove
the old notification mechanism that is actually unused.

Partially Implements: blueprint watcher-notifications-ovo

Change-Id: I1901e65f031441b98a7d6f6c9c1c0364eaaaf481
2016-11-16 13:57:25 +01:00
Vincent Françoise
cdee2719f7 Added notifications documentation page
In this changeset, I added a new Sphinx directive (from Nova) that
allows us to automatically display notification samples for of
all the upcoming notifications in Watcher.

Partially Implements: blueprint watcher-notifications-ovo

Change-Id: Id7d819ee21db6dc616c78faea483b883f77e3bd6
2016-11-16 13:57:25 +01:00
Vincent Françoise
b27e5b91b9 Added support for versioned notifications
In this changeset, I added all the required modification in order
for Watcher to enable the implementation of versioned notifications.

Change-Id: I600ecbc767583824555b016fb9fc7faf69c53b39
Partially-Implements: blueprint watcher-notifications-ovo
2016-11-16 13:57:25 +01:00
Jenkins
9dc3fce3e5 Merge "Add doc for vm_workload_consolidation strategy" 2016-11-16 09:34:06 +00:00
Bruno Grazioli
5c793894ab Add doc for vm_workload_consolidation strategy
This patch adds documentation on requirements for
vm_workload_consolidation strategy.

Change-Id: I304644138b34658b09f7cd8f29eb089757807b61
Closes-bug: #1640820
2016-11-16 09:26:31 +01:00
Alexander Chadin
8eb99ef76e Fix rally gate test
This patch set removes extra field from rally tasks
since Watcher team has removed extra field from
watcher and python-watcherclient projects.

Change-Id: Ib1640cbe8668f56f3a3a54e9f73bb1e3e6747d79
2016-11-16 11:26:27 +03:00
OpenStack Proposal Bot
3532c74001 Updated from global requirements
Change-Id: If8e1f5591d026572e8fdd187ab737801a58b9a88
2016-11-15 19:39:39 +00:00
Jenkins
c0b17105af Merge "Add doc for workload-stabilization spec" 2016-11-15 10:53:58 +00:00
Jenkins
22c424cecb Merge "optimized 'find_instance()'" 2016-11-15 10:08:21 +00:00
Jenkins
01118b72c7 Merge "Remove unused SUBMITTED audit state" 2016-11-14 15:09:57 +00:00
Alexander Chadin
9761207d8a Add doc for workload-stabilization spec
This patch set adds how-to-use documentation for
Watcher Overload standard deviation algorithm.

Change-Id: I75d7cd0ff8507ca70efb6d9668ae9fbf651a7f97
2016-11-14 18:07:11 +03:00
Jenkins
abd93a298e Merge "Fix the typo in efficacy_indicator module" 2016-11-14 14:00:30 +00:00
Jenkins
71c21c3e41 Merge "Use oslo_log instead of logging" 2016-11-14 13:53:19 +00:00
Jenkins
a487718fdc Merge "Added action_plan ObjectField for Action" 2016-11-14 10:10:10 +00:00
Jenkins
b8dddfcf67 Merge "Added audit & strategy ObjectField for ActionPlan" 2016-11-14 10:10:04 +00:00
Jenkins
2a6a96640a Merge "Added goal & strategy ObjectField for Audit" 2016-11-14 10:09:58 +00:00
Jenkins
e7d70cd279 Merge "Added goal+strategy ObjectField for AuditTemplate" 2016-11-14 10:09:02 +00:00
ericxiett
72c9d86094 Fix the typo in efficacy_indicator module
Modify typo 'gobal' to 'global'.

Change-Id: Ib8b4c65d64d767666ea1a32bc72b66559a035c5f
Closes-Bug: #1641408
2016-11-13 22:49:15 +08:00
Alexander Chadin
5e9a2f6fa6 Fix NoMetricValuesForInstance error
This patch set allows in case of NoMetricValuesForInstance exception
to continue the flow of Workload Stabilization strategy
instead of interrupting.

Closes-Bug: #1640096
Change-Id: I2de22bb97defb9c34e409102d06846275793b80d
2016-11-11 12:01:28 +03:00
howardlee
b6e17a8bc8 Use oslo_log instead of logging
1. the use of the log in the module should be unified.
2. oslo_log wrapped logging.

Change-Id: Iac48fcbcd2fc9a7002fed6a328dc74242ad770de
2016-11-10 19:22:25 +08:00
licanwei
3e392b07bf optimized 'find_instance()'
The function nova.servers.list() will return a huge context in the
large infrastructure. We can use nova.servers.get() instead of it.

Change-Id: If922c31e8aff27bfbc475c63083ee561ee7f1f67
2016-11-10 19:17:46 +08:00
Jenkins
21abbb4cd1 Merge "Added 'goal' ObjectField for Strategy object" 2016-11-10 09:38:04 +00:00
Jenkins
a539ef8c58 Merge "Refactored Watcher objects to use OVO" 2016-11-10 09:37:58 +00:00
Jenkins
0c29946590 Merge "Removed deadline, version, extra & host_aggregate" 2016-11-10 09:37:52 +00:00
Jenkins
be0a889327 Merge "Eager loading on One-to-X foreign keys" 2016-11-10 09:26:41 +00:00
Hidekazu Nakamura
ea7e6a7c94 Fix workload stabilization strategy to ignore disabled hosts
Change-Id: Ib5244da196111fac03b1ac4b756a59ad15deb778
Closes-Bug: #1640102
2016-11-10 15:55:55 +09:00
Vincent Françoise
090d5d1f1e Added Tempest API tests for /scoring_engines
In this changeset, I added tempest tests for the scoring engine
endpoint.

Change-Id: I7f36250eb9df437b518d20fd4f2e1feb223f3c82
2016-11-09 16:35:31 +05:30
Jaewoo Park
4973dd6bf6 Remove unused SUBMITTED audit state
Submitted state is not referenced nor used in the Watcher codebase,
and hence removed the references to the state from the codebase.

Change-Id: Ibb0cf5125e2d93683739abf35030b7cf5cb1b056
Closes-Bug: 1634150
2016-11-07 13:16:31 -08:00
Jenkins
ad7ae3e676 Merge "[Doc] Fix strategy list optional argument" 2016-11-07 16:11:58 +00:00
Jenkins
7ed7aade91 Merge "Added missing test on GMR plugin" 2016-11-07 14:50:13 +00:00
Jenkins
1fbc8b57bf Merge "Transform KB into MB and normalize CPU" 2016-11-07 14:24:44 +00:00
Vincent Françoise
b4b17ba395 Added action_plan ObjectField for Action
In this changeset, I added the "action_plan" ObjectField which can
either be loaded by setting the new "eager" parameter as True
or not loaded (as before) by setting it to False. The advantage of
introducing this eager parameter is that this way,
we can reduce to a minimum the overhead of DB queries whenever the
related object fields is not actually needed.

Change-Id: Iae255d9a0a919e2b6db710be24dbf9033b72166e
Partially-Implements: blueprint watcher-versioned-objects
2016-11-07 10:40:07 +01:00
Vincent Françoise
060c369838 Added audit & strategy ObjectField for ActionPlan
In this changeset, I added the "audit" and the "strategy "ObjectField
which can either be loaded by setting the new "eager" parameter as True
or not loaded (as before) by setting it to False.
The advantage of introducing this eager parameter is that this way,
we can reduce to a minimum the overhead of DB queries whenever the
related object fields is not actually needed.

Change-Id: Ieca6fa438cbf2267f5ae2ea0d059b2953e65076b
Partially-Implements: blueprint watcher-versioned-objects
2016-11-07 10:40:07 +01:00
Vincent Françoise
f9df54c555 Added goal & strategy ObjectField for Audit
In this changeset, I added the "goal" and the "strategy "ObjectField
which can either be loaded by setting the new "eager" parameter as True
or not loaded (as before) by setting it to False.
The advantage of introducing this eager parameter is that this way,
we can reduce to a minimum the overhead of DB queries whenever the
related goal is not actually needed.

Change-Id: I5a1b25e395e3d904dae954952f8e0862c3556210
Partially-Implements: blueprint watcher-versioned-objects
2016-11-07 10:39:20 +01:00
Vincent Françoise
54cf10b41c Added goal+strategy ObjectField for AuditTemplate
In this changeset, I added the "goal" and the "strategy "ObjectField
which can either be loaded by setting the new "eager" parameter as True
or not loaded (as before) by setting it to False.
The advantage of introducing this eager parameter is that this way,
we can reduce to a minimum the overhead of DB queries whenever the
related goal is not actually needed.

Change-Id: I97418163f68aea2f1cda80be94e4035f0b3700ae
Partially-Implements: blueprint watcher-versioned-objects
2016-11-07 10:39:20 +01:00
Vincent Françoise
f54aca70cc Added 'goal' ObjectField for Strategy object
In this changeset, I added the "goal" ObjectField which can
either be loaded by setting the new "eager" parameter as True or
not loaded (as before) by setting it to False.
The advantage of introducing this eager parameter is that this way,
we can reduce to a minimum the overhead of DB queries whenever the
related goal is not actually needed.

Partially-Implements: blueprint watcher-versioned-objects
Change-Id: I103c9ed161d2cedf7b43c55f9e095ef66bf44dea
2016-11-07 10:39:20 +01:00
Vincent Françoise
fc31dae7f2 Refactored Watcher objects to use OVO
In this changeset, I modified all existing Watcher objects to now
rely on oslo.versionedobjects as a base.

Change-Id: I3c9b1ca6da529d128743b99020350f28926ea1a2
Partially-Implements: blueprint watcher-versioned-objects
2016-11-07 10:39:19 +01:00
Vincent Françoise
ed95d621f4 Removed deadline, version, extra & host_aggregate
As we are about to version the Watcher objects, we need to make sure
that upcoming model/object modifications are additive in order to
avoid having to bump the major version of the API. Therefore,
this changeset removes 4 unused DB fields that were exposed in their
associated Watcher objects (i.e. AuditTemplate and Audit).

Change-Id: Ifb0783f21cd66db16b31e3c8e376fc9d6c07dea3
Partially-Implements: blueprint watcher-versioned-objects
2016-11-07 10:39:19 +01:00
Vincent Françoise
750e6bf213 Eager loading on One-to-X foreign keys
In this changeset, I added ORM relationships to the DB models
concerning the already-declared foreign keys.
I also modified the DB query building to now handle a new 'eager'
parameter that, if True, is responsible to also fetch the data
relative to these 'parent' DB entities (no cascading).

Change-Id: Ieea181af9a4b173c54621dcc6c549161f5a35aeb
Partially-Implements: blueprint watcher-versioned-objects
2016-11-07 10:37:14 +01:00
Hidekazu Nakamura
afdfd8161f [Doc] Fix strategy list optional argument
Optional argument --goal-uuid changed to --goal.
This patch fixes it.

Change-Id: I79dedc94d6437eda0363da91b5065724e22bc1c8
2016-11-04 16:29:34 +09:00
Atul Pandey
aae3f79fef Use Enum value instead of String Value
Fixing Gating Issue.

Change-Id: I64a0df9e27e09172119a3a8f5395204b7476f23d
2016-11-02 10:32:57 +00:00
Jenkins
86f4f6a979 Merge "Change "Openstack" to "OpenStack"" 2016-10-26 08:50:14 +00:00
liyanhang
b48b881c20 Change "Openstack" to "OpenStack"
According to the word choice convention in
http://docs.openstack.org/contributor-guide/writing-style/word-choice.html
We should use OpenStack instead of Openstack.

Change-Id: I4300305eabc8d7cfe0072e5ac345dab63303558c
2016-10-25 13:07:26 +00:00
qinchunhua
e681645a54 Avoid use xx=[] for parameter to initialize it's value
This patch is deprecated use xx = [] for the parameter initial value,
this parameter will only be initialized at the first call,this is not
what we expected.
Better choice is to set the initial value to None,
then initialize xx with xx= xx or [] in method body.

More details:http://effbot.org/zone/default-values.htm

Change-Id: Iee0648fd500422f48c3f7f35b5066fdc644ae308
2016-10-25 12:47:36 +00:00
Jenkins
111e04d5c3 Merge "Updated from global requirements" 2016-10-18 10:04:46 +00:00
Jenkins
26424ee670 Merge "Remove duplicate unittest" 2016-10-18 09:46:42 +00:00
Jenkins
a95b73f3bb Merge "Fix typo in docstring" 2016-10-18 09:46:34 +00:00
Alexander Chadin
ef0d133ba8 Transform KB into MB and normalize CPU
This patch set normalizes CPU for hardware.cpu.util meter and
transform KB into MB fetched from hardware.memory.used meter.

Change-Id: I6dd1d053e81f06c3adb6fb256ac77bbf55a07f3e
2016-10-18 11:21:18 +03:00
OpenStack Proposal Bot
40f98bfd07 Updated from global requirements
Change-Id: Ib8ad24fbf57fbbabc7224d2e2c203cf4aeee0bfb
2016-10-17 15:52:45 +00:00
Jenkins
10bf74041b Merge "Add strategy template doc" 2016-10-17 13:47:56 +00:00
Jenkins
b9969d4854 Merge "Fix typo in hooks.py" 2016-10-17 08:50:04 +00:00
Jenkins
989d9807e8 Merge "Test code tidy up" 2016-10-17 08:49:59 +00:00
Jenkins
751d5a00d0 Merge "Delete python bytecode file" 2016-10-17 08:49:49 +00:00
Jenkins
cc44e2a0c0 Merge "Drop MANIFEST.in - it's not needed by pbr" 2016-10-17 08:49:43 +00:00
Jenkins
0139d8537c Merge "Add service object to the watcher_db_schema" 2016-10-17 08:49:35 +00:00
qinchunhua
35e6565183 Delete python bytecode file
This patch delete python bytecode including
pyo before every test run.

Change-Id: Ie6406337072bebaaa94a2b5437a2c1cece107b56
2016-10-15 00:57:02 -04:00
Jenkins
ebc70a46db Merge "Add Audit Scope Handler" 2016-10-14 15:01:20 +00:00
David TARDIVEL
3de2d368c0 Add strategy template doc
Developer should provide a detailled documentation about his strategy
algorithm to make it even easier to use by a Watcher end user.
I propose in this changeset a template for strategy documentation.
you will find also a example with basic consolidation strategy.

Change-Id: I66da1a33b87a94b508dd23ac7dce4cae6f4e068b
2016-10-13 15:30:11 +00:00
Iswarya_Vakati
6331274708 Drop MANIFEST.in - it's not needed by pbr
watcher already uses PBR:-
setuptools.setup(
    setup_requires=['pbr>=1.8'],
    pbr=True)

This patch removes `MANIFEST.in` file as pbr generates a
sensible manifest from git files and some standard files
and it removes the need for an explicit `MANIFEST.in` file.

Change-Id: If1b80738e28d71fd9e9bb4939f94adfd721a041a
Closes-Bug:#1608980
2016-10-13 18:16:44 +05:30
Jenkins
ff81f237a7 Merge "Moved Watcher doc plugin outside main package" 2016-10-13 08:42:09 +00:00
Alexander Chadin
48cc6b2718 Add Audit Scope Handler
This patch set adds audit scope mechanism.
It also removes host_aggregate field.

Change-Id: Ia98ed180a93fc8c19599735e2b41471d322bae9a
Partially-Implements: blueprint define-the-audit-scope
2016-10-13 10:50:58 +03:00
Alexander Chadin
c34a1acbea Add service object to the watcher_db_schema
This patch set adds service object to the watcher_db_schema_diagram.txt
New watcher_db_schema_diagram.png is compiled and attached.

Partially-Implements: blueprint watcher-service-list
Change-Id: Ibaac9020b0cb9fb147259a8685a7a072216ff95d
2016-10-12 12:53:11 +00:00
Alexander Chadin
e7a1e148ca Add service supervisor
This patch set adds supervisor mechanism for Watcher services
to get ability to track states.

Partially-Implements: blueprint watcher-service-list
Change-Id: Iab1cefb971c79ed27b22b6a5d1bed8698e35f9a4
2016-10-12 15:52:06 +03:00
Jenkins
6cf796ca87 Merge "Added composite unique name constraints" 2016-10-12 12:45:53 +00:00
Jenkins
4f6d42e26a Merge "Updated from global requirements" 2016-10-12 12:13:06 +00:00
Jenkins
c3aac66add Merge "Stop adding ServiceAvailable group option" 2016-10-12 12:12:55 +00:00
OpenStack Proposal Bot
0a7a9e9ab4 Updated from global requirements
Change-Id: Ib9ba6bcb305584db7675db73850dee844e2e2c66
2016-10-11 19:17:31 +00:00
Vincent Françoise
3129f31208 Added Model base class + related doc
In this changeset I added a Model abtract base class for models
and updated the plugin docs to reference it. I also added the
CDM and CDMC acronyms to the glossary.

Change-Id: Ie7d76a9f84d545715fa6e5bf350b9d2321bab9e1
2016-10-11 17:48:14 +02:00
licanwei
cd2cd184eb Add RECOMMENDED state
The state of RECOMMENDED also can change to CANCELLED
state by Adminstrator.

Change-Id: Ia6a117367451e6fb35a55acfcbf0fb788063ea4b
2016-10-09 15:43:10 +08:00
Andreas Jaeger
f1a2de4138 Enable release notes translation
Releasenote translation publishing is being prepared. 'locale_dirs'
needs to be defined in conf.py to generate translated version of the
release notes.

Note that this repository might not get translated release notes - or
no translations at all - but we add the entry here nevertheless to
prepare for it.

Change-Id: I67b2202570e23d3f20f7d132b7d7dbb460a2af3f
2016-10-06 20:56:44 +02:00
Vincent Françoise
10cbcd2432 Added composite unique name constraints
In this changeset, I added composite contraints on models that
were currently subject to an implicit uniqueness.
This composite constraint takes as an input the name and the deleted
columns.
The 'deleted' column is 0 by default and is updated with the value of
the associated 'id' column upon soft deletion.
What this means is that this composite constraint guarantees the fact
that we can hold many soft deleted records holding the exact same name.
Indeed, the value of the deleted column will be different whilst
forbidding 2 non-soft deleted records with the same name as their
associated 'deleted' value will be 0 and subsequently violates the
constraint.

Change-Id: I3ee39e23aa4ca9bc6b4ea9af8c6b7e6d67af0136
2016-10-06 16:59:41 +02:00
Jenkins
431b54fb4c Merge "Watcher utils cleanup" 2016-10-05 15:50:11 +00:00
Vincent Françoise
f2e5702657 Added missing test on GMR plugin
The show_models() function was not covered by any test so this
changeset is adding a new unit test for it.

Change-Id: Ia861f049c1f1ee0aa623404e62fb002392c9052d
2016-10-05 15:41:56 +02:00
Vincent Françoise
96357aec04 Moved Watcher doc plugin outside main package
In this changeset, I renamed watcher/doc.py file as
doc/ext/term.py which subsequently does not get scanned for coverage
anymore. Hence, we can close this bug.

Change-Id: I9d700da6569b464ce71085fe78002521555002b9
Closes-bug: #1527163
2016-10-05 15:22:50 +02:00
Jenkins
eb0da97ea6 Merge "Updated from global requirements" 2016-10-04 16:36:36 +00:00
Nishant Kumar
631e1398a1 Stop adding ServiceAvailable group option
Service available group already exists.Therefore we don't need to
register this group here again.

Change-Id: I2096ed1f69a1f70fbf248fee5fded94e3caca6c6
Closes-Bug: #1621036
2016-10-04 20:48:11 +05:30
haris tanvir
db3b5b30b2 HasLength() rewritten to assertEqual()
HasLength() based assertions are re-written to assertEqual() based
assertion to homogenize the test code base.

For example:

assertThat(.., HasLength()) becomes assertEqual(len(),...)

Change-Id: Ia3bf24e34fffe445ff4b0ee61fb7cbc0eb36a57a
Closes-Bug:#1629898
2016-10-04 12:35:38 +05:00
OpenStack Proposal Bot
74acf2a3f2 Updated from global requirements
Change-Id: I19710f88c213dd34ed2d3bf75e208d6bfe206c0c
2016-10-03 14:17:48 +00:00
Jenkins
2642627da6 Merge "Docstrings should not start with a space" 2016-10-03 06:33:58 +00:00
Jenkins
09861a0d20 Merge "Fix capital letter in doc" 2016-10-03 06:31:03 +00:00
Jenkins
dddd29b04a Merge "Doc updates" 2016-09-28 16:12:40 +00:00
Ha Van Tu
abe37390ae Fix typo in docstring
This patch replaces typo "applicatin" by "application".

Change-Id: I3c117920966fcc4400ee30c368b61180b983dd8b
2016-09-28 12:06:44 +07:00
licanwei
8b37bef12d Remove duplicate unittest
In tests/api/test_hooks.py
the 'test_hook_without_traceback_debug'
and 'test_hook_without_traceback_debug_tracebacks'
have the same function.

Change-Id: I5209bc8b1251b77b7444d51531c3902529d11453
2016-09-28 10:47:03 +08:00
Jenkins
d3d85c4801 Merge "Fixed issue on compute nodes iteration" 2016-09-27 08:20:58 +00:00
zhangyanxian
0f9361ee6c Fix typo in hooks.py
TrivialFix

Change-Id: Iff206110433b9cd76a1a2b7c6475267a9c7a568c
2016-09-27 06:33:20 +00:00
Cao Xuan Hoang
c8d605984f Docstrings should not start with a space
As per OpenStack Docstrings guide lines [1]:
[H401] Docstrings should not start with a space.

[1] http://docs.openstack.org/developer/hacking/#docstrings

trivialfix

Change-Id: Ifdff563f3ab7559e68c7201575045013db7383a5
2016-09-27 13:01:12 +07:00
Jenkins
a5b485ae29 Merge "Refactored Tests to load scenarios from file" 2016-09-26 16:31:24 +00:00
Jenkins
0e4bae9391 Merge "Add constraint target to tox.ini and remove 1 dep" 2016-09-26 09:41:29 +00:00
Ha Van Tu
e7888b2844 Fix capital letter in doc
This patch replaces 'english' by 'English' in the Watcher doc.

Change-Id: I2de7100fbbbb4de5d2d6f9575c116986aa37982a
2016-09-26 16:10:37 +07:00
Vincent Françoise
d0ea20e4bc Doc updates
Updated inconsistent docs.

Change-Id: I4be05f662fee6ebdf721ac93dd97611b5a686273
2016-09-26 09:58:44 +02:00
Vincent Françoise
55591cce43 Watcher utils cleanup
As some of our utils function are actually available in oslo.utils,
this patchset simply gets rid of our local copy and instead references
the oslo.utils one. This will help us deport the code maintenance of
these to a 3rd party library.

Change-Id: I3760ebab69d35c37bbe78500918d4e46b5bd9119
2016-09-23 11:12:26 +02:00
Jenkins
21b3ac173a Merge "Fixed GMR configuration issue" 2016-09-21 14:37:42 +00:00
Jenkins
35a1ee7670 Merge "Fix typo in docstring from "interprete" to "interpret"" 2016-09-21 14:11:28 +00:00
Vincent Françoise
5112f294c8 Fixed GMR configuration issue
GMR was ignoring the config because the conf wasn't passed when
starting any of the Watcher services. This changeset fixes this issue.

Change-Id: If386c5f0459c4278a2a56c8c3185fcdafce673a0
2016-09-21 15:43:44 +02:00
Jenkins
28970d0512 Merge "Fix a typo in watcher.po" 2016-09-21 13:05:58 +00:00
David TARDIVEL
83d06cef5f Add constraint target to tox.ini and remove 1 dep
This adds a pip install command to tox.ini that is only used when the
tox env is passed with the 'constraints' factor appended onto it.
As such this will not effect developer workflows or current unit tests.

The initial use of this will be in a non-voting job, to verify that the
constrained checks with tox are stable.  DevStack is already running
constrained jobs, as such problems are no expected.

To run a tox with pip using constraints on a developer system a
developer should run the desired tox environment with -constraints.
For example: $(tox -epy27-constraints)
Pip will pull the current version of the upper-constraints.txt file down
from the git.openstack.org, however this method can be overriden to use
a local file setting the environment variable "UPPER_CONSTRAINTS_FILE"
to the local path or a different URL, it is passed directly to pip.

This is currently not enabled in the default tox run, however it is
possible to enable it as a default by adding it to 'envlist' in tox.ini

This also removes requirements.txt from tox.ini deps
This is redundant, per lifeless email:
http://lists.openstack.org/pipermail/openstack-dev/2015-July/069663.html

Change-Id: I79c0ceb46fc980840a8baf5fa4a303bb450bfbec
2016-09-21 10:09:47 +00:00
Jenkins
f2ec9fc99a Merge "'tox -e py27' failed" 2016-09-21 09:20:10 +00:00
OpenStack Proposal Bot
0d6166760a Updated from global requirements
Change-Id: I17b916ccbb31ca6bf72579fc10327e2e91ec3cf2
2016-09-21 07:35:28 +00:00
licanwei
d8ae88dd40 'tox -e py27' failed
cfg.CONF.debug should be set False as default,
If it's True, some unittests can't pass.

Change-Id: Ib098250af3aec48aa9d9152e20c80460f3bd641e
Closes-Bug: #1625560
2016-09-21 15:28:00 +08:00
Ha Van Tu
9eeaa07188 Fix typo in docstring from "interprete" to "interpret"
Change-Id: I931aa71dcf208a33ad8f48037b1296a4339e9b76
2016-09-21 12:16:36 +07:00
zhangyanxian
88b04dba2c Fix a typo in watcher.po
TrivialFix

Change-Id: I71a5338df14c5f5b65bed93fc26dabde447c596c
2016-09-21 03:19:35 +00:00
Jenkins
adf65f9f42 Merge "Deactivate dashboard plugin until fixed" 2016-09-20 16:18:06 +00:00
Vincent Françoise
1fc59cca9f Deactivate dashboard plugin until fixed
I deactivated the watcher-dashboard plugin in the docs since it
doesn't at the moment. Will be reactivated once it gets fixed
(see https://bugs.launchpad.net/devstack/+bug/1540328).

Change-Id: I65de8ca4a08cdb294d0e48eac52927eed13ec66c
2016-09-20 17:23:27 +02:00
Jenkins
1887ae402f Merge "Fix a typo in basic_consolidation.py" 2016-09-20 10:11:56 +00:00
licanwei
7714f48520 remove redundant word
root.description = ("Watcher is an OpenStack project which aims to"
                    "to improve physical resources usage through"
Here more one 'to'

Change-Id: I3dac6cb15af43583a1360b7f2f14d4e2c9c26cf9
2016-09-20 15:54:55 +08:00
licanwei
f741434209 Fix a typo in basic_consolidation.py
LOG.info(_LI("Initializing Sercon Consolidation"))
here 'Sercon' should be 'Server'

Change-Id: I3dc341a49066a7280bebb75471c8b433c7d0c1e7
2016-09-20 14:25:36 +08:00
Jenkins
3a3ce0268b Merge "Update Watcher description" 2016-09-19 13:33:33 +00:00
Jenkins
e647c8e01d Merge "Update reno for stable/newton" 2016-09-19 13:11:54 +00:00
Antoine Cabot
8cf233ab03 Update Watcher description
This change-set update Watcher description
as it is used in the ML to announce each
new release.

Change-Id: I6318107c3e3322a3ef734e90c9e3e0176967ceaf
2016-09-19 15:00:00 +02:00
Vincent Françoise
56a9dd0f08 Test code tidy up
Removed some comments and replace some hardocded values with the
correct variable.

Change-Id: Ica897f2de25c9de04ec2d0f94e7a13e84ee97dbb
2016-09-19 10:50:04 +02:00
Thierry Carrez
f79bed060c Update reno for stable/newton
Change-Id: Ia64ef5a4c1cd135b1bf4bbc8486270a0ff067122
2016-09-16 14:51:10 +00:00
Jenkins
e32df3c5eb Merge "Updated from global requirements" 2016-09-16 13:35:32 +00:00
Vincent Françoise
55537d254e Fixed issue on compute nodes iteration
In this changeset, I fixed the issue with the basic server
consolidation strategy to now loop over all compute nodes
as expected instead of stopping after the first one.

Change-Id: If594f0df41e39dfb0ef8f0fce41822018490c4ec
Closes-bug: #1548874
2016-09-16 14:55:51 +02:00
Vincent Françoise
e621f5ddc6 Refactored Tests to load scenarios from file
In this changeset, I simplified the logic that is used to create
cluster data model scenarios.

Change-Id: Ia6e138d9897190d3207a70485dc62ccc34087686
2016-09-16 14:51:42 +02:00
Jenkins
fc36a7a698 Merge "Remove group_by statement in metric queries" 2016-09-16 08:23:51 +00:00
OpenStack Proposal Bot
b2fe413a53 Updated from global requirements
Change-Id: I1dbcf1796b49de27fa8fb6711e4610fb934ba000
2016-09-15 20:27:53 +00:00
David TARDIVEL
b6ab86c45a Remove group_by statement in metric queries
ceilosca seems to not correctly handle group_by statement.
I remove it, because we don't need it for now.

Change-Id: Ie18551ba5a6bd155f9f4ae9c29c25b379affe019
Closes-Bug: #1623983
2016-09-15 17:16:38 +02:00
Jenkins
eeb2788355 Merge "Add rally-jobs folder to get rally support" 2016-09-15 15:15:52 +00:00
Alexander Chadin
af7871831a Add rally-jobs folder to get rally support
This patch set adds rally-jobs folder with watcher.yaml scenario
to get rally support for Watcher project

Change-Id: Icb8eace045d86a9b78b543a8a49fe747f4b00772
2016-09-15 12:54:10 +00:00
Vincent Françoise
fbd9411fd9 Log CDM structure before+after executing strategy
In this changeset, I added debug logs to dump the cluster data model
copy structure (as XML) before and after the execution of the strategy.

By doing so, we can see the cluster data model structure that is
expected after the execution of the corresponding action plan.

Change-Id: I81c23e148a78d9b176154f7620087a322a5bce28
2016-09-15 12:25:44 +00:00
Jenkins
0a0f482f2d Merge "Fixed Tempest test due to notification issues" 2016-09-15 09:55:33 +00:00
Jenkins
0873c26b17 Merge "Use memory mode for sqlite in db test" 2016-09-14 13:39:51 +00:00
Jenkins
af99b3f4eb Merge "Use parameters instead of config for workload stabilization" 2016-09-14 13:33:34 +00:00
Jenkins
4b20e991a1 Merge "Added tests on API hooks and related context" 2016-09-14 12:41:09 +00:00
Jenkins
e907cec90a Merge "When action plan is empty, its state is incorrect" 2016-09-14 12:39:39 +00:00
Jenkins
9f814e6c15 Merge "The default value of 'is_admin_project'" 2016-09-14 12:38:00 +00:00
Jenkins
5ac51efa69 Merge "Implemented GMR plugin to show CDM structures" 2016-09-14 10:09:55 +00:00
Jenkins
0baec1cfc2 Merge "Fixed indentation" 2016-09-14 09:29:51 +00:00
Vincent Françoise
72e6564549 Fixed Tempest test due to notification issues
Change-Id: I33a0764060600b8e3d6bec757669490b9003b345
2016-09-09 16:03:12 +02:00
ChangBo Guo(gcb)
23092b6f84 Use memory mode for sqlite in db test
Config option sqlite_db is deprecated in
0a1bae9859079fb21a03716be947c5f1da6db0a2, and deprecate argumentsqlite_db in method set_defaults in
https://review.openstack.org/#/c/350945/, should use config option
connection instead. For watcher, we test database with sqlite in memory
mode [1] as below:

cfg.CONF.set_override("connection", "sqlite://",
                      group="database", enforce_type=True)

and don't use config option sqlite_db, so remove unused code.

[1]http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html#sqlite

Change-Id: I9b1f995e1b7004bcfe6c5a854c2f83b24e5bfb56
2016-09-09 19:51:15 +08:00
Vincent Françoise
0b276fb602 Added tests on API hooks and related context
In this changeset, I raised the overall test coverage on the Watcher
API hooks.

Change-Id: I718030fdd23e83bf50d22b00828664adf66fc59d
2016-09-08 12:18:07 +02:00
licanwei
a684e70b61 When action plan is empty, its state is incorrect
When the action plan is empty,its state is still 'RECOMMENDED'.
I think it's should be 'SUCCEED'.
Closes-Bug: #1619155

Change-Id: Icd10707c7892089999f8b37775ca62bde47db0c4
2016-09-08 13:37:39 +08:00
Jenkins
414685ab53 Merge "Add documentation for Scoring Module" 2016-09-07 14:20:32 +00:00
Alexander Chadin
2d8650f87a Use parameters instead of config for workload stabilization
This patch set allows to use audit parameters for
workload-stabilization strategy and makes some little
refactoring.

Closes-Bug: #1620604
Change-Id: I60e34611d4dd001beed31666fd11d2ab11c1723c
2016-09-07 17:15:07 +03:00
Tomasz Kaczynski
dd924dd9d5 Add documentation for Scoring Module
This change is updating the glossary with Scoring Module terms
and adding an information page about implementing scoring engine
plugin.

Partially-Implements: blueprint scoring-module
Change-Id: I411370dcc003ed837d8ce67659ecbfef3548ee11
2016-09-07 08:13:36 +00:00
Jenkins
dd5b5428db Merge "Fix incorrect strings and formatting" 2016-09-06 10:58:16 +00:00
Vincent Françoise
74989fe94e Implemented GMR plugin to show CDM structures
In this changeset, I implemented a small GMR plugin that converts
the cluster data model structure into an XML structure.

Change-Id: I75548952635a0aa3c7dbd6d068831b5765a5db1a
Closes-Bug: #1620551
2016-09-06 12:15:51 +02:00
Michelle Mandel
3b673fe9bd Fix incorrect strings and formatting
There were a few typos and some lines of code that weren't formatted
properly. Now, the typos are fixed and the misformatted code is
corrected.

Change-Id: I6009e44f65b9be1ae9edc806618342c334b2fa92
2016-09-02 14:46:05 -04:00
Bin Zhou
c1cbd9ebf4 Modify use of assertTrue(A in B)
Developers should use assertIn(A, B) instead of assertTrue(A in B ).

TrivialFix

Change-Id: Ie169e731c800b5d77fd231f09ac7f92090287ef3
2016-09-02 12:43:56 +08:00
Gábor Antal
add49f3fcb Fixed indentation
In some places, indentation was wrong.
In this patchset, they are fixed.

Change-Id: Ic66105d53b76cf53fbb4bc7bc19329b83b89687e
2016-09-01 19:00:25 +02:00
licanwei
1f4a46ea5d The default value of 'is_admin_project'
The default value of 'is_admin_project' should be 'True'
As in the file '/oslo_context/context.py':
    def __init__(self,...
                 is_admin_project=True)

Change-Id: Id5e3f7347819deff27c6423679f9964fbb35f047
2016-08-31 15:30:31 +08:00
280 changed files with 13075 additions and 5838 deletions

View File

@@ -1,6 +0,0 @@
include AUTHORS
include ChangeLog
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

View File

@@ -1,3 +1,12 @@
========================
Team and repository tags
========================
.. image:: http://governance.openstack.org/badges/watcher.svg
:target: http://governance.openstack.org/reference/tags/index.html
.. Change things from this point on
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
@@ -10,10 +19,8 @@ Watcher
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
Watcher 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!

View File

@@ -126,6 +126,8 @@ function create_watcher_conf {
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_host $RABBIT_HOST
iniset $WATCHER_CONF oslo_messaging_notifications driver "messaging"
iniset $NOVA_CONF oslo_messaging_notifications topics "notifications,watcher_notifications"
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR

View File

@@ -28,7 +28,7 @@ ENABLED_SERVICES+=,q-svc,q-dhcp,q-meta,q-agt,q-l3,neutron
enable_service n-cauth
# Enable the Watcher Dashboard plugin
enable_plugin watcher-dashboard git://git.openstack.org/openstack/watcher-dashboard
# enable_plugin watcher-dashboard git://git.openstack.org/openstack/watcher-dashboard
# Enable the Watcher plugin
enable_plugin watcher git://git.openstack.org/openstack/watcher

View File

@@ -75,7 +75,7 @@ class WatcherTerm(BaseWatcherDirective):
# Inside your .rst file
.. watcher-term:: import.path.to.your.DocumentedObject
This directive will then import the docstring and then interprete it.
This directive will then import the docstring and then interpret it.
"""
# You need to put an import path as an argument for this directive to work
@@ -85,7 +85,12 @@ class WatcherTerm(BaseWatcherDirective):
cls_path = self.arguments[0]
try:
cls = importlib.import_module(cls_path)
try:
cls = importlib.import_module(cls_path)
except ImportError:
module_name, cls_name = cls_path.rsplit('.', 1)
mod = importlib.import_module(module_name)
cls = getattr(mod, cls_name)
except Exception as exc:
raise self.error(exc)
@@ -97,6 +102,72 @@ class WatcherTerm(BaseWatcherDirective):
return node.children
class WatcherFunc(BaseWatcherDirective):
"""Directive to import a value returned by a func into the Watcher doc
**How to use it**
# inside your .py file
class Bar(object):
def foo(object):
return foo_string
# Inside your .rst file
.. watcher-func:: import.path.to.your.Bar.foo node_classname
node_classname is decumented here:
http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html
This directive will then import the value and then interpret it.
"""
# You need to put an import path as an argument for this directive to work
# required_arguments = 1
# optional_arguments = 1
option_spec = {'format': rst.directives.unchanged}
has_content = True
def run(self):
if not self.content:
error = self.state_machine.reporter.error(
'The "%s" directive is empty; content required.' % self.name,
nodes.literal_block(self.block_text, self.block_text),
line=self.lineno)
return [error]
func_path = self.content[0]
try:
cls_path, func_name = func_path.rsplit('.', 1)
module_name, cls_name = cls_path.rsplit('.', 1)
mod = importlib.import_module(module_name)
cls = getattr(mod, cls_name)
except Exception as exc:
raise self.error(exc)
cls_obj = cls()
func = getattr(cls_obj, func_name)
textblock = func()
if not isinstance(textblock, str):
textblock = str(textblock)
self.add_textblock(textblock)
try:
node_class = getattr(nodes,
self.options.get('format', 'paragraph'))
except Exception as exc:
raise self.error(exc)
node = node_class()
node.document = self.state.document
self.state.nested_parse(self.result, 0, node)
return [node]
def setup(app):
app.add_directive('watcher-term', WatcherTerm)
app.add_directive('watcher-func', WatcherFunc)
return {'version': version_info.version_string()}

View File

@@ -0,0 +1,133 @@
# 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.
"""
This provides a sphinx extension able to list the implemented versioned
notifications into the developer documentation.
It is used via a single directive in the .rst file
.. versioned_notifications::
"""
from sphinx.util.compat import Directive
from docutils import nodes
from watcher.notifications import base as notification
from watcher.objects import base
class VersionedNotificationDirective(Directive):
SAMPLE_ROOT = 'doc/notification_samples/'
TOGGLE_SCRIPT = """
<script>
jQuery(document).ready(function(){
jQuery('#%s-div').toggle('show');
jQuery('#%s-hideshow').on('click', function(event) {
jQuery('#%s-div').toggle('show');
});
});
</script>
"""
def run(self):
notifications = self._collect_notifications()
return self._build_markup(notifications)
def _collect_notifications(self):
base.WatcherObjectRegistry.register_notification_objects()
notifications = []
ovos = base.WatcherObjectRegistry.obj_classes()
for name, cls in ovos.items():
cls = cls[0]
if (issubclass(cls, notification.NotificationBase) and
cls != notification.NotificationBase):
payload_name = cls.fields['payload'].objname
payload_cls = ovos[payload_name][0]
for sample in cls.samples:
notifications.append((cls.__name__,
payload_cls.__name__,
sample))
return sorted(notifications)
def _build_markup(self, notifications):
content = []
cols = ['Event type', 'Notification class', 'Payload class', 'Sample']
table = nodes.table()
content.append(table)
group = nodes.tgroup(cols=len(cols))
table.append(group)
head = nodes.thead()
group.append(head)
for _ in cols:
group.append(nodes.colspec(colwidth=1))
body = nodes.tbody()
group.append(body)
# fill the table header
row = nodes.row()
body.append(row)
for col_name in cols:
col = nodes.entry()
row.append(col)
text = nodes.strong(text=col_name)
col.append(text)
# fill the table content, one notification per row
for name, payload, sample_file in notifications:
event_type = sample_file[0: -5].replace('-', '.')
row = nodes.row()
body.append(row)
col = nodes.entry()
row.append(col)
text = nodes.literal(text=event_type)
col.append(text)
col = nodes.entry()
row.append(col)
text = nodes.literal(text=name)
col.append(text)
col = nodes.entry()
row.append(col)
text = nodes.literal(text=payload)
col.append(text)
col = nodes.entry()
row.append(col)
with open(self.SAMPLE_ROOT + sample_file, 'r') as f:
sample_content = f.read()
event_type = sample_file[0: -5]
html_str = self.TOGGLE_SCRIPT % ((event_type, ) * 3)
html_str += ("<input type='button' id='%s-hideshow' "
"value='hide/show sample'>" % event_type)
html_str += ("<div id='%s-div'><pre>%s</pre></div>"
% (event_type, sample_content))
raw = nodes.raw('', html_str, format="html")
col.append(raw)
return content
def setup(app):
app.add_directive('versioned_notifications',
VersionedNotificationDirective)

View File

@@ -0,0 +1,69 @@
{
"priority": "INFO",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "PENDING",
"updated_at": null,
"deleted_at": null,
"goal": {
"watcher_object.data": {
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"name": "dummy",
"updated_at": null,
"deleted_at": null,
"efficacy_specification": [],
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy goal"
},
"watcher_object.name": "GoalPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"interval": null,
"scope": [],
"strategy": {
"watcher_object.data": {
"parameters_spec": {
"properties": {
"para2": {
"type": "string",
"default": "hello",
"description": "string parameter example"
},
"para1": {
"description": "number parameter example",
"maximum": 10.2,
"type": "number",
"default": 3.2,
"minimum": 1.0
}
}
},
"name": "dummy",
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"updated_at": null,
"deleted_at": null,
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy strategy"
},
"watcher_object.name": "StrategyPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"created_at": "2016-11-04T16:29:20Z",
"uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6"
},
"watcher_object.name": "AuditCreatePayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:31:36.264673 ",
"event_type": "audit.create",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,69 @@
{
"priority": "INFO",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "DELETED",
"updated_at": null,
"deleted_at": null,
"goal": {
"watcher_object.data": {
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"name": "dummy",
"updated_at": null,
"deleted_at": null,
"efficacy_specification": [],
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy goal"
},
"watcher_object.name": "GoalPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"interval": null,
"scope": [],
"strategy": {
"watcher_object.data": {
"parameters_spec": {
"properties": {
"para2": {
"type": "string",
"default": "hello",
"description": "string parameter example"
},
"para1": {
"description": "number parameter example",
"maximum": 10.2,
"type": "number",
"default": 3.2,
"minimum": 1.0
}
}
},
"name": "dummy",
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"updated_at": null,
"deleted_at": null,
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy strategy"
},
"watcher_object.name": "StrategyPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"created_at": "2016-11-04T16:29:20Z",
"uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6"
},
"watcher_object.name": "AuditDeletePayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:31:36.264673 ",
"event_type": "audit.delete",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,70 @@
{
"priority": "INFO",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": null,
"goal": {
"watcher_object.data": {
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"name": "dummy",
"updated_at": null,
"deleted_at": null,
"efficacy_specification": [],
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy goal"
},
"watcher_object.name": "GoalPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"interval": null,
"scope": [],
"strategy": {
"watcher_object.data": {
"parameters_spec": {
"properties": {
"para2": {
"type": "string",
"default": "hello",
"description": "string parameter example"
},
"para1": {
"description": "number parameter example",
"maximum": 10.2,
"type": "number",
"default": 3.2,
"minimum": 1.0
}
}
},
"name": "dummy",
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"updated_at": null,
"deleted_at": null,
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy strategy"
},
"watcher_object.name": "StrategyPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"created_at": "2016-11-04T16:29:20Z",
"uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6"
},
"watcher_object.name": "AuditActionPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:31:36.264673 ",
"event_type": "audit.planner.end",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,80 @@
{
"priority": "ERROR",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": {
"watcher_object.data": {
"exception": "WatcherException",
"exception_message": "TEST",
"function_name": "test_send_audit_action_with_error",
"module_name": "watcher.tests.notifications.test_audit_notification"
},
"watcher_object.name": "ExceptionPayload",
"watcher_object.namespace": "watcher",
"watcher_object.version": "1.0"
},
"goal": {
"watcher_object.data": {
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"name": "dummy",
"updated_at": null,
"deleted_at": null,
"efficacy_specification": [],
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy goal"
},
"watcher_object.name": "GoalPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"interval": null,
"scope": [],
"strategy": {
"watcher_object.data": {
"parameters_spec": {
"properties": {
"para2": {
"type": "string",
"default": "hello",
"description": "string parameter example"
},
"para1": {
"description": "number parameter example",
"maximum": 10.2,
"type": "number",
"default": 3.2,
"minimum": 1.0
}
}
},
"name": "dummy",
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"updated_at": null,
"deleted_at": null,
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy strategy"
},
"watcher_object.name": "StrategyPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"created_at": "2016-11-04T16:29:20Z",
"uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6"
},
"watcher_object.name": "AuditActionPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:31:36.264673 ",
"event_type": "audit.planner.error",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,70 @@
{
"priority": "INFO",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": null,
"goal": {
"watcher_object.data": {
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"name": "dummy",
"updated_at": null,
"deleted_at": null,
"efficacy_specification": [],
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy goal"
},
"watcher_object.name": "GoalPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"interval": null,
"scope": [],
"strategy": {
"watcher_object.data": {
"parameters_spec": {
"properties": {
"para2": {
"type": "string",
"default": "hello",
"description": "string parameter example"
},
"para1": {
"description": "number parameter example",
"maximum": 10.2,
"type": "number",
"default": 3.2,
"minimum": 1.0
}
}
},
"name": "dummy",
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"updated_at": null,
"deleted_at": null,
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy strategy"
},
"watcher_object.name": "StrategyPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"created_at": "2016-11-04T16:29:20Z",
"uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6"
},
"watcher_object.name": "AuditActionPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:31:36.264673 ",
"event_type": "audit.planner.start",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,70 @@
{
"priority": "INFO",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": null,
"goal": {
"watcher_object.data": {
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"name": "dummy",
"updated_at": null,
"deleted_at": null,
"efficacy_specification": [],
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy goal"
},
"watcher_object.name": "GoalPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"interval": null,
"scope": [],
"strategy": {
"watcher_object.data": {
"parameters_spec": {
"properties": {
"para2": {
"type": "string",
"default": "hello",
"description": "string parameter example"
},
"para1": {
"description": "number parameter example",
"maximum": 10.2,
"type": "number",
"default": 3.2,
"minimum": 1.0
}
}
},
"name": "dummy",
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"updated_at": null,
"deleted_at": null,
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy strategy"
},
"watcher_object.name": "StrategyPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"created_at": "2016-11-04T16:29:20Z",
"uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6"
},
"watcher_object.name": "AuditActionPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:31:36.264673 ",
"event_type": "audit.strategy.end",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,80 @@
{
"priority": "ERROR",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": {
"watcher_object.data": {
"exception": "WatcherException",
"exception_message": "TEST",
"function_name": "test_send_audit_action_with_error",
"module_name": "watcher.tests.notifications.test_audit_notification"
},
"watcher_object.name": "ExceptionPayload",
"watcher_object.namespace": "watcher",
"watcher_object.version": "1.0"
},
"goal": {
"watcher_object.data": {
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"name": "dummy",
"updated_at": null,
"deleted_at": null,
"efficacy_specification": [],
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy goal"
},
"watcher_object.name": "GoalPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"interval": null,
"scope": [],
"strategy": {
"watcher_object.data": {
"parameters_spec": {
"properties": {
"para2": {
"type": "string",
"default": "hello",
"description": "string parameter example"
},
"para1": {
"description": "number parameter example",
"maximum": 10.2,
"type": "number",
"default": 3.2,
"minimum": 1.0
}
}
},
"name": "dummy",
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"updated_at": null,
"deleted_at": null,
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy strategy"
},
"watcher_object.name": "StrategyPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"created_at": "2016-11-04T16:29:20Z",
"uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6"
},
"watcher_object.name": "AuditActionPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:31:36.264673 ",
"event_type": "audit.strategy.error",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,70 @@
{
"priority": "INFO",
"payload": {
"watcher_object.data": {
"audit_type": "ONESHOT",
"parameters": {
"para2": "hello",
"para1": 3.2
},
"state": "ONGOING",
"updated_at": null,
"deleted_at": null,
"fault": null,
"goal": {
"watcher_object.data": {
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"name": "dummy",
"updated_at": null,
"deleted_at": null,
"efficacy_specification": [],
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy goal"
},
"watcher_object.name": "GoalPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"interval": null,
"scope": [],
"strategy": {
"watcher_object.data": {
"parameters_spec": {
"properties": {
"para2": {
"type": "string",
"default": "hello",
"description": "string parameter example"
},
"para1": {
"description": "number parameter example",
"maximum": 10.2,
"type": "number",
"default": 3.2,
"minimum": 1.0
}
}
},
"name": "dummy",
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"updated_at": null,
"deleted_at": null,
"created_at": "2016-11-04T16:25:35Z",
"display_name": "Dummy strategy"
},
"watcher_object.name": "StrategyPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"created_at": "2016-11-04T16:29:20Z",
"uuid": "4a97b9dd-2023-43dc-b713-815bdd94d4d6"
},
"watcher_object.name": "AuditActionPayload",
"watcher_object.version": "1.0",
"watcher_object.namespace": "watcher"
},
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:31:36.264673 ",
"event_type": "audit.strategy.start",
"message_id": "cbcf9f2c-7c53-4b4d-91ec-db49cca024b6"
}

View File

@@ -0,0 +1,78 @@
{
"publisher_id": "infra-optim:localhost",
"timestamp": "2016-11-04 16:51:38.722986 ",
"payload": {
"watcher_object.name": "AuditUpdatePayload",
"watcher_object.data": {
"strategy": {
"watcher_object.name": "StrategyPayload",
"watcher_object.data": {
"name": "dummy",
"parameters_spec": {
"properties": {
"para2": {
"default": "hello",
"type": "string",
"description": "string parameter example"
},
"para1": {
"maximum": 10.2,
"default": 3.2,
"minimum": 1.0,
"description": "number parameter example",
"type": "number"
}
}
},
"updated_at": null,
"display_name": "Dummy strategy",
"deleted_at": null,
"uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
"created_at": "2016-11-04T16:25:35Z"
},
"watcher_object.namespace": "watcher",
"watcher_object.version": "1.0"
},
"scope": [],
"created_at": "2016-11-04T16:51:21Z",
"uuid": "f1e0d912-afd9-4bf2-91ef-c99cd08cc1ef",
"goal": {
"watcher_object.name": "GoalPayload",
"watcher_object.data": {
"efficacy_specification": [],
"updated_at": null,
"name": "dummy",
"display_name": "Dummy goal",
"deleted_at": null,
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
"created_at": "2016-11-04T16:25:35Z"
},
"watcher_object.namespace": "watcher",
"watcher_object.version": "1.0"
},
"parameters": {
"para2": "hello",
"para1": 3.2
},
"deleted_at": null,
"state_update": {
"watcher_object.name": "AuditStateUpdatePayload",
"watcher_object.data": {
"state": "ONGOING",
"old_state": "PENDING"
},
"watcher_object.namespace": "watcher",
"watcher_object.version": "1.0"
},
"interval": null,
"updated_at": null,
"state": "ONGOING",
"audit_type": "ONESHOT"
},
"watcher_object.namespace": "watcher",
"watcher_object.version": "1.0"
},
"priority": "INFO",
"event_type": "audit.update",
"message_id": "697fdf55-7252-4b6c-a2c2-5b9e85f6342c"
}

View File

@@ -0,0 +1,16 @@
{
"event_type": "infra-optim.exception",
"payload": {
"watcher_object.data": {
"exception": "NoAvailableStrategyForGoal",
"exception_message": "No strategy could be found to achieve the server_consolidation goal.",
"function_name": "_aggregate_create_in_db",
"module_name": "watcher.objects.aggregate"
},
"watcher_object.name": "ExceptionPayload",
"watcher_object.namespace": "watcher",
"watcher_object.version": "1.0"
},
"priority": "ERROR",
"publisher_id": "watcher-api:fake-mini"
}

View File

@@ -171,12 +171,12 @@ 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 first reads the parameters of the :ref:`Audit <audit_definition>` to know
the :ref:`Goal <goal_definition>` to achieve.
It then selects the most appropriate :ref:`Strategy <strategy_definition>`
from the list of available strategies achieving this goal.
Unless specified, it then selects the most appropriate :ref:`strategy
<strategy_definition>` from the list of available strategies achieving this
goal.
The :ref:`Strategy <strategy_definition>` is then dynamically loaded (via
`stevedore <http://docs.openstack.org/developer/stevedore/>`_). The
@@ -290,7 +290,7 @@ the Audit parameters from the
:ref:`Watcher Database <watcher_database_definition>`. It instantiates the
appropriate :ref:`strategy <strategy_definition>` (using entry points)
given both the :ref:`goal <goal_definition>` and the strategy associated to the
parent :ref:`audit template <audit_template_definition>` of the :ref:`Audit
parent :ref:`audit template <audit_template_definition>` of the :ref:`audit
<audit_definition>`. If no strategy is associated to the audit template, the
strategy is dynamically selected by the Decision Engine.
@@ -298,7 +298,7 @@ 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>`.
:ref:`OpenStack cluster <cluster_definition>`.
The :ref:`Watcher Decision Engine <watcher_decision_engine_definition>` calls
the **execute()** method of the instantiated
@@ -316,7 +316,7 @@ 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
**RECOMMENDED** and saves it into the :ref:`Watcher Database
<watcher_database_definition>`. The saved action plan is now a scheduled flow
of actions to which a global efficacy is associated alongside a number of
:ref:`Efficacy Indicators <efficacy_indicator_definition>` as specified by the
@@ -450,7 +450,7 @@ state may be one of the following:
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
**RECOMMENDED**, **PENDING** or **ONGOING** state and was cancelled by the
:ref:`Administrator <administrator_definition>`

View File

@@ -11,7 +11,21 @@
# 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
from watcher import objects
objects.register_all()
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../../'))
sys.path.insert(0, os.path.abspath('../'))
sys.path.insert(0, os.path.abspath('./'))
# -- General configuration ----------------------------------------------------
@@ -26,7 +40,8 @@ extensions = [
'sphinxcontrib.pecanwsme.rest',
'stevedore.sphinxext',
'wsmeext.sphinxext',
'watcher.doc',
'ext.term',
'ext.versioned_notifications',
]
wsme_protocols = ['restjson']
@@ -67,6 +82,8 @@ exclude_patterns = [
# them when scanning for input files.
'man/footer.rst',
'man/general-options.rst',
'strategies/strategy-template.rst',
'image_src/plantuml/README.rst',
]
# If true, '()' will be appended to :func: etc. cross-reference text.

View File

@@ -93,11 +93,11 @@ following command:
.. code:: bash
$ watcher strategy list --goal-uuid <your_goal_uuid>
$ watcher strategy list --goal <your_goal_uuid_or_name>
or::
$ openstack optimize strategy list --goal-uuid <your_goal_uuid>
$ openstack optimize strategy list --goal <your_goal_uuid_or_name>
You can use the following command to check strategy details including which
parameters of which format it supports:

View File

@@ -0,0 +1,13 @@
..
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_notifications:
========================
Notifications in Watcher
========================
.. versioned_notifications::

View File

@@ -22,7 +22,7 @@ cluster data model collectors within Watcher.
Creating a new plugin
=====================
In order to create a new model, you have to:
In order to create a new cluster data model collector, you have to:
- Extend the :py:class:`~.base.BaseClusterDataModelCollector` class.
- Implement its :py:meth:`~.BaseClusterDataModelCollector.execute` abstract
@@ -65,6 +65,49 @@ This implementation is the most basic one. So in order to get a better
understanding on how to implement a more advanced cluster data model collector,
have a look at the :py:class:`~.NovaClusterDataModelCollector` class.
Define a custom model
=====================
As you may have noticed in the above example, we are reusing an existing model
provided by Watcher. However, this model can be easily customized by
implementing a new class that would implement the :py:class:`~.Model` abstract
base class. Here below is simple example on how to proceed in implementing a
custom Model:
.. code-block:: python
# Filepath = <PROJECT_DIR>/thirdparty/dummy.py
# Import path = thirdparty.dummy
from watcher.decision_engine.model import base as modelbase
from watcher.decision_engine.model.collector import base
class MyModel(modelbase.Model):
def to_string(self):
return 'MyModel'
class DummyClusterDataModelCollector(base.BaseClusterDataModelCollector):
def execute(self):
model = MyModel()
# Do something here...
return model
@property
def notification_endpoints(self):
return []
Here below is the abstract ``Model`` class that every single cluster data model
should implement:
.. autoclass:: watcher.decision_engine.model.base.Model
:members:
:special-members: __init__
:noindex:
Define configuration parameters
===============================

View File

@@ -60,8 +60,8 @@ Here is an example showing how you can define a new ``NewGoal`` goal plugin:
# import path: thirdparty.new
from watcher._i18n import _
from watcher.decision_engine.goal import base
from watcher.decision_engine.goal.efficacy import specs
from watcher.decision_engine.strategy.strategies import base
class NewGoal(base.Goal):
@@ -79,11 +79,11 @@ Here is an example showing how you can define a new ``NewGoal`` goal plugin:
@classmethod
def get_efficacy_specification(cls):
return specs.UnclassifiedStrategySpecification()
return specs.Unclassified()
As you may have noticed, the :py:meth:`~.Goal.get_efficacy_specification`
method returns an :py:meth:`~.UnclassifiedStrategySpecification` instance which
method returns an :py:meth:`~.Unclassified` instance which
is provided by Watcher. This efficacy specification is useful during the
development process of your goal as it corresponds to an empty specification.
If you want to learn more about what efficacy specifications are used for or to

View File

@@ -39,7 +39,7 @@ Here is an example showing how you can write a planner plugin called
# Filepath = third-party/third_party/dummy.py
# Import path = third_party.dummy
import uuid
from oslo_utils import uuidutils
from watcher.decision_engine.planner import base
@@ -47,7 +47,7 @@ Here is an example showing how you can write a planner plugin called
def _create_action_plan(self, context, audit_id):
action_plan_dict = {
'uuid': uuid.uuid4(),
'uuid': uuidutils.generate_uuid(),
'audit_id': audit_id,
'first_action_id': None,
'state': objects.action_plan.State.RECOMMENDED

View File

@@ -0,0 +1,210 @@
..
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_scoring_engine_plugin:
==========================
Build a new scoring engine
==========================
Watcher Decision Engine has an external :ref:`scoring engine
<scoring_engine_definition>` plugin interface which gives anyone the ability
to integrate an external scoring engine in order to make use of it in a
:ref:`strategy <strategy_definition>`.
This section gives some guidelines on how to implement and integrate custom
scoring engines with Watcher. If you wish to create a third-party package for
your plugin, you can refer to our :ref:`documentation for third-party package
creation <plugin-base_setup>`.
Pre-requisites
==============
Because scoring engines execute a purely mathematical tasks, they typically do
not have any additional dependencies. Additional requirements might be defined
by specific scoring engine implementations. For example, some scoring engines
might require to prepare learning data, which has to be loaded during the
scoring engine startup. Some other might require some external services to be
available (e.g. if the scoring infrastructure is running in the cloud).
Create a new scoring engine plugin
==================================
In order to create a new scoring engine you have to:
- Extend the :py:class:`~.ScoringEngine` class
- Implement its :py:meth:`~.ScoringEngine.get_name` method to return the
**unique** ID of the new scoring engine you want to create. This unique ID
should be the same as the name of :ref:`the entry point we will declare later
on <scoring_engine_plugin_add_entrypoint>`.
- Implement its :py:meth:`~.ScoringEngine.get_description` method to return the
user-friendly description of the implemented scoring engine. It might contain
information about algorithm used, learning data etc.
- Implement its :py:meth:`~.ScoringEngine.get_metainfo` method to return the
machine-friendly metadata about this scoring engine. For example, it could be
a JSON formatted text with information about the data model used, its input
and output data format, column names, etc.
- Implement its :py:meth:`~.ScoringEngine.calculate_score` method to return the
result calculated by this scoring engine.
Here is an example showing how you can write a plugin called ``NewScorer``:
.. code-block:: python
# filepath: thirdparty/new.py
# import path: thirdparty.new
from watcher.decision_engine.scoring import base
class NewScorer(base.ScoringEngine):
def get_name(self):
return 'new_scorer'
def get_description(self):
return ''
def get_metainfo(self):
return """{
"feature_columns": [
"column1",
"column2",
"column3"],
"result_columns": [
"value",
"probability"]
}"""
def calculate_score(self, features):
return '[12, 0.83]'
As you can see in the above example, the
:py:meth:`~.ScoringEngine.calculate_score` method returns a string. Both this
class and the client (caller) should perform all the necessary serialization
or deserialization.
(Optional) Create a new scoring engine container plugin
=======================================================
Optionally, it's possible to implement a container plugin, which can return a
list of scoring engines. This list can be re-evaluated multiple times during
the lifecycle of :ref:`Watcher Decision Engine
<watcher_decision_engine_definition>` and synchronized with :ref:`Watcher
Database <watcher_database_definition>` using the ``watcher-sync`` command line
tool.
Below is an example of a container using some scoring engine implementation
that is simply made of a client responsible for communicating with a real
scoring engine deployed as a web service on external servers:
.. code-block:: python
class NewScoringContainer(base.ScoringEngineContainer):
@classmethod
def get_scoring_engine_list(self):
return [
RemoteScoringEngine(
name='scoring_engine1',
description='Some remote Scoring Engine 1',
remote_url='http://engine1.example.com/score'),
RemoteScoringEngine(
name='scoring_engine2',
description='Some remote Scoring Engine 2',
remote_url='http://engine2.example.com/score'),
]
Abstract Plugin Class
=====================
Here below is the abstract :py:class:`~.ScoringEngine` class:
.. autoclass:: watcher.decision_engine.scoring.base.ScoringEngine
:members:
:special-members: __init__
:noindex:
Abstract Plugin Container Class
===============================
Here below is the abstract :py:class:`~.ScoringContainer` class:
.. autoclass:: watcher.decision_engine.scoring.base.ScoringEngineContainer
:members:
:special-members: __init__
:noindex:
.. _scoring_engine_plugin_add_entrypoint:
Add a new entry point
=====================
In order for the Watcher Decision Engine to load your new scoring engine, it
must be registered as a named entry point under the ``watcher_scoring_engines``
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 and should be the same
as the value returned by the :py:meth:`~.ScoringEngine.get_name` method of your
strategy.
Here below is how you would proceed to register ``NewScorer`` using pbr_:
.. code-block:: ini
[entry_points]
watcher_scoring_engines =
new_scorer = thirdparty.new:NewScorer
To get a better understanding on how to implement a more advanced scoring
engine, have a look at the :py:class:`~.DummyScorer` class. This implementation
is not really using machine learning, but other than that it contains all the
pieces which the "real" implementation would have.
In addition, for some use cases there is a need to register a list (possibly
dynamic, depending on the implementation and configuration) of scoring engines
in a single plugin, so there is no need to restart :ref:`Watcher Decision
Engine <watcher_decision_engine_definition>` every time such list changes. For
these cases, an additional ``watcher_scoring_engine_containers`` entry point
can be used.
For the example how to use scoring engine containers, please have a look at
the :py:class:`~.DummyScoringContainer` and the way it is configured in
``setup.cfg``. For new containers it could be done like this:
.. code-block:: ini
[entry_points]
watcher_scoring_engine_containers =
new_scoring_container = thirdparty.new:NewContainer
.. _pbr: http://docs.openstack.org/developer/pbr/
Using scoring engine 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 scan and register inside the :ref:`Watcher Database
<watcher_database_definition>` all the scoring engines you implemented upon
restarting the :ref:`Watcher Decision Engine
<watcher_decision_engine_definition>`.
In addition, ``watcher-sync`` tool can be used to trigger :ref:`Watcher
Database <watcher_database_definition>` synchronization. This might be used for
"dynamic" scoring containers, which can return different scoring engines based
on some external configuration (if they support that).

View File

@@ -43,7 +43,7 @@ In order to create a new strategy, you have to:
Note: Do not use a variable to return the translated string so it can be
automatically collected by the translation tool.
- Implement its :py:meth:`~.BaseStrategy.get_translatable_display_name`
class method to return the translation key (actually the english display
class method to return the translation key (actually the English display
name) of your new strategy. The value return should be the same as the
string translated in :py:meth:`~.BaseStrategy.get_display_name`.
- Implement its :py:meth:`~.BaseStrategy.execute` method to return the

View File

@@ -21,6 +21,22 @@ Goals
.. list-plugins:: watcher_goals
:detailed:
.. _watcher_scoring_engines:
Scoring Engines
===============
.. list-plugins:: watcher_scoring_engines
:detailed:
.. _watcher_scoring_engine_containers:
Scoring Engine Containers
=========================
.. list-plugins:: watcher_scoring_engine_containers
:detailed:
.. _watcher_strategies:
Strategies

View File

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

View File

@@ -96,8 +96,8 @@ The :ref:`Cluster <cluster_definition>` may be divided in one or several
.. _cluster_data_model_definition:
Cluster Data Model
==================
Cluster Data Model (CDM)
========================
.. watcher-term:: watcher.decision_engine.model.collector.base
@@ -132,7 +132,7 @@ Compute node
============
Please, read `the official OpenStack definition of a Compute Node
<http://docs.openstack.org/openstack-ops/content/compute_nodes.html>`_.
<http://docs.openstack.org/ops-guide/arch-compute-nodes.html>`_.
.. _customer_definition:
@@ -164,7 +164,8 @@ Goal
Host Aggregate
==============
Please, read `the official OpenStack definition of a Host Aggregate <http://docs.openstack.org/developer/nova/aggregates.html>`_.
Please, read `the official OpenStack definition of a Host Aggregate
<http://docs.openstack.org/developer/nova/aggregates.html>`_.
.. _instance_definition:
@@ -280,6 +281,12 @@ specific domain.
Please, read `the official OpenStack definition of a Project
<http://docs.openstack.org/glossary/content/glossary.html>`_.
.. _scoring_engine_definition:
Scoring Engine
==============
.. watcher-term:: watcher.api.controllers.v1.scoring_engine
.. _sla_definition:

View File

@@ -4,7 +4,7 @@ actor Administrator
== Create some Audit settings ==
Administrator -> Watcher : create new Audit Template (i.e. Audit settings : goal, scope, deadline,...)
Administrator -> Watcher : create new Audit Template (i.e. Audit settings : goal, scope, ...)
Watcher -> Watcher : save Audit Template in database
Administrator <-- Watcher : Audit Template UUID

View File

@@ -5,11 +5,12 @@
hide methods
hide stereotypes
table(goal) {
table(goals) {
primary_key(id: Integer)
uuid : String[36]
name : String[63]
display_name : String[63]
efficacy_specification : JSONEncodedList, nullable
created_at : DateTime
updated_at : DateTime
@@ -18,12 +19,13 @@ table(goal) {
}
table(strategy) {
table(strategies) {
primary_key(id: Integer)
foreign_key(goal_id : Integer)
uuid : String[36]
name : String[63]
display_name : String[63]
parameters_spec : JSONEncodedDict, nullable
created_at : DateTime
updated_at : DateTime
@@ -32,16 +34,14 @@ table(strategy) {
}
table(audit_template) {
table(audit_templates) {
primary_key(id: Integer)
foreign_key("goal_id : Integer")
foreign_key("strategy_id : Integer, nullable")
uuid : String[36]
name : String[63], nullable
description : String[255], nullable
host_aggregate : Integer, nullable
extra : JSONEncodedDict
version : String[15], nullable
scope : JSONEncodedList
created_at : DateTime
updated_at : DateTime
@@ -50,14 +50,16 @@ table(audit_template) {
}
table(audit) {
table(audits) {
primary_key(id: Integer)
foreign_key("audit_template_id : Integer")
foreign_key("goal_id : Integer")
foreign_key("strategy_id : Integer, nullable")
uuid : String[36]
audit_type : String[20]
state : String[20], nullable
deadline :DateTime, nullable
interval : Integer, nullable
parameters : JSONEncodedDict, nullable
scope : JSONEncodedList, nullable
created_at : DateTime
updated_at : DateTime
@@ -66,9 +68,10 @@ table(audit) {
}
table(action_plan) {
table(action_plans) {
primary_key(id: Integer)
foreign_key("audit_id : Integer, nullable")
foreign_key("strategy_id : Integer")
uuid : String[36]
first_action_id : Integer
state : String[20], nullable
@@ -81,7 +84,7 @@ table(action_plan) {
}
table(action) {
table(actions) {
primary_key(id: Integer)
foreign_key("action_plan_id : Integer")
uuid : String[36]
@@ -97,7 +100,7 @@ table(action) {
}
table(efficacy_indicator) {
table(efficacy_indicators) {
primary_key(id: Integer)
foreign_key("action_plan_id : Integer")
uuid : String[36]
@@ -112,12 +115,39 @@ table(efficacy_indicator) {
deleted : Integer
}
"goal" <.. "strategy" : Foreign Key
"goal" <.. "audit_template" : Foreign Key
"strategy" <.. "audit_template" : Foreign Key
"audit_template" <.. "audit" : Foreign Key
"action_plan" <.. "action" : Foreign Key
"action_plan" <.. "efficacy_indicator" : Foreign Key
"audit" <.. "action_plan" : Foreign Key
table(scoring_engines) {
primary_key(id: Integer)
uuid : String[36]
name : String[63]
description : String[255], nullable
metainfo : Text, nullable
created_at : DateTime
updated_at : DateTime
deleted_at : DateTime
deleted : Integer
}
table(service) {
primary_key(id: Integer)
name: String[255]
host: String[255]
last_seen_up: DateTime
created_at : DateTime
updated_at : DateTime
deleted_at : DateTime
deleted : Integer
}
"goals" <.. "strategies" : Foreign Key
"goals" <.. "audit_templates" : Foreign Key
"strategies" <.. "audit_templates" : Foreign Key
"goals" <.. "audits" : Foreign Key
"strategies" <.. "audits" : Foreign Key
"action_plans" <.. "actions" : Foreign Key
"action_plans" <.. "efficacy_indicators" : Foreign Key
"strategies" <.. "action_plans" : Foreign Key
"audits" <.. "action_plans" : Foreign Key
@enduml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -56,7 +56,9 @@ Getting Started
dev/devstack
deploy/configuration
deploy/conf-files
dev/notifications
dev/testing
dev/rally_link
API References
--------------
@@ -74,6 +76,7 @@ Plugins
dev/plugin/base-setup
dev/plugin/goal-plugin
dev/plugin/scoring-engine-plugin
dev/plugin/strategy-plugin
dev/plugin/cdmc-plugin
dev/plugin/action-plugin
@@ -94,6 +97,7 @@ Introduction
deploy/user-guide
deploy/policy
deploy/gmr
strategies/strategies
Watcher Manual Pages
====================

View File

@@ -0,0 +1,96 @@
==================================
Basic Offline Server Consolidation
==================================
Synopsis
--------
**display name**: ``basic``
**goal**: ``server_consolidation``
.. watcher-term:: watcher.decision_engine.strategy.strategies.basic_consolidation
Requirements
------------
Metrics
*******
The *basic* strategy requires the following metrics:
============================ ============ ======= =======
metric service name plugins comment
============================ ============ ======= =======
``compute.node.cpu.percent`` ceilometer_ none
``cpu_util`` ceilometer_ none
============================ ============ ======= =======
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#openstack-compute
Cluster data model
******************
Default Watcher's Compute cluster data model:
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
Actions
*******
Default Watcher's actions:
.. list-table::
:widths: 30 30
:header-rows: 1
* - action
- description
* - ``migration``
- .. watcher-term:: watcher.applier.actions.migration.Migrate
* - ``change_nova_service_state``
- .. watcher-term:: watcher.applier.actions.change_nova_service_state.ChangeNovaServiceState
Planner
*******
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
Configuration
-------------
Strategy parameter is:
====================== ====== ============= ===================================
parameter type default Value description
====================== ====== ============= ===================================
``migration_attempts`` Number 0 Maximum number of combinations to
be tried by the strategy while
searching for potential candidates.
To remove the limit, set it to 0
====================== ====== ============= ===================================
Efficacy Indicator
------------------
.. watcher-func::
:format: literal_block
watcher.decision_engine.goal.efficacy.specs.ServerConsolidation.get_global_efficacy_indicator
How to use it ?
---------------
.. code-block:: shell
$ openstack optimize audittemplate create \
at1 server_consolidation --strategy basic
$ openstack optimize audit create -a at1 -p migration_attempts=4
External Links
--------------
None.

View File

@@ -0,0 +1,101 @@
=================================
Outlet Temperature Based Strategy
=================================
Synopsis
--------
**display name**: ``outlet_temperature``
**goal**: ``thermal_optimization``
Outlet (Exhaust Air) temperature is a new thermal telemetry which can be
used to measure the host's thermal/workload status. This strategy makes
decisions to migrate workloads to the hosts with good thermal condition
(lowest outlet temperature) when the outlet temperature of source hosts
reach a configurable threshold.
Requirements
------------
This strategy has a dependency on the host having Intel's Power
Node Manager 3.0 or later enabled.
Metrics
*******
The *outlet_temperature* strategy requires the following metrics:
========================================= ============ ======= =======
metric service name plugins comment
========================================= ============ ======= =======
``hardware.ipmi.node.outlet_temperature`` ceilometer_ IPMI
========================================= ============ ======= =======
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#ipmi-based-meters
Cluster data model
******************
Default Watcher's Compute cluster data model:
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
Actions
*******
Default Watcher's actions:
.. list-table::
:widths: 30 30
:header-rows: 1
* - action
- description
* - ``migration``
- .. watcher-term:: watcher.applier.actions.migration.Migrate
Planner
*******
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
Configuration
-------------
Strategy parameter is:
============== ====== ============= ====================================
parameter type default Value description
============== ====== ============= ====================================
``threshold`` Number 35.0 Temperature threshold for migration
============== ====== ============= ====================================
Efficacy Indicator
------------------
None
Algorithm
---------
For more information on the Outlet Temperature Based Strategy please refer to:
https://specs.openstack.org/openstack/watcher-specs/specs/mitaka/implemented/outlet-temperature-based-strategy.html
How to use it ?
---------------
.. code-block:: shell
$ openstack optimize audittemplate create \
at1 thermal_optimization --strategy outlet_temperature
$ openstack optimize audit create -a at1 -p threshold=31.0
External Links
--------------
- `Intel Power Node Manager 3.0 <http://www.intel.com/content/www/us/en/power-management/intelligent-power-node-manager-3-0-specification.html>`_

View File

@@ -0,0 +1,8 @@
Strategies
==========
.. toctree::
:glob:
:maxdepth: 1
./*

View File

@@ -0,0 +1,115 @@
=============
Strategy name
=============
Synopsis
--------
**display name**:
**goal**:
Add here a complete description of your strategy
Requirements
------------
Metrics
*******
Write here the list of metrics required by your strategy algorithm (in the form
of a table). If these metrics requires specific Telemetry plugin or other
additional software, please explain here how to deploy them (and add link to
dedicated installation guide).
Example:
======================= ============ ======= =======
metric service name plugins comment
======================= ============ ======= =======
compute.node.* ceilometer_ none one point every 60s
vm.cpu.utilization_perc monasca_ none
power ceilometer_ kwapi_ one point every 60s
======================= ============ ======= =======
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#openstack-compute
.. _monasca: https://github.com/openstack/monasca-agent/blob/master/docs/Libvirt.md
.. _kwapi: https://kwapi.readthedocs.io/en/latest/index.html
Cluster data model
******************
Default Watcher's cluster data model.
or
If your strategy implementation requires a new cluster data model, please
describe it in this section, with a link to model plugin's installation guide.
Actions
*******
Default Watcher's actions.
or
If your strategy implementation requires new actions, add the list of Action
plugins here (in the form of a table) with a link to the plugin's installation
procedure.
======== =================
action description
======== =================
action1_ This action1 ...
action2_ This action2 ...
======== =================
.. _action1 : https://github.com/myrepo/watcher/plugins/action1
.. _action2 : https://github.com/myrepo/watcher/plugins/action2
Planner
*******
Default Watcher's planner.
or
If your strategy requires also a new planner to schedule built actions in time,
please describe it in this section, with a link to planner plugin's
installation guide.
Configuration
-------------
If your strategy use configurable parameters, explain here how to tune them.
Efficacy Indicator
------------------
Add here the Efficacy indicator computed by your strategy.
Algorithm
---------
Add here either the description of your algorithm or
link to the existing description.
How to use it ?
---------------
.. code-block:: shell
$ Write the command line to create an audit with your strategy.
External Links
--------------
If you have written papers, blog articles .... about your strategy into Watcher,
or if your strategy is based from external publication(s), please add HTTP
links and references in this section.
- `link1 <http://www.link1.papers.com>`_
- `link2 <http://www.link2.papers.com>`_

View File

@@ -0,0 +1,107 @@
==================================
Uniform Airflow Migration Strategy
==================================
Synopsis
--------
**display name**: ``uniform_airflow``
**goal**: ``airflow_optimization``
.. watcher-term:: watcher.decision_engine.strategy.strategies.uniform_airflow
Requirements
------------
This strategy has a dependency on the server having Intel's Power
Node Manager 3.0 or later enabled.
Metrics
*******
The *uniform_airflow* strategy requires the following metrics:
================================== ============ ======= =======
metric service name plugins comment
================================== ============ ======= =======
``hardware.ipmi.node.airflow`` ceilometer_ IPMI
``hardware.ipmi.node.temperature`` ceilometer_ IPMI
``hardware.ipmi.node.power`` ceilometer_ IPMI
================================== ============ ======= =======
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#ipmi-based-meters
Cluster data model
******************
Default Watcher's Compute cluster data model:
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
Actions
*******
Default Watcher's actions:
.. list-table::
:widths: 30 30
:header-rows: 1
* - action
- description
* - ``migration``
- .. watcher-term:: watcher.applier.actions.migration.Migrate
Planner
*******
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
Configuration
-------------
Strategy parameters are:
====================== ====== ============= ===========================
parameter type default Value description
====================== ====== ============= ===========================
``threshold_airflow`` Number 400.0 Airflow threshold for
migration Unit is 0.1CFM
``threshold_inlet_t`` Number 28.0 Inlet temperature threshold
for migration decision
``threshold_power`` Number 350.0 System power threshold for
migration decision
``period`` Number 300 Aggregate time period of
ceilometer
====================== ====== ============= ===========================
Efficacy Indicator
------------------
None
Algorithm
---------
For more information on the Uniform Airflow Migration Strategy please refer to:
https://specs.openstack.org/openstack/watcher-specs/specs/newton/implemented/uniform-airflow-migration-strategy.html
How to use it ?
---------------
.. code-block:: shell
$ openstack optimize audittemplate create \
at1 airflow_optimization --strategy uniform_airflow
$ openstack optimize audit create -a at1 -p threshold_airflow=410 \
-p threshold_inlet_t=29.0 -p threshold_power=355.0 -p period=310
External Links
--------------
- `Intel Power Node Manager 3.0 <http://www.intel.com/content/www/us/en/power-management/intelligent-power-node-manager-3-0-specification.html>`_

View File

@@ -0,0 +1,100 @@
==================================
VM Workload Consolidation Strategy
==================================
Synopsis
--------
**display name**: ``vm_workload_consolidation``
**goal**: ``vm_consolidation``
.. watcher-term:: watcher.decision_engine.strategy.strategies.vm_workload_consolidation
Requirements
------------
Metrics
*******
The *vm_workload_consolidation* strategy requires the following metrics:
============================ ============ ======= =======
metric service name plugins comment
============================ ============ ======= =======
``memory`` ceilometer_ none
``disk.root.size`` ceilometer_ none
============================ ============ ======= =======
The following metrics are not required but increase the accuracy of
the strategy if available:
============================ ============ ======= =======
metric service name plugins comment
============================ ============ ======= =======
``memory.usage`` ceilometer_ none
``cpu_util`` ceilometer_ none
============================ ============ ======= =======
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#openstack-compute
Cluster data model
******************
Default Watcher's Compute cluster data model:
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
Actions
*******
Default Watcher's actions:
.. list-table::
:widths: 30 30
:header-rows: 1
* - action
- description
* - ``migration``
- .. watcher-term:: watcher.applier.actions.migration.Migrate
* - ``change_nova_service_state``
- .. watcher-term:: watcher.applier.actions.change_nova_service_state.ChangeNovaServiceState
Planner
*******
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
Efficacy Indicator
------------------
.. watcher-func::
:format: literal_block
watcher.decision_engine.goal.efficacy.specs.ServerConsolidation.get_global_efficacy_indicator
Algorithm
---------
For more information on the VM Workload consolidation strategy please refer to: https://specs.openstack.org/openstack/watcher-specs/specs/mitaka/implemented/zhaw-load-consolidation.html
How to use it ?
---------------
.. code-block:: shell
$ openstack optimize audittemplate create \
at1 vm_consolidation --strategy vm_workload_consolidation
$ openstack optimize audit create -a at1
External Links
--------------
*Spec URL*
https://specs.openstack.org/openstack/watcher-specs/specs/mitaka/implemented/zhaw-load-consolidation.html

View File

@@ -0,0 +1,141 @@
=============================================
Watcher Overload standard deviation algorithm
=============================================
Synopsis
--------
**display name**: ``workload_stabilization``
**goal**: ``workload_balancing``
.. watcher-term:: watcher.decision_engine.strategy.strategies.workload_stabilization
Requirements
------------
Metrics
*******
The *workload_stabilization* strategy requires the following metrics:
============================ ============ ======= =======
metric service name plugins comment
============================ ============ ======= =======
``compute.node.cpu.percent`` ceilometer_ none
``hardware.memory.used`` ceilometer_ SNMP_
``cpu_util`` ceilometer_ none
``memory.resident`` ceilometer_ none
============================ ============ ======= =======
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#openstack-compute
.. _SNMP: http://docs.openstack.org/admin-guide/telemetry-measurements.html
Cluster data model
******************
Default Watcher's Compute cluster data model:
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
Actions
*******
Default Watcher's actions:
.. list-table::
:widths: 30 30
:header-rows: 1
* - action
- description
* - ``migration``
- .. watcher-term:: watcher.applier.actions.migration.Migrate
Planner
*******
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
Configuration
-------------
Strategy parameters are:
==================== ====== ===================== =============================
parameter type default Value description
==================== ====== ===================== =============================
``metrics`` array |metrics| Metrics used as rates of
cluster loads.
``thresholds`` object |thresholds| Dict where key is a metric
and value is a trigger value.
``weights`` object |weights| These weights used to
calculate common standard
deviation. Name of weight
contains meter name and
_weight suffix.
``instance_metrics`` object |instance_metrics| Mapping to get hardware
statistics using instance
metrics.
``host_choice`` string retry Method of host's choice.
There are cycle, retry and
fullsearch methods. Cycle
will iterate hosts in cycle.
Retry will get some hosts
random (count defined in
retry_count option).
Fullsearch will return each
host from list.
``retry_count`` number 1 Count of random returned
hosts.
``periods`` object |periods| These periods are used to get
statistic aggregation for
instance and host metrics.
The period is simply a
repeating interval of time
into which the samples are
grouped for aggregation.
Watcher uses only the last
period of all recieved ones.
==================== ====== ===================== =============================
.. |metrics| replace:: ["cpu_util", "memory.resident"]
.. |thresholds| replace:: {"cpu_util": 0.2, "memory.resident": 0.2}
.. |weights| replace:: {"cpu_util_weight": 1.0, "memory.resident_weight": 1.0}
.. |instance_metrics| replace:: {"cpu_util": "compute.node.cpu.percent", "memory.resident": "hardware.memory.used"}
.. |periods| replace:: {"instance": 720, "node": 600}
Efficacy Indicator
------------------
.. watcher-func::
:format: literal_block
watcher.decision_engine.goal.efficacy.specs.ServerConsolidation.get_global_efficacy_indicator
Algorithm
---------
You can find description of overload algorithm and role of standard deviation
here: https://specs.openstack.org/openstack/watcher-specs/specs/newton/implemented/sd-strategy.html
How to use it ?
---------------
.. code-block:: shell
$ openstack optimize audittemplate create \
at1 workload_balancing --strategy workload_stabilization
$ openstack optimize audit create -a at1 \
-p thresholds='{"memory.resident": 0.05}' \
-p metrics='["memory.resident"]'
External Links
--------------
- `Watcher Overload standard deviation algorithm spec <https://specs.openstack.org/openstack/watcher-specs/specs/newton/implemented/sd-strategy.html>`_

View File

@@ -0,0 +1,98 @@
===================================
Workload Balance Migration Strategy
===================================
Synopsis
--------
**display name**: ``workload_balance``
**goal**: ``workload_balancing``
.. watcher-term:: watcher.decision_engine.strategy.strategies.workload_balance
Requirements
------------
None.
Metrics
*******
The *workload_balance* strategy requires the following metrics:
======================= ============ ======= =======
metric service name plugins comment
======================= ============ ======= =======
``cpu_util`` ceilometer_ none
======================= ============ ======= =======
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#openstack-compute
Cluster data model
******************
Default Watcher's Compute cluster data model:
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
Actions
*******
Default Watcher's actions:
.. list-table::
:widths: 30 30
:header-rows: 1
* - action
- description
* - ``migration``
- .. watcher-term:: watcher.applier.actions.migration.Migrate
Planner
*******
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
Configuration
-------------
Strategy parameters are:
============== ====== ============= ====================================
parameter type default Value description
============== ====== ============= ====================================
``threshold`` Number 25.0 Workload threshold for migration
``period`` Number 300 Aggregate time period of ceilometer
============== ====== ============= ====================================
Efficacy Indicator
------------------
None
Algorithm
---------
For more information on the Workload Balance Migration Strategy please refer
to: https://specs.openstack.org/openstack/watcher-specs/specs/mitaka/implemented/workload-balance-migration-strategy.html
How to use it ?
---------------
.. code-block:: shell
$ openstack optimize audittemplate create \
at1 workload_balancing --strategy workload_balance
$ openstack optimize audit create -a at1 -p threshold=26.0 \
-p period=310
External Links
--------------
None.

View File

@@ -37,5 +37,9 @@
"strategy:detail": "rule:default",
"strategy:get": "rule:default",
"strategy:get_all": "rule:default"
"strategy:get_all": "rule:default",
"service:detail": "rule:default",
"service:get": "rule:default",
"service:get_all": "rule:default"
}

42
rally-jobs/README.rst Normal file
View File

@@ -0,0 +1,42 @@
Rally job
=========
We provide, with Watcher, a Rally plugin you can use to benchmark the optimization service.
To launch this task with configured Rally you just need to run:
::
rally task start watcher/rally-jobs/watcher-watcher.yaml
Structure
---------
* plugins - directory where you can add rally plugins. Almost everything in
Rally is a plugin. Benchmark context, Benchmark scenario, SLA checks, Generic
cleanup resources, ....
* extra - all files from this directory will be copy pasted to gates, so you
are able to use absolute paths in rally tasks.
Files will be located in ~/.rally/extra/*
* watcher.yaml is a task that is run in gates against OpenStack
deployed by DevStack
Useful links
------------
* How to install: http://docs.openstack.org/developer/rally/install.html
* How to set Rally up and launch your first scenario: https://rally.readthedocs.io/en/latest/tutorial/step_1_setting_up_env_and_running_benchmark_from_samples.html
* More about Rally: https://rally.readthedocs.org/en/latest/
* Rally release notes: https://rally.readthedocs.org/en/latest/release_notes.html
* How to add rally-gates: https://rally.readthedocs.org/en/latest/gates.html
* About plugins: https://rally.readthedocs.org/en/latest/plugins.html
* Plugin samples: https://github.com/openstack/rally/tree/master/samples/plugins

View File

@@ -0,0 +1,63 @@
---
Watcher.create_audit_and_delete:
-
runner:
type: "constant"
times: 10
concurrency: 2
context:
users:
tenants: 2
users_per_tenant: 2
audit_templates:
audit_templates_per_admin: 5
fill_strategy: "round_robin"
params:
- goal:
name: "dummy"
strategy:
name: "dummy"
sla:
failure_rate:
max: 0
Watcher.create_audit_template_and_delete:
-
args:
goal:
name: "dummy"
strategy:
name: "dummy"
runner:
type: "constant"
times: 10
concurrency: 2
sla:
failure_rate:
max: 0
Watcher.list_audit_templates:
-
runner:
type: "constant"
times: 10
concurrency: 2
context:
users:
tenants: 2
users_per_tenant: 2
audit_templates:
audit_templates_per_admin: 5
fill_strategy: "random"
params:
- goal:
name: "workload_balancing"
strategy:
name: "workload_stabilization"
- goal:
name: "dummy"
strategy:
name: "dummy"
sla:
failure_rate:
max: 0

View File

@@ -1,7 +1,7 @@
---
features:
- Added a generic scoring engine module, which
will standarize interactions with scoring engines
will standardize interactions with scoring engines
through the common API. It is possible to use the
scoring engine by different Strategies, which
improve the code and data model re-use.

View File

@@ -2,4 +2,4 @@
features:
- Added a way to return the of available goals depending
on which strategies have been deployed on the node
where the decison engine is running.
where the decision engine is running.

View File

@@ -242,3 +242,6 @@ texinfo_documents = [
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# -- Options for Internationalization output ------------------------------
locale_dirs = ['locale/']

View File

@@ -6,5 +6,6 @@ Contents:
.. toctree::
:maxdepth: 1
unreleased.rst
unreleased
newton

View File

@@ -0,0 +1,6 @@
===================================
Newton Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/newton

View File

@@ -5,36 +5,38 @@
apscheduler # MIT License
enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
jsonpatch>=1.1 # BSD
keystoneauth1>=2.10.0 # Apache-2.0
keystonemiddleware!=4.1.0,!=4.5.0,>=4.0.0 # Apache-2.0
keystoneauth1>=2.14.0 # Apache-2.0
keystonemiddleware!=4.5.0,>=4.2.0 # Apache-2.0
lxml>=2.3 # BSD
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.cache>=1.5.0 # Apache-2.0
oslo.config>=3.14.0 # Apache-2.0
oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
oslo.context>=2.9.0 # Apache-2.0
oslo.db>=4.10.0 # Apache-2.0
oslo.db!=4.13.1,!=4.13.2,>=4.11.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0
oslo.messaging>=5.2.0 # Apache-2.0
oslo.policy>=1.9.0 # Apache-2.0
oslo.log>=3.11.0 # Apache-2.0
oslo.messaging>=5.14.0 # Apache-2.0
oslo.policy>=1.17.0 # Apache-2.0
oslo.reports>=0.6.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.service>=1.10.0 # Apache-2.0
oslo.utils>=3.16.0 # Apache-2.0
oslo.utils>=3.18.0 # Apache-2.0
oslo.versionedobjects>=1.17.0 # Apache-2.0
PasteDeploy>=1.5.0 # MIT
pbr>=1.6 # Apache-2.0
pecan!=1.0.2,!=1.0.3,!=1.0.4,>=1.0.0 # BSD
PrettyTable<0.8,>=0.7 # BSD
pbr>=1.8 # Apache-2.0
pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD
PrettyTable<0.8,>=0.7.1 # BSD
voluptuous>=0.8.9 # BSD License
python-ceilometerclient>=2.5.0 # Apache-2.0
python-cinderclient!=1.7.0,!=1.7.1,>=1.6.0 # Apache-2.0
python-glanceclient!=2.4.0,>=2.3.0 # Apache-2.0
python-keystoneclient!=2.1.0,>=2.0.0 # Apache-2.0
python-glanceclient>=2.5.0 # Apache-2.0
python-keystoneclient>=3.8.0 # Apache-2.0
python-neutronclient>=5.1.0 # Apache-2.0
python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
python-openstackclient>=2.1.0 # Apache-2.0
python-openstackclient>=3.3.0 # Apache-2.0
six>=1.9.0 # MIT
SQLAlchemy<1.1.0,>=1.0.10 # MIT
stevedore>=1.16.0 # Apache-2.0
stevedore>=1.17.1 # Apache-2.0
taskflow>=1.26.0 # Apache-2.0
WebOb>=1.2.3 # MIT
WebOb>=1.6.0 # MIT
WSME>=0.8 # MIT

View File

@@ -1,6 +1,6 @@
[metadata]
name = python-watcher
summary = Watcher takes advantage of CEP and ML algorithms/metaheuristics to improve physical resources usage through better VM placement. Watcher can improve your cloud optimization by reducing energy footprint and increasing profits.
summary = OpenStack Watcher provides a flexible and scalable resource optimization service for multi-tenant OpenStack-based clouds.
description-file =
README.rst
author = OpenStack
@@ -32,7 +32,7 @@ setup-hooks =
[entry_points]
oslo.config.opts =
watcher = watcher.opts:list_opts
watcher = watcher.conf.opts:list_opts
console_scripts =
watcher-api = watcher.cmd.api:main

View File

@@ -2,25 +2,25 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
coverage>=3.6 # Apache-2.0
coverage>=4.0 # Apache-2.0
doc8 # Apache-2.0
freezegun # Apache-2.0
freezegun>=0.3.6 # Apache-2.0
hacking<0.11,>=0.10.2
mock>=2.0 # BSD
oslotest>=1.10.0 # Apache-2.0
os-testr>=0.7.0 # Apache-2.0
os-testr>=0.8.0 # 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
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
oslosphinx>=4.7.0 # Apache-2.0
sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
sphinxcontrib-pecanwsme>=0.8 # Apache-2.0
# releasenotes
reno>=1.8.0 # Apache2
reno>=1.8.0 # Apache-2.0
# bandit
bandit>=1.1.0 # Apache-2.0

16
tox.ini
View File

@@ -1,26 +1,26 @@
[tox]
minversion = 1.6
minversion = 1.8
envlist = py35,py34,py27,pep8
skipsdist = True
[testenv]
usedevelop = True
whitelist_externals = find
install_command = pip install -U {opts} {packages}
install_command =
pip install -U --force-reinstall -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
deps = -r{toxinidir}/test-requirements.txt
commands =
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
find . -type f -name "*.py[c|o]" -delete
ostestr --concurrency=6 {posargs}
[testenv:pep8]
commands =
doc8 doc/source/ CONTRIBUTING.rst HACKING.rst README.rst
flake8
bandit -r watcher -x tests -n5 -ll
bandit -r watcher -x tests -n5 -ll -s B320
[testenv:venv]
setenv = PYTHONHASHSEED=0
@@ -66,4 +66,4 @@ commands = sphinx-build -a -W -E -d releasenotes/build/doctrees -b html releasen
[testenv:bandit]
deps = -r{toxinidir}/test-requirements.txt
commands = bandit -r watcher -x tests -n5 -ll
commands = bandit -r watcher -x tests -n5 -ll -s B320

View File

@@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2012 New Dream Network, LLC (DreamHost)
# Copyright (c) 2016 Intel Corp
#
# 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
@@ -17,32 +18,23 @@
"""Access Control Lists (ACL's) control access the API server."""
from oslo_config import cfg
from watcher.api.middleware import auth_token
from watcher import conf
AUTH_OPTS = [
cfg.BoolOpt('enable_authentication',
default=True,
help='This option enables or disables user authentication '
'via keystone. Default value is True.'),
]
CONF = cfg.CONF
CONF.register_opts(AUTH_OPTS)
CONF = conf.CONF
def install(app, conf, public_routes):
"""Install ACL check on application.
:param app: A WSGI applicatin.
:param app: A WSGI application.
:param conf: Settings. Dict'ified and passed to keystonemiddleware
:param public_routes: The list of the routes which will be allowed to
access without authentication.
:return: The same WSGI application with ACL installed.
"""
if not cfg.CONF.get('enable_authentication'):
if not CONF.get('enable_authentication'):
return app
return auth_token.AuthTokenMiddleware(app,
conf=dict(conf),

View File

@@ -2,6 +2,7 @@
# Copyright © 2012 New Dream Network, LLC (DreamHost)
# All Rights Reserved.
# Copyright (c) 2016 Intel Corp
#
# 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
@@ -16,49 +17,14 @@
# under the License.
from oslo_config import cfg
import pecan
from watcher._i18n import _
from watcher.api import acl
from watcher.api import config as api_config
from watcher.api import middleware
from watcher import conf
# Register options for the service
API_SERVICE_OPTS = [
cfg.PortOpt('port',
default=9322,
help=_('The port for the watcher API server')),
cfg.StrOpt('host',
default='127.0.0.1',
help=_('The listen IP for the watcher API server')),
cfg.IntOpt('max_limit',
default=1000,
help=_('The maximum number of items returned in a single '
'response from a collection resource')),
cfg.IntOpt('workers',
min=1,
help=_('Number of workers for Watcher API service. '
'The default is equal to the number of CPUs available '
'if that can be determined, else a default worker '
'count of 1 is returned.')),
cfg.BoolOpt('enable_ssl_api',
default=False,
help=_("Enable the integrated stand-alone API to service "
"requests via HTTPS instead of HTTP. If there is a "
"front-end service performing HTTPS offloading from "
"the service, this option should be False; note, you "
"will want to change public API endpoint to represent "
"SSL termination URL with 'public_endpoint' option.")),
]
CONF = cfg.CONF
opt_group = cfg.OptGroup(name='api',
title='Options for the watcher-api service')
CONF.register_group(opt_group)
CONF.register_opts(API_SERVICE_OPTS, opt_group)
CONF = conf.CONF
def get_pecan_config():

View File

@@ -61,7 +61,7 @@ class Root(base.APIBase):
root = Root()
root.name = "OpenStack Watcher API"
root.description = ("Watcher is an OpenStack project which aims to "
"to improve physical resources usage through "
"improve physical resources usage through "
"better VM placement.")
root.versions = [Version.convert('v1')]
root.default_version = Version.convert('v1')

View File

@@ -35,6 +35,7 @@ from watcher.api.controllers.v1 import audit
from watcher.api.controllers.v1 import audit_template
from watcher.api.controllers.v1 import goal
from watcher.api.controllers.v1 import scoring_engine
from watcher.api.controllers.v1 import service
from watcher.api.controllers.v1 import strategy
@@ -105,6 +106,9 @@ class V1(APIBase):
scoring_engines = [link.Link]
"""Links to the Scoring Engines resource"""
services = [link.Link]
"""Links to the services resource"""
links = [link.Link]
"""Links that point to a specific URL for this version and documentation"""
@@ -159,6 +163,14 @@ class V1(APIBase):
'scoring_engines', '',
bookmark=True)
]
v1.services = [link.Link.make_link(
'self', pecan.request.host_url, 'services', ''),
link.Link.make_link('bookmark',
pecan.request.host_url,
'services', '',
bookmark=True)
]
return v1
@@ -171,6 +183,7 @@ class Controller(rest.RestController):
action_plans = action_plan.ActionPlansController()
goals = goal.GoalsController()
scoring_engines = scoring_engine.ScoringEngineController()
services = service.ServicesController()
strategies = strategy.StrategiesController()
@wsme_pecan.wsexpose(V1)

View File

@@ -41,7 +41,7 @@ be one of the following:
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
- **FAILED** : an error occurred 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
@@ -379,7 +379,7 @@ class ActionsController(rest.RestController):
action_dict = action.as_dict()
context = pecan.request.context
new_action = objects.Action(context, **action_dict)
new_action.create(context)
new_action.create()
# Set the HTTP Location Header
pecan.response.location = link.build_url('actions', new_action.uuid)

View File

@@ -496,7 +496,6 @@ class ActionPlansController(rest.RestController):
: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:
raise exception.OperationNotPermitted

View File

@@ -60,8 +60,6 @@ class AuditPostType(wtypes.Base):
audit_type = wtypes.wsattr(wtypes.text, mandatory=True)
deadline = wtypes.wsattr(datetime.datetime, mandatory=False)
state = wsme.wsattr(wtypes.text, readonly=True,
default=objects.audit.State.PENDING)
@@ -69,8 +67,7 @@ class AuditPostType(wtypes.Base):
default={})
interval = wsme.wsattr(int, mandatory=False)
host_aggregate = wsme.wsattr(wtypes.IntegerType(minimum=1),
mandatory=False)
scope = wtypes.wsattr(types.jsontype, readonly=True)
def as_audit(self, context):
audit_type_values = [val.value for val in objects.audit.AuditType]
@@ -82,7 +79,7 @@ class AuditPostType(wtypes.Base):
raise exception.AuditIntervalNotAllowed(audit_type=self.audit_type)
if (self.audit_type == objects.audit.AuditType.CONTINUOUS.value and
self.interval in (wtypes.Unset, None)):
self.interval in (wtypes.Unset, None)):
raise exception.AuditIntervalNotSpecified(
audit_type=self.audit_type)
@@ -100,7 +97,7 @@ class AuditPostType(wtypes.Base):
at2a = {
'goal': 'goal_id',
'strategy': 'strategy_id',
'host_aggregate': 'host_aggregate'
'scope': 'scope',
}
to_string_fields = set(['goal', 'strategy'])
for k in at2a:
@@ -114,12 +111,11 @@ class AuditPostType(wtypes.Base):
pass
return Audit(
audit_type=self.audit_type,
deadline=self.deadline,
parameters=self.parameters,
goal_id=self.goal,
host_aggregate=self.host_aggregate,
strategy_id=self.strategy,
interval=self.interval)
interval=self.interval,
scope=self.scope,)
class AuditPatchType(types.JsonPatchType):
@@ -230,9 +226,6 @@ class Audit(base.APIBase):
audit_type = wtypes.text
"""Type of this audit"""
deadline = datetime.datetime
"""deadline of the audit"""
state = wtypes.text
"""This audit state"""
@@ -261,8 +254,8 @@ class Audit(base.APIBase):
interval = wsme.wsattr(int, mandatory=False)
"""Launch audit periodically (in seconds)"""
host_aggregate = wtypes.IntegerType(minimum=1)
"""ID of the Nova host aggregate targeted by the audit template"""
scope = wsme.wsattr(types.jsontype, mandatory=False)
"""Audit Scope"""
def __init__(self, **kwargs):
self.fields = []
@@ -292,10 +285,10 @@ class Audit(base.APIBase):
@staticmethod
def _convert_with_links(audit, url, expand=True):
if not expand:
audit.unset_fields_except(['uuid', 'audit_type', 'deadline',
'state', 'goal_uuid', 'interval',
'strategy_uuid', 'host_aggregate',
'goal_name', 'strategy_name'])
audit.unset_fields_except(['uuid', 'audit_type', 'state',
'goal_uuid', 'interval', 'scope',
'strategy_uuid', 'goal_name',
'strategy_name'])
audit.links = [link.Link.make_link('self', url,
'audits', audit.uuid),
@@ -316,15 +309,15 @@ class Audit(base.APIBase):
sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
audit_type='ONESHOT',
state='PENDING',
deadline=None,
created_at=datetime.datetime.utcnow(),
deleted_at=None,
updated_at=datetime.datetime.utcnow(),
interval=7200)
interval=7200,
scope=[])
sample.goal_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
sample.strategy_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ff'
sample.host_aggregate = 1
return cls._convert_with_links(sample, 'http://localhost:9322', expand)
@@ -381,7 +374,7 @@ class AuditsController(rest.RestController):
def _get_audits_collection(self, marker, limit,
sort_key, sort_dir, expand=False,
resource_url=None, goal=None,
strategy=None, host_aggregate=None):
strategy=None):
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
marker_obj = None
@@ -424,19 +417,16 @@ class AuditsController(rest.RestController):
@wsme_pecan.wsexpose(AuditCollection, types.uuid, int, wtypes.text,
wtypes.text, wtypes.text, wtypes.text, int)
def get_all(self, marker=None, limit=None,
sort_key='id', sort_dir='asc', goal=None,
strategy=None, host_aggregate=None):
def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc',
goal=None, strategy=None):
"""Retrieve a list of audits.
: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.
id.
:param goal: goal UUID or name to filter by
:param strategy: strategy UUID or name to filter by
:param host_aggregate: Optional host_aggregate
"""
context = pecan.request.context
@@ -445,8 +435,7 @@ class AuditsController(rest.RestController):
return self._get_audits_collection(marker, limit, sort_key,
sort_dir, goal=goal,
strategy=strategy,
host_aggregate=host_aggregate)
strategy=strategy)
@wsme_pecan.wsexpose(AuditCollection, wtypes.text, types.uuid, int,
wtypes.text, wtypes.text)
@@ -530,13 +519,12 @@ class AuditsController(rest.RestController):
audit_dict = audit.as_dict()
new_audit = objects.Audit(context, **audit_dict)
new_audit.create(context)
new_audit.create()
# Set the HTTP Location Header
pecan.response.location = link.build_url('audits', new_audit.uuid)
# trigger decision-engine to run the audit
if new_audit.audit_type == objects.audit.AuditType.ONESHOT.value:
dc_client = rpcapi.DecisionEngineAPI()
dc_client.trigger_audit(context, new_audit.uuid)
@@ -555,14 +543,11 @@ class AuditsController(rest.RestController):
raise exception.OperationNotPermitted
context = pecan.request.context
audit_to_update = api_utils.get_resource('Audit',
audit_uuid)
audit_to_update = api_utils.get_resource(
'Audit', audit_uuid, eager=True)
policy.enforce(context, 'audit:update', audit_to_update,
action='audit:update')
audit_to_update = objects.Audit.get_by_uuid(pecan.request.context,
audit_uuid)
try:
audit_dict = audit_to_update.as_dict()
audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch))
@@ -591,7 +576,8 @@ class AuditsController(rest.RestController):
:param audit_uuid: UUID of a audit.
"""
context = pecan.request.context
audit_to_delete = api_utils.get_resource('Audit', audit_uuid)
audit_to_delete = api_utils.get_resource(
'Audit', audit_uuid, eager=True)
policy.enforce(context, 'audit:update', audit_to_delete,
action='audit:update')

View File

@@ -41,11 +41,6 @@ settings related to the level of automation for the
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
@@ -66,6 +61,7 @@ from watcher.common import context as context_utils
from watcher.common import exception
from watcher.common import policy
from watcher.common import utils as common_utils
from watcher.decision_engine.scope import default
from watcher import objects
@@ -78,37 +74,24 @@ class AuditTemplatePostType(wtypes.Base):
description = wtypes.wsattr(wtypes.text, mandatory=False)
"""Short description of this audit template"""
deadline = wsme.wsattr(datetime.datetime, mandatory=False)
"""deadline of the audit template"""
host_aggregate = wsme.wsattr(wtypes.IntegerType(minimum=1),
mandatory=False)
"""ID of the Nova host aggregate targeted by the audit template"""
extra = wtypes.wsattr({wtypes.text: types.jsontype}, mandatory=False)
"""The metadata of the audit template"""
goal = wtypes.wsattr(wtypes.text, mandatory=True)
"""Goal UUID or name of the audit template"""
strategy = wtypes.wsattr(wtypes.text, mandatory=False)
"""Strategy UUID or name of the audit template"""
version = wtypes.text
"""Internal version of the audit template"""
scope = wtypes.wsattr(types.jsontype, mandatory=False, default=[])
"""Audit Scope"""
def as_audit_template(self):
return AuditTemplate(
name=self.name,
description=self.description,
deadline=self.deadline,
host_aggregate=self.host_aggregate,
extra=self.extra,
goal_id=self.goal, # Dirty trick ...
goal=self.goal,
strategy_id=self.strategy, # Dirty trick ...
strategy_uuid=self.strategy,
version=self.version,
scope=self.scope,
)
@staticmethod
@@ -123,6 +106,9 @@ class AuditTemplatePostType(wtypes.Base):
else:
raise exception.InvalidGoal(goal=audit_template.goal)
common_utils.Draft4Validator(
default.DefaultScope.DEFAULT_SCHEMA).validate(audit_template.scope)
if audit_template.strategy:
available_strategies = objects.Strategy.list(
AuditTemplatePostType._ctx)
@@ -305,18 +291,9 @@ class AuditTemplate(base.APIBase):
name = wtypes.text
"""Name of this audit template"""
description = wtypes.text
description = wtypes.wsattr(wtypes.text, mandatory=False)
"""Short description of this audit template"""
deadline = datetime.datetime
"""deadline of the audit template"""
host_aggregate = wtypes.IntegerType(minimum=1)
"""ID of the Nova host aggregate targeted by the audit template"""
extra = {wtypes.text: types.jsontype}
"""The metadata of the audit template"""
goal_uuid = wsme.wsproperty(
wtypes.text, _get_goal_uuid, _set_goal_uuid, mandatory=True)
"""Goal UUID the audit template refers to"""
@@ -333,15 +310,15 @@ class AuditTemplate(base.APIBase):
wtypes.text, _get_strategy_name, _set_strategy_name, mandatory=False)
"""The name of the strategy this audit template refers to"""
version = wtypes.text
"""Internal version of the audit template"""
audits = wsme.wsattr([link.Link], readonly=True)
"""Links to the collection of audits contained in this audit template"""
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link and associated audit template links"""
scope = wsme.wsattr(types.jsontype, mandatory=False)
"""Audit Scope"""
def __init__(self, **kwargs):
super(AuditTemplate, self).__init__()
self.fields = []
@@ -374,8 +351,8 @@ class AuditTemplate(base.APIBase):
def _convert_with_links(audit_template, url, expand=True):
if not expand:
audit_template.unset_fields_except(
['uuid', 'name', 'host_aggregate', 'goal_uuid', 'goal_name',
'strategy_uuid', 'strategy_name'])
['uuid', 'name', 'goal_uuid', 'goal_name',
'scope', 'strategy_uuid', 'strategy_name'])
# The numeric ID should not be exposed to
# the user, it's internal only.
@@ -402,13 +379,12 @@ class AuditTemplate(base.APIBase):
sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
name='My Audit Template',
description='Description of my audit template',
host_aggregate=5,
goal_uuid='83e44733-b640-40e2-8d8a-7dd3be7134e6',
strategy_uuid='367d826e-b6a4-4b70-bc44-c3f6fe1c9986',
extra={'automatic': True},
created_at=datetime.datetime.utcnow(),
deleted_at=None,
updated_at=datetime.datetime.utcnow())
updated_at=datetime.datetime.utcnow(),
scope=[],)
return cls._convert_with_links(sample, 'http://localhost:9322', expand)
@@ -592,11 +568,11 @@ class AuditTemplatesController(rest.RestController):
audit_template_dict = audit_template.as_dict()
new_audit_template = objects.AuditTemplate(context,
**audit_template_dict)
new_audit_template.create(context)
new_audit_template.create()
# Set the HTTP Location Header
pecan.response.location = link.build_url('audit_templates',
new_audit_template.uuid)
pecan.response.location = link.build_url(
'audit_templates', new_audit_template.uuid)
return AuditTemplate.convert_with_links(new_audit_template)
@wsme.validate(types.uuid, [AuditTemplatePatchType])

View File

@@ -19,7 +19,7 @@ An efficacy indicator is a single value that gives an indication on how the
:ref:`solution <solution_definition>` produced by a given :ref:`strategy
<strategy_definition>` performed. These efficacy indicators are specific to a
given :ref:`goal <goal_definition>` and are usually used to compute the
:ref:`gobal efficacy <efficacy_definition>` of the resulting :ref:`action plan
:ref:`global efficacy <efficacy_definition>` of the resulting :ref:`action plan
<action_plan_definition>`.
In Watcher, these efficacy indicators are specified alongside the goal they

View File

@@ -16,8 +16,10 @@
# limitations under the License.
"""
A :ref:`Scoring Engine <scoring_engine_definition>` is an instance of a data
model, to which a learning data was applied.
A :ref:`Scoring Engine <scoring_engine_definition>` is an executable that has
a well-defined input, a well-defined output, and performs a purely mathematical
task. That is, the calculation does not depend on the environment in which it
is running - it would produce the same result anywhere.
Because there might be multiple algorithms used to build a particular data
model (and therefore a scoring engine), the usage of scoring engine might

View File

@@ -0,0 +1,263 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 Servionica
#
# 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.
"""
Service mechanism provides ability to monitor Watcher services state.
"""
import datetime
import six
from oslo_config import cfg
from oslo_log import log
from oslo_utils import timeutils
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from watcher._i18n import _LW
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 utils as api_utils
from watcher.common import exception
from watcher.common import policy
from watcher import objects
CONF = cfg.CONF
LOG = log.getLogger(__name__)
class Service(base.APIBase):
"""API representation of a service.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of a service.
"""
_status = None
def _get_status(self):
return self._status
def _set_status(self, name):
service = objects.Service.get_by_name(pecan.request.context, name)
last_heartbeat = (service.last_seen_up or service.updated_at
or service.created_at)
if isinstance(last_heartbeat, six.string_types):
# NOTE(russellb) If this service came in over rpc via
# conductor, then the timestamp will be a string and needs to be
# converted back to a datetime.
last_heartbeat = timeutils.parse_strtime(last_heartbeat)
else:
# Objects have proper UTC timezones, but the timeutils comparison
# below does not (and will fail)
last_heartbeat = last_heartbeat.replace(tzinfo=None)
elapsed = timeutils.delta_seconds(last_heartbeat, timeutils.utcnow())
is_up = abs(elapsed) <= CONF.service_down_time
if not is_up:
LOG.warning(_LW('Seems service %(name)s on host %(host)s is down. '
'Last heartbeat was %(lhb)s.'
'Elapsed time is %(el)s'),
{'name': service.name,
'host': service.host,
'lhb': str(last_heartbeat), 'el': str(elapsed)})
self._status = objects.service.ServiceStatus.FAILED
else:
self._status = objects.service.ServiceStatus.ACTIVE
id = wsme.wsattr(int, readonly=True)
"""ID for this service."""
name = wtypes.text
"""Name of the service."""
host = wtypes.text
"""Host where service is placed on."""
last_seen_up = wsme.wsattr(datetime.datetime, readonly=True)
"""Time when Watcher service sent latest heartbeat."""
status = wsme.wsproperty(wtypes.text, _get_status, _set_status,
mandatory=True)
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link."""
def __init__(self, **kwargs):
super(Service, self).__init__()
fields = list(objects.Service.fields.keys()) + ['status']
self.fields = []
for field in fields:
self.fields.append(field)
setattr(self, field, kwargs.get(
field if field != 'status' else 'name', wtypes.Unset))
@staticmethod
def _convert_with_links(service, url, expand=True):
if not expand:
service.unset_fields_except(
['id', 'name', 'host', 'status'])
service.links = [
link.Link.make_link('self', url, 'services', str(service.id)),
link.Link.make_link('bookmark', url, 'services', str(service.id),
bookmark=True)]
return service
@classmethod
def convert_with_links(cls, service, expand=True):
service = Service(**service.as_dict())
return cls._convert_with_links(
service, pecan.request.host_url, expand)
@classmethod
def sample(cls, expand=True):
sample = cls(id=1,
name='watcher-applier',
host='Controller',
last_seen_up=datetime.datetime(2016, 1, 1))
return cls._convert_with_links(sample, 'http://localhost:9322', expand)
class ServiceCollection(collection.Collection):
"""API representation of a collection of services."""
services = [Service]
"""A list containing services objects"""
def __init__(self, **kwargs):
super(ServiceCollection, self).__init__()
self._type = 'services'
@staticmethod
def convert_with_links(services, limit, url=None, expand=False,
**kwargs):
service_collection = ServiceCollection()
service_collection.services = [
Service.convert_with_links(g, expand) for g in services]
if 'sort_key' in kwargs:
reverse = False
if kwargs['sort_key'] == 'service':
if 'sort_dir' in kwargs:
reverse = True if kwargs['sort_dir'] == 'desc' else False
service_collection.services = sorted(
service_collection.services,
key=lambda service: service.id,
reverse=reverse)
service_collection.next = service_collection.get_next(
limit, url=url, marker_field='id', **kwargs)
return service_collection
@classmethod
def sample(cls):
sample = cls()
sample.services = [Service.sample(expand=False)]
return sample
class ServicesController(rest.RestController):
"""REST controller for Services."""
def __init__(self):
super(ServicesController, self).__init__()
from_services = False
"""A flag to indicate if the requests to this controller are coming
from the top-level resource Services."""
_custom_actions = {
'detail': ['GET'],
}
def _get_services_collection(self, marker, limit, sort_key, sort_dir,
expand=False, resource_url=None):
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
sort_db_key = (sort_key if sort_key in objects.Service.fields.keys()
else None)
marker_obj = None
if marker:
marker_obj = objects.Service.get(
pecan.request.context, marker)
services = objects.Service.list(
pecan.request.context, limit, marker_obj,
sort_key=sort_db_key, sort_dir=sort_dir)
return ServiceCollection.convert_with_links(
services, limit, url=resource_url, expand=expand,
sort_key=sort_key, sort_dir=sort_dir)
@wsme_pecan.wsexpose(ServiceCollection, int, int, wtypes.text, wtypes.text)
def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc'):
"""Retrieve a list of services.
: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.
"""
context = pecan.request.context
policy.enforce(context, 'service:get_all',
action='service:get_all')
return self._get_services_collection(marker, limit, sort_key, sort_dir)
@wsme_pecan.wsexpose(ServiceCollection, int, int, wtypes.text, wtypes.text)
def detail(self, marker=None, limit=None, sort_key='id', sort_dir='asc'):
"""Retrieve a list of services with detail.
: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.
"""
context = pecan.request.context
policy.enforce(context, 'service:detail',
action='service:detail')
# NOTE(lucasagomes): /detail should only work agaist collections
parent = pecan.request.path.split('/')[:-1][-1]
if parent != "services":
raise exception.HTTPNotFound
expand = True
resource_url = '/'.join(['services', 'detail'])
return self._get_services_collection(
marker, limit, sort_key, sort_dir, expand, resource_url)
@wsme_pecan.wsexpose(Service, wtypes.text)
def get_one(self, service):
"""Retrieve information about the given service.
:param service: ID or name of the service.
"""
if self.from_services:
raise exception.OperationNotPermitted
context = pecan.request.context
rpc_service = api_utils.get_resource('Service', service)
policy.enforce(context, 'service:get', rpc_service,
action='service:get')
return Service.convert_with_links(rpc_service)

View File

@@ -110,7 +110,7 @@ class Strategy(base.APIBase):
"""The name of the goal this audit refers to"""
parameters_spec = {wtypes.text: types.jsontype}
""" Parameters spec dict"""
"""Parameters spec dict"""
def __init__(self, **kwargs):
super(Strategy, self).__init__()

View File

@@ -15,11 +15,13 @@
import jsonpatch
from oslo_config import cfg
from oslo_utils import reflection
from oslo_utils import uuidutils
import pecan
import wsme
from watcher._i18n import _
from watcher.common import utils
from watcher import objects
CONF = cfg.CONF
@@ -80,17 +82,27 @@ def as_filters_dict(**filters):
return filters_dict
def get_resource(resource, resource_ident):
"""Get the resource from the uuid or logical name.
def get_resource(resource, resource_id, eager=False):
"""Get the resource from the uuid, id or logical name.
:param resource: the resource type.
:param resource_ident: the UUID or logical name of the resource.
:param resource_id: the UUID, ID or logical name of the resource.
:returns: The resource.
"""
resource = getattr(objects, resource)
if uuidutils.is_uuid_like(resource_ident):
return resource.get_by_uuid(pecan.request.context, resource_ident)
_get = None
if utils.is_int_like(resource_id):
resource_id = int(resource_id)
_get = resource.get
elif uuidutils.is_uuid_like(resource_id):
_get = resource.get_by_uuid
else:
_get = resource.get_by_name
return resource.get_by_name(pecan.request.context, resource_ident)
method_signature = reflection.get_signature(_get)
if 'eager' in method_signature.parameters:
return _get(pecan.request.context, resource_id, eager=eager)
return _get(pecan.request.context, resource_id)

View File

@@ -18,6 +18,7 @@
from oslo_config import cfg
from oslo_utils import importutils
from pecan import hooks
from six.moves import http_client
from watcher.common import context
@@ -95,22 +96,24 @@ class NoExceptionTracebackHook(hooks.PecanHook):
return
# Do nothing if there is no error.
if 200 <= state.response.status_int < 400:
# Status codes in the range 200 (OK) to 399 (400 = BAD_REQUEST) are not
# an error.
if (http_client.OK <= state.response.status_int <
http_client.BAD_REQUEST):
return
json_body = state.response.json
# Do not remove traceback when server in debug mode (except 'Server'
# errors when 'debuginfo' will be used for traces).
if cfg.CONF.debug and json_body.get('faultcode') != 'Server':
# Do not remove traceback when traceback config is set
if cfg.CONF.debug:
return
faultstring = json_body.get('faultstring')
traceback_marker = 'Traceback (most recent call last):'
if faultstring and (traceback_marker in faultstring):
if faultstring and traceback_marker in faultstring:
# Cut-off traceback.
faultstring = faultstring.split(traceback_marker, 1)[0]
# Remove trailing newlines and spaces if any.
json_body['faultstring'] = faultstring.rstrip()
# Replace the whole json. Cannot change original one beacause it's
# Replace the whole json. Cannot change original one because it's
# generated on the fly.
state.response.json = json_body

View File

@@ -33,7 +33,7 @@ class AuthTokenMiddleware(auth_token.AuthProtocol):
for public routes in the API.
"""
def __init__(self, app, conf, public_api_routes=[]):
def __init__(self, app, conf, public_api_routes=()):
route_pattern_tpl = '%s(\.json|\.xml)?$'
try:

View File

@@ -27,14 +27,14 @@ from oslo_serialization import jsonutils
import six
import webob
from watcher._i18n import _
from watcher._i18n import _LE
from watcher._i18n import _, _LE
LOG = log.getLogger(__name__)
class ParsableErrorMiddleware(object):
"""Replace error body with something the client can parse."""
def __init__(self, app):
self.app = app
@@ -59,8 +59,7 @@ class ParsableErrorMiddleware(object):
# compute the length.
headers = [(h, v)
for (h, v) in headers
if h not in ('Content-Length', 'Content-Type')
]
if h not in ('Content-Length', 'Content-Type')]
# Save the headers in case we need to modify them.
state['headers'] = headers
return start_response(status, headers, exc_info)
@@ -68,24 +67,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.Element('error_message',
text='\n'.join(app_iter)))]
body = [
et.ElementTree.tostring(
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 = [et.ElementTree.tostring(
et.ElementTree.Element('error_message',
text=state['status_code']))]
body = ['<error_message>%s'
'</error_message>' % 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 = [jsonutils.dumps(
{'error_message': '\n'.join(app_iter)})]
{'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'))

View File

@@ -20,9 +20,7 @@ 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
from watcher import objects
LOG = log.getLogger(__name__)
@@ -34,32 +32,20 @@ class DefaultActionPlanHandler(base.BaseActionPlanHandler):
self.service = service
self.action_plan_uuid = action_plan_uuid
def notify(self, uuid, event_type, state):
action_plan = ap_objects.ActionPlan.get_by_uuid(self.ctx, uuid)
def update_action_plan(self, uuid, state):
action_plan = 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.service.publish_status_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)
self.update_action_plan(self.action_plan_uuid,
objects.action_plan.State.ONGOING)
applier = default.DefaultApplier(self.ctx, self.service)
applier.execute(self.action_plan_uuid)
state = ap_objects.State.SUCCEEDED
state = objects.action_plan.State.SUCCEEDED
except Exception as e:
LOG.exception(e)
state = ap_objects.State.FAILED
state = objects.action_plan.State.FAILED
finally:
# update state
self.notify(self.action_plan_uuid,
event_types.EventTypes.LAUNCH_ACTION_PLAN,
state)
self.update_action_plan(self.action_plan_uuid, state)

View File

@@ -1,5 +1,6 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
# Copyright (c) 2016 Intel Corp
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
@@ -17,60 +18,40 @@
# limitations under the License.
#
from oslo_config import cfg
from watcher.applier.messaging import trigger
from watcher.common import service_manager
CONF = cfg.CONF
from watcher import conf
CONF = conf.CONF
# Register options
APPLIER_MANAGER_OPTS = [
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('status_topic',
default='watcher.applier.status',
help='The topic name used for '
'status events, this topic '
'is used so as to notify'
'the others components '
'of the system'),
cfg.StrOpt('publisher_id',
default='watcher.applier.api',
help='The identifier used by watcher '
'module on the message broker'),
cfg.StrOpt('workflow_engine',
default='taskflow',
required=True,
help='Select the engine to use to execute the workflow')
]
class ApplierManager(service_manager.ServiceManager):
opt_group = cfg.OptGroup(name='watcher_applier',
title='Options for the Applier messaging'
'core')
CONF.register_group(opt_group)
CONF.register_opts(APPLIER_MANAGER_OPTS, opt_group)
@property
def service_name(self):
return 'watcher-applier'
@property
def api_version(self):
return '1.0'
class ApplierManager(object):
@property
def publisher_id(self):
return CONF.watcher_applier.publisher_id
API_VERSION = '1.0'
@property
def conductor_topic(self):
return CONF.watcher_applier.conductor_topic
conductor_endpoints = [trigger.TriggerActionPlan]
status_endpoints = []
notification_endpoints = []
notification_topics = []
@property
def notification_topics(self):
return []
def __init__(self):
self.publisher_id = CONF.watcher_applier.publisher_id
self.conductor_topic = CONF.watcher_applier.conductor_topic
self.status_topic = CONF.watcher_applier.status_topic
self.api_version = self.API_VERSION
@property
def conductor_endpoints(self):
return [trigger.TriggerActionPlan]
@property
def notification_endpoints(self):
return []

View File

@@ -1,5 +1,6 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
# Copyright (c) 2016 Intel Corp
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
@@ -15,18 +16,15 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from oslo_config import cfg
from watcher.applier import manager
from watcher.common import exception
from watcher.common import service
from watcher.common import service_manager
from watcher.common import utils
from watcher import conf
CONF = cfg.CONF
CONF.register_group(manager.opt_group)
CONF.register_opts(manager.APPLIER_MANAGER_OPTS, manager.opt_group)
CONF = conf.CONF
class ApplierAPI(service.Service):
@@ -39,21 +37,35 @@ class ApplierAPI(service.Service):
raise exception.InvalidUuidOrName(name=action_plan_uuid)
return self.conductor_client.call(
context.to_dict(), 'launch_action_plan',
action_plan_uuid=action_plan_uuid)
context, 'launch_action_plan', action_plan_uuid=action_plan_uuid)
class ApplierAPIManager(object):
class ApplierAPIManager(service_manager.ServiceManager):
API_VERSION = '1.0'
@property
def service_name(self):
return None
conductor_endpoints = []
status_endpoints = []
notification_endpoints = []
notification_topics = []
@property
def api_version(self):
return '1.0'
def __init__(self):
self.publisher_id = CONF.watcher_applier.publisher_id
self.conductor_topic = CONF.watcher_applier.conductor_topic
self.status_topic = CONF.watcher_applier.status_topic
self.api_version = self.API_VERSION
@property
def publisher_id(self):
return CONF.watcher_applier.publisher_id
@property
def conductor_topic(self):
return CONF.watcher_applier.conductor_topic
@property
def notification_topics(self):
return []
@property
def conductor_endpoints(self):
return []
@property
def notification_endpoints(self):
return []

View File

@@ -21,10 +21,8 @@ import abc
import six
from watcher.applier.actions import factory
from watcher.applier.messaging import event_types
from watcher.common import clients
from watcher.common.loader import loadable
from watcher.common.messaging.events import event
from watcher import objects
@@ -77,12 +75,7 @@ class BaseWorkFlowEngine(loadable.Loadable):
db_action = objects.Action.get_by_uuid(self.context, action.uuid)
db_action.state = state
db_action.save()
ev = event.Event()
ev.type = event_types.EventTypes.LAUNCH_ACTION
ev.data = {}
payload = {'action_uuid': action.uuid,
'action_state': state}
self.applier_manager.publish_status_event(ev.type.name, payload)
# NOTE(v-francoise): Implement notifications for action
@abc.abstractmethod
def execute(self, actions):

View File

@@ -23,7 +23,7 @@ from taskflow import task
from watcher._i18n import _LE, _LW, _LC
from watcher.applier.workflow_engine import base
from watcher.common import exception
from watcher.objects import action as obj_action
from watcher import objects
LOG = log.getLogger(__name__)
@@ -107,14 +107,12 @@ class TaskFlowActionContainer(task.Task):
def pre_execute(self):
try:
self.engine.notify(self._db_action,
obj_action.State.ONGOING)
self.engine.notify(self._db_action, objects.action.State.ONGOING)
LOG.debug("Pre-condition action: %s", self.name)
self.action.pre_condition()
except Exception as e:
LOG.exception(e)
self.engine.notify(self._db_action,
obj_action.State.FAILED)
self.engine.notify(self._db_action, objects.action.State.FAILED)
raise
def execute(self, *args, **kwargs):
@@ -122,15 +120,13 @@ class TaskFlowActionContainer(task.Task):
LOG.debug("Running action: %s", self.name)
self.action.execute()
self.engine.notify(self._db_action,
obj_action.State.SUCCEEDED)
self.engine.notify(self._db_action, objects.action.State.SUCCEEDED)
except Exception as e:
LOG.exception(e)
LOG.error(_LE('The workflow engine has failed '
'to execute the action: %s'), self.name)
self.engine.notify(self._db_action,
obj_action.State.FAILED)
self.engine.notify(self._db_action, objects.action.State.FAILED)
raise
def post_execute(self):
@@ -139,8 +135,7 @@ class TaskFlowActionContainer(task.Task):
self.action.post_condition()
except Exception as e:
LOG.exception(e)
self.engine.notify(self._db_action,
obj_action.State.FAILED)
self.engine.notify(self._db_action, objects.action.State.FAILED)
raise
def revert(self, *args, **kwargs):

View File

@@ -24,19 +24,19 @@ from oslo_log import log as logging
from watcher._i18n import _LI
from watcher.common import service
from watcher import conf
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONF = conf.CONF
def main():
service.prepare_service(sys.argv)
service.prepare_service(sys.argv, CONF)
host, port = cfg.CONF.api.host, cfg.CONF.api.port
protocol = "http" if not CONF.api.enable_ssl_api else "https"
# Build and start the WSGI app
server = service.WSGIService(
'watcher-api', CONF.api.enable_ssl_api)
server = service.WSGIService('watcher-api', CONF.api.enable_ssl_api)
if host == '127.0.0.1':
LOG.info(_LI('serving on 127.0.0.1:%(port)s, '

View File

@@ -20,19 +20,19 @@
import os
import sys
from oslo_config import cfg
from oslo_log import log as logging
from watcher._i18n import _LI
from watcher.applier import manager
from watcher.common import service as watcher_service
from watcher import conf
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONF = conf.CONF
def main():
watcher_service.prepare_service(sys.argv)
watcher_service.prepare_service(sys.argv, CONF)
LOG.info(_LI('Starting Watcher Applier service in PID %s'), os.getpid())

View File

@@ -24,10 +24,11 @@ import sys
from oslo_config import cfg
from watcher.common import service
from watcher import conf
from watcher.db import migration
from watcher.db import purge
CONF = cfg.CONF
CONF = conf.CONF
class DBCommand(object):
@@ -152,5 +153,5 @@ def main():
if not set(sys.argv).intersection(valid_commands):
sys.argv.append('upgrade')
service.prepare_service(sys.argv)
service.prepare_service(sys.argv, CONF)
CONF.command.func()

View File

@@ -20,21 +20,23 @@
import os
import sys
from oslo_config import cfg
from oslo_log import log as logging
from watcher._i18n import _LI
from watcher.common import service as watcher_service
from watcher import conf
from watcher.decision_engine import gmr
from watcher.decision_engine import manager
from watcher.decision_engine import scheduling
from watcher.decision_engine import sync
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONF = conf.CONF
def main():
watcher_service.prepare_service(sys.argv)
watcher_service.prepare_service(sys.argv, CONF)
gmr.register_gmr_plugins()
LOG.info(_LI('Starting Watcher Decision Engine service in PID %s'),
os.getpid())

View File

@@ -24,15 +24,17 @@ from oslo_log import log as logging
from watcher._i18n import _LI
from watcher.common import service as service
from watcher import conf
from watcher.decision_engine import sync
LOG = logging.getLogger(__name__)
CONF = conf.CONF
def main():
LOG.info(_LI('Watcher sync started.'))
service.prepare_service(sys.argv)
service.prepare_service(sys.argv, CONF)
syncer = sync.Syncer()
syncer.sync()

View File

@@ -134,27 +134,24 @@ class CeilometerHelper(object):
aggregate='avg'):
"""Representing a statistic aggregate by operators
:param resource_id: id
:param meter_name: meter names of which we want the statistics
:param period: `period`: In seconds. If no period is given, only one
aggregate statistic is returned. If given, a faceted
result will be returned, divided into given periods.
Periods with no data are ignored.
:param aggregate:
:return:
:param resource_id: id of resource to list statistics for.
:param meter_name: Name of meter to list statistics for.
:param period: Period in seconds over which to group samples.
:param aggregate: Available aggregates are: count, cardinality,
min, max, sum, stddev, avg. Defaults to avg.
:return: Return the latest statistical data, None if no data.
"""
start_time = (datetime.datetime.utcnow() -
datetime.timedelta(seconds=int(period)))
end_time = datetime.datetime.utcnow()
start_time = end_time - datetime.timedelta(seconds=int(period))
query = self.build_query(
resource_id=resource_id, start_time=start_time)
resource_id=resource_id, start_time=start_time, end_time=end_time)
statistic = self.query_retry(f=self.ceilometer.statistics.list,
meter_name=meter_name,
q=query,
period=period,
aggregates=[
{'func': aggregate}],
groupby=['resource_id'])
{'func': aggregate}])
item_value = None
if statistic:

View File

@@ -19,47 +19,14 @@ from neutronclient.neutron import client as netclient
from novaclient import client as nvclient
from oslo_config import cfg
from watcher._i18n import _
from watcher.common import exception
from watcher import conf
NOVA_CLIENT_OPTS = [
cfg.StrOpt('api_version',
default='2',
help=_('Version of Nova API to use in novaclient.'))]
GLANCE_CLIENT_OPTS = [
cfg.StrOpt('api_version',
default='2',
help=_('Version of Glance API to use in glanceclient.'))]
CINDER_CLIENT_OPTS = [
cfg.StrOpt('api_version',
default='2',
help=_('Version of Cinder API to use in cinderclient.'))]
CEILOMETER_CLIENT_OPTS = [
cfg.StrOpt('api_version',
default='2',
help=_('Version of Ceilometer API to use in '
'ceilometerclient.'))]
NEUTRON_CLIENT_OPTS = [
cfg.StrOpt('api_version',
default='2.0',
help=_('Version of Neutron API to use in neutronclient.'))]
cfg.CONF.register_opts(NOVA_CLIENT_OPTS, group='nova_client')
cfg.CONF.register_opts(GLANCE_CLIENT_OPTS, group='glance_client')
cfg.CONF.register_opts(CINDER_CLIENT_OPTS, group='cinder_client')
cfg.CONF.register_opts(CEILOMETER_CLIENT_OPTS, group='ceilometer_client')
cfg.CONF.register_opts(NEUTRON_CLIENT_OPTS, group='neutron_client')
CONF = conf.CONF
_CLIENTS_AUTH_GROUP = 'watcher_clients_auth'
ka_loading.register_auth_conf_options(cfg.CONF, _CLIENTS_AUTH_GROUP)
ka_loading.register_session_conf_options(cfg.CONF, _CLIENTS_AUTH_GROUP)
class OpenStackClients(object):
"""Convenience class to create and cache client instances."""

View File

@@ -51,7 +51,7 @@ class RequestContext(context.RequestContext):
show_deleted=kwargs.pop('show_deleted', False),
request_id=request_id,
resource_uuid=kwargs.pop('resource_uuid', None),
is_admin_project=kwargs.pop('is_admin_project', None),
is_admin_project=kwargs.pop('is_admin_project', True),
overwrite=overwrite,
roles=roles)

View File

@@ -26,22 +26,16 @@ import functools
import sys
from keystoneclient import exceptions as keystone_exceptions
from oslo_config import cfg
from oslo_log import log as logging
import six
from watcher._i18n import _, _LE
from watcher import conf
LOG = logging.getLogger(__name__)
exc_log_opts = [
cfg.BoolOpt('fatal_exception_format_errors',
default=False,
help='Make exception message format errors fatal.'),
]
CONF = cfg.CONF
CONF.register_opts(exc_log_opts)
CONF = conf.CONF
def wrap_keystone_exception(func):
@@ -119,6 +113,10 @@ class WatcherException(Exception):
return six.text_type(self)
class UnsupportedError(WatcherException):
msg_fmt = _("Not supported")
class NotAuthorized(WatcherException):
msg_fmt = _("Not authorized")
code = 403
@@ -168,6 +166,14 @@ class InvalidStrategy(Invalid):
msg_fmt = _("Strategy %(strategy)s is invalid")
class InvalidAudit(Invalid):
msg_fmt = _("Audit %(audit)s is invalid")
class EagerlyLoadedAuditRequired(InvalidAudit):
msg_fmt = _("Audit %(audit)s was not eagerly loaded")
class InvalidUUID(Invalid):
msg_fmt = _("Expected a uuid but received %(uuid)s")
@@ -207,7 +213,7 @@ class AuditTemplateAlreadyExists(Conflict):
class AuditTemplateReferenced(Invalid):
msg_fmt = _("AuditTemplate %(audit_template)s is referenced by one or "
"multiple audit")
"multiple audits")
class AuditTypeNotFound(Invalid):
@@ -317,7 +323,7 @@ class AuthorizationFailure(WatcherException):
class KeystoneFailure(WatcherException):
msg_fmt = _("'Keystone API endpoint is missing''")
msg_fmt = _("Keystone API endpoint is missing")
class ClusterEmpty(WatcherException):
@@ -328,6 +334,10 @@ class MetricCollectorNotDefined(WatcherException):
msg_fmt = _("The metrics resource collector is not defined")
class ClusterStateStale(WatcherException):
msg_fmt = _("The cluster state is stale")
class ClusterDataModelCollectionError(WatcherException):
msg_fmt = _("The cluster data model '%(cdm)s' could not be built")
@@ -336,6 +346,10 @@ class ClusterStateNotDefined(WatcherException):
msg_fmt = _("The cluster state is not defined")
class CapacityNotDefined(WatcherException):
msg_fmt = _("The capacity %(capacity)s is not defined for '%(resource)s'")
class NoAvailableStrategyForGoal(WatcherException):
msg_fmt = _("No strategy could be found to achieve the '%(goal)s' goal.")
@@ -358,6 +372,19 @@ class NoSuchMetricForHost(WatcherException):
msg_fmt = _("No %(metric)s metric for %(host)s found.")
class ServiceAlreadyExists(Conflict):
msg_fmt = _("A service with name %(name)s is already working on %(host)s.")
class ServiceNotFound(ResourceNotFound):
msg_fmt = _("The service %(service)s cannot be found.")
class WildcardCharacterIsUsed(WatcherException):
msg_fmt = _("You shouldn't use any other IDs of %(resource)s if you use "
"wildcard character.")
# Model
class InstanceNotFound(WatcherException):
@@ -382,3 +409,8 @@ class NotSoftDeletedStateError(WatcherException):
class NegativeLimitError(WatcherException):
msg_fmt = _("Limit should be positive")
class NotificationPayloadError(WatcherException):
_msg_fmt = _("Payload not populated when trying to send notification "
"\"%(class_name)s\"")

View File

@@ -1,54 +0,0 @@
# -*- 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.
class Event(object):
"""Generic event to use with EventDispatcher"""
def __init__(self, event_type=None, data=None, request_id=None):
"""Default constructor
:param event_type: the type of the event
:param data: a dictionary which contains data
:param request_id: a string which represent the uuid of the request
"""
self._type = event_type
self._data = data
self._request_id = request_id
@property
def type(self):
return self._type
@type.setter
def type(self, type):
self._type = type
@property
def data(self):
return self._data
@data.setter
def data(self, data):
self._data = data
@property
def request_id(self):
return self._request_id
@request_id.setter
def request_id(self, id):
self._request_id = id

View File

@@ -1,78 +0,0 @@
# -*- 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.
from oslo_log import log
from watcher.decision_engine.messaging import events as messaging_events
LOG = log.getLogger(__name__)
class EventDispatcher(object):
"""Generic event dispatcher which listen and dispatch events"""
def __init__(self):
self._events = dict()
def __del__(self):
self._events = None
def has_listener(self, event_type, listener):
"""Return true if listener is register to event_type """
# Check for event type and for the listener
if event_type in self._events.keys():
return listener in self._events[event_type]
else:
return False
def dispatch_event(self, event):
LOG.debug("dispatch evt : %s" % str(event.type))
"""
Dispatch an instance of Event class
"""
if messaging_events.Events.ALL in self._events.keys():
listeners = self._events[messaging_events.Events.ALL]
for listener in listeners:
listener(event)
# Dispatch the event to all the associated listeners
if event.type in self._events.keys():
listeners = self._events[event.type]
for listener in listeners:
listener(event)
def add_event_listener(self, event_type, listener):
"""Add an event listener for an event type"""
# Add listener to the event type
if not self.has_listener(event_type, listener):
listeners = self._events.get(event_type, [])
listeners.append(listener)
self._events[event_type] = listeners
def remove_event_listener(self, event_type, listener):
"""Remove event listener. """
# Remove the listener from the event type
if self.has_listener(event_type, listener):
listeners = self._events[event_type]
if len(listeners) == 1:
# Only this listener remains so remove the key
del self._events[event_type]
else:
# Update listeners chain
listeners.remove(listener)
self._events[event_type] = listeners

View File

@@ -1,120 +0,0 @@
# -*- 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 socket
import threading
import eventlet
from oslo_config import cfg
from oslo_log import log
import oslo_messaging as om
from watcher.common import rpc
from watcher._i18n import _LE, _LW
# NOTE:
# Ubuntu 14.04 forces librabbitmq when kombu is used
# Unfortunately it forces a version that has a crash
# bug. Calling eventlet.monkey_patch() tells kombu
# to use libamqp instead.
eventlet.monkey_patch()
LOG = log.getLogger(__name__)
CONF = cfg.CONF
class MessagingHandler(threading.Thread):
def __init__(self, publisher_id, topic_name, endpoints, version,
serializer=None):
super(MessagingHandler, self).__init__()
self.publisher_id = publisher_id
self.topic_name = topic_name
self.__endpoints = []
self.__serializer = serializer
self.__version = version
self.__server = None
self.__notifier = None
self.__transport = None
self.add_endpoints(endpoints)
def add_endpoints(self, endpoints):
self.__endpoints.extend(endpoints)
def remove_endpoint(self, endpoint):
if endpoint in self.__endpoints:
self.__endpoints.remove(endpoint)
@property
def endpoints(self):
return self.__endpoints
@property
def transport(self):
return self.__transport
def build_notifier(self):
serializer = rpc.RequestContextSerializer(rpc.JsonPayloadSerializer())
return om.Notifier(
self.__transport,
publisher_id=self.publisher_id,
topic=self.topic_name,
serializer=serializer
)
def build_server(self, target):
return om.get_rpc_server(self.__transport, target,
self.__endpoints,
executor='eventlet',
serializer=self.__serializer)
def _configure(self):
try:
self.__transport = om.get_transport(CONF)
self.__notifier = self.build_notifier()
if len(self.__endpoints):
target = om.Target(
topic=self.topic_name,
# For compatibility, we can override it with 'host' opt
server=CONF.host or socket.getfqdn(),
version=self.__version,
)
self.__server = self.build_server(target)
else:
LOG.warning(
_LW("No endpoint defined; can only publish events"))
except Exception as e:
LOG.exception(e)
LOG.error(_LE("Messaging configuration error"))
def run(self):
LOG.debug("configure MessagingHandler for %s" % self.topic_name)
self._configure()
if len(self.__endpoints) > 0:
LOG.debug("Starting up server")
self.__server.start()
def stop(self):
LOG.debug('Stopped server')
self.__server.stop()
def publish_event(self, event_type, payload, request_id=None):
self.__notifier.info(
{'version_api': self.__version,
'request_id': request_id},
{'event_id': event_type}, payload
)

View File

@@ -1,47 +0,0 @@
# -*- 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 eventlet
import oslo_messaging as messaging
from watcher.common.messaging.utils import observable
eventlet.monkey_patch()
class NotificationHandler(observable.Observable):
def __init__(self, publisher_id):
super(NotificationHandler, self).__init__()
self.publisher_id = publisher_id
def info(self, ctx, publisher_id, event_type, payload, metadata):
if publisher_id == self.publisher_id:
self.set_changed()
self.notify(ctx, publisher_id, event_type, metadata, payload)
return messaging.NotificationResult.HANDLED
def warn(self, ctx, publisher_id, event_type, payload, metadata):
if publisher_id == self.publisher_id:
self.set_changed()
self.notify(ctx, publisher_id, event_type, metadata, payload)
return messaging.NotificationResult.HANDLED
def error(self, ctx, publisher_id, event_type, payload, metadata):
if publisher_id == self.publisher_id:
self.set_changed()
self.notify(ctx, publisher_id, event_type, metadata, payload)
return messaging.NotificationResult.HANDLED

View File

@@ -26,6 +26,8 @@ import cinderclient.exceptions as ciexceptions
import novaclient.exceptions as nvexceptions
from watcher.common import clients
from watcher.common import exception
from watcher.common import utils
LOG = log.getLogger(__name__)
@@ -43,16 +45,35 @@ class NovaHelper(object):
def get_compute_node_list(self):
return self.nova.hypervisors.list()
def get_compute_node_by_id(self, node_id):
"""Get compute node by ID (*not* UUID)"""
# We need to pass an object with an 'id' attribute to make it work
return self.nova.hypervisors.get(utils.Struct(id=node_id))
def get_compute_node_by_hostname(self, node_hostname):
"""Get compute node by ID (*not* UUID)"""
# We need to pass an object with an 'id' attribute to make it work
try:
compute_nodes = self.nova.hypervisors.search(node_hostname)
if len(compute_nodes) != 1:
raise exception.ComputeNodeNotFound(name=node_hostname)
return self.get_compute_node_by_id(compute_nodes[0].id)
except Exception as exc:
LOG.exception(exc)
raise exception.ComputeNodeNotFound(name=node_hostname)
def get_aggregate_list(self):
return self.nova.aggregates.list()
def get_aggregate_detail(self, aggregate_id):
return self.nova.aggregates.get(aggregate_id)
def get_availability_zone_list(self):
return self.nova.availability_zones.list(detailed=True)
def find_instance(self, instance_id):
search_opts = {'all_tenants': True}
instances = self.nova.servers.list(detailed=True,
search_opts=search_opts)
instance = None
for _instance in instances:
if _instance.id == instance_id:
instance = _instance
break
return instance
return self.nova.servers.get(instance_id)
def wait_for_volume_status(self, volume, status, timeout=60,
poll_interval=1):
@@ -643,7 +664,7 @@ class NovaHelper(object):
cache[fid] = flavor
attr_defaults = [('name', 'unknown-id-%s' % fid),
('vcpus', 0), ('ram', 0), ('disk', 0),
('ephemeral', 0)]
('ephemeral', 0), ('extra_specs', {})]
for attr, default in attr_defaults:
if not flavor:
instance.flavor[attr] = default

View File

@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from watcher.common.messaging.utils import synchronization
from watcher.common import synchronization
class Observable(synchronization.Synchronization):

View File

@@ -17,38 +17,9 @@
import os
from oslo_config import cfg
from watcher import conf
PATH_OPTS = [
cfg.StrOpt('pybasedir',
default=os.path.abspath(os.path.join(os.path.dirname(__file__),
'../')),
help='Directory where the watcher python module is installed.'),
cfg.StrOpt('bindir',
default='$pybasedir/bin',
help='Directory where watcher binaries are installed.'),
cfg.StrOpt('state_path',
default='$pybasedir',
help="Top-level directory for maintaining watcher's state."),
]
CONF = cfg.CONF
CONF.register_opts(PATH_OPTS)
def basedir_def(*args):
"""Return an uninterpolated path relative to $pybasedir."""
return os.path.join('$pybasedir', *args)
def bindir_def(*args):
"""Return an uninterpolated path relative to $bindir."""
return os.path.join('$bindir', *args)
def state_path_def(*args):
"""Return an uninterpolated path relative to $state_path."""
return os.path.join('$state_path', *args)
CONF = conf.CONF
def basedir_rel(*args):

View File

@@ -72,8 +72,12 @@ def init(conf):
aliases=TRANSPORT_ALIASES)
serializer = RequestContextSerializer(JsonPayloadSerializer())
NOTIFIER = messaging.Notifier(NOTIFICATION_TRANSPORT,
serializer=serializer)
if not conf.notification_level:
NOTIFIER = messaging.Notifier(
NOTIFICATION_TRANSPORT, serializer=serializer, driver='noop')
else:
NOTIFIER = messaging.Notifier(NOTIFICATION_TRANSPORT,
serializer=serializer)
def initialized():
@@ -121,7 +125,7 @@ class RequestContextSerializer(messaging.Serializer):
return self._base.deserialize_entity(context, entity)
def serialize_context(self, context):
return context
return context.to_dict()
def deserialize_context(self, context):
return watcher_context.RequestContext.from_dict(context)
@@ -146,8 +150,6 @@ def get_server(target, endpoints, serializer=None):
serializer=serializer)
def get_notifier(service=None, host=None, publisher_id=None):
def get_notifier(publisher_id):
assert NOTIFIER is not None
if not publisher_id:
publisher_id = "%s.%s" % (service, host or CONF.host)
return NOTIFIER.prepare(publisher_id=publisher_id)

View File

@@ -14,9 +14,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
import datetime
import socket
import eventlet
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import _options
@@ -27,30 +28,36 @@ from oslo_reports import opts as gmr_opts
from oslo_service import service
from oslo_service import wsgi
from watcher._i18n import _, _LI
from watcher._i18n import _
from watcher.api import app
from watcher.common import config
from watcher.common.messaging.events import event_dispatcher as dispatcher
from watcher.common.messaging import messaging_handler
from watcher.common import context
from watcher.common import rpc
from watcher.common import scheduling
from watcher.conf import plugins as plugins_conf
from watcher import objects
from watcher.objects import base
from watcher import opts
from watcher.objects import fields as wfields
from watcher import version
service_opts = [
cfg.IntOpt('periodic_interval',
default=60,
help=_('Seconds between running periodic tasks.')),
cfg.StrOpt('host',
default=socket.getfqdn(),
help=_('Name of this node. This can be an opaque identifier. '
'It is not necessarily a hostname, FQDN, or IP address. '
'However, the node name must be valid within '
'an AMQP key, and if using ZeroMQ, a valid '
'hostname, FQDN, or IP address.')),
]
# NOTE:
# Ubuntu 14.04 forces librabbitmq when kombu is used
# Unfortunately it forces a version that has a crash
# bug. Calling eventlet.monkey_patch() tells kombu
# to use libamqp instead.
eventlet.monkey_patch()
NOTIFICATION_OPTS = [
cfg.StrOpt('notification_level',
choices=[''] + list(wfields.NotificationPriority.ALL),
default=wfields.NotificationPriority.INFO,
help=_('Specifies the minimum level for which to send '
'notifications. If not set, no notifications will '
'be sent. The default is for this option to be at the '
'`INFO` level.'))
]
cfg.CONF.register_opts(NOTIFICATION_OPTS)
cfg.CONF.register_opts(service_opts)
CONF = cfg.CONF
LOG = log.getLogger(__name__)
@@ -68,21 +75,21 @@ Singleton = service.Singleton
class WSGIService(service.ServiceBase):
"""Provides ability to launch Watcher API from wsgi app."""
def __init__(self, name, use_ssl=False):
def __init__(self, service_name, use_ssl=False):
"""Initialize, but do not start the WSGI server.
:param name: The name of the WSGI server given to the loader.
:param service_name: The service name of the WSGI server.
:param use_ssl: Wraps the socket in an SSL context if True.
"""
self.name = name
self.service_name = service_name
self.app = app.VersionSelectorApplication()
self.workers = (CONF.api.workers or
processutils.get_worker_count())
self.server = wsgi.Server(CONF, name, self.app,
self.server = wsgi.Server(CONF, self.service_name, self.app,
host=CONF.api.host,
port=CONF.api.port,
use_ssl=use_ssl,
logger_name=name)
logger_name=self.service_name)
def start(self):
"""Start serving this service using loaded configuration"""
@@ -101,7 +108,53 @@ class WSGIService(service.ServiceBase):
self.server.reset()
class Service(service.ServiceBase, dispatcher.EventDispatcher):
class ServiceHeartbeat(scheduling.BackgroundSchedulerService):
def __init__(self, gconfig=None, service_name=None, **kwargs):
gconfig = None or {}
super(ServiceHeartbeat, self).__init__(gconfig, **kwargs)
self.service_name = service_name
self.context = context.make_context()
def send_beat(self):
host = CONF.host
watcher_list = objects.Service.list(
self.context, filters={'name': self.service_name,
'host': host})
if watcher_list:
watcher_service = watcher_list[0]
watcher_service.last_seen_up = datetime.datetime.utcnow()
watcher_service.save()
else:
watcher_service = objects.Service(self.context)
watcher_service.name = self.service_name
watcher_service.host = host
watcher_service.create()
def add_heartbeat_job(self):
self.add_job(self.send_beat, 'interval', seconds=60,
next_run_time=datetime.datetime.now())
def start(self):
"""Start service."""
self.add_heartbeat_job()
super(ServiceHeartbeat, self).start()
def stop(self):
"""Stop service."""
self.shutdown()
def wait(self):
"""Wait for service to complete."""
def reset(self):
"""Reset service.
Called in case service running in daemon mode receives SIGHUP.
"""
class Service(service.ServiceBase):
API_VERSION = '1.0'
@@ -110,18 +163,14 @@ class Service(service.ServiceBase, dispatcher.EventDispatcher):
self.manager = manager_class()
self.publisher_id = self.manager.publisher_id
self.api_version = self.manager.API_VERSION
self.api_version = self.manager.api_version
self.conductor_topic = self.manager.conductor_topic
self.status_topic = self.manager.status_topic
self.notification_topics = self.manager.notification_topics
self.conductor_endpoints = [
ep(self) for ep in self.manager.conductor_endpoints
]
self.status_endpoints = [
ep(self.publisher_id) for ep in self.manager.status_endpoints
]
self.notification_endpoints = self.manager.notification_endpoints
self.serializer = rpc.RequestContextSerializer(
@@ -130,22 +179,23 @@ class Service(service.ServiceBase, dispatcher.EventDispatcher):
self._transport = None
self._notification_transport = None
self._conductor_client = None
self._status_client = None
self.conductor_topic_handler = None
self.status_topic_handler = None
self.notification_handler = None
self.heartbeat = None
if self.conductor_topic and self.conductor_endpoints:
self.conductor_topic_handler = self.build_topic_handler(
self.conductor_topic, self.conductor_endpoints)
if self.status_topic and self.status_endpoints:
self.status_topic_handler = self.build_topic_handler(
self.status_topic, self.status_endpoints)
if self.notification_topics and self.notification_endpoints:
self.notification_handler = self.build_notification_handler(
self.notification_topics, self.notification_endpoints
)
self.service_name = self.manager.service_name
if self.service_name:
self.heartbeat = ServiceHeartbeat(
service_name=self.manager.service_name)
@property
def transport(self):
@@ -174,25 +224,17 @@ class Service(service.ServiceBase, dispatcher.EventDispatcher):
def conductor_client(self, c):
self.conductor_client = c
@property
def status_client(self):
if self._status_client is None:
target = om.Target(
topic=self.status_topic,
version=self.API_VERSION,
)
self._status_client = om.RPCClient(
self.transport, target, serializer=self.serializer)
return self._status_client
@status_client.setter
def status_client(self, c):
self.status_client = c
def build_topic_handler(self, topic_name, endpoints=()):
return messaging_handler.MessagingHandler(
self.publisher_id, topic_name, [self.manager] + list(endpoints),
self.api_version, self.serializer)
serializer = rpc.RequestContextSerializer(rpc.JsonPayloadSerializer())
target = om.Target(
topic=topic_name,
# For compatibility, we can override it with 'host' opt
server=CONF.host or socket.gethostname(),
version=self.api_version,
)
return om.get_rpc_server(
self.transport, target, endpoints,
executor='eventlet', serializer=serializer)
def build_notification_handler(self, topic_names, endpoints=()):
serializer = rpc.RequestContextSerializer(rpc.JsonPayloadSerializer())
@@ -207,20 +249,20 @@ class Service(service.ServiceBase, dispatcher.EventDispatcher):
CONF.transport_url, CONF.rpc_backend)
if self.conductor_topic_handler:
self.conductor_topic_handler.start()
if self.status_topic_handler:
self.status_topic_handler.start()
if self.notification_handler:
self.notification_handler.start()
if self.heartbeat:
self.heartbeat.start()
def stop(self):
LOG.debug("Disconnecting from '%s' (%s)",
CONF.transport_url, CONF.rpc_backend)
if self.conductor_topic_handler:
self.conductor_topic_handler.stop()
if self.status_topic_handler:
self.status_topic_handler.stop()
if self.notification_handler:
self.notification_handler.stop()
if self.heartbeat:
self.heartbeat.stop()
def reset(self):
"""Reset a service in case it received a SIGHUP."""
@@ -228,34 +270,11 @@ class Service(service.ServiceBase, dispatcher.EventDispatcher):
def wait(self):
"""Wait for service to complete."""
def publish_control(self, event, payload):
return self.conductor_topic_handler.publish_event(event, payload)
def publish_status_event(self, event, payload, request_id=None):
if self.status_topic_handler:
return self.status_topic_handler.publish_event(
event, payload, request_id)
else:
LOG.info(
_LI("No status notifier declared: notification '%s' not sent"),
event)
def get_version(self):
return self.api_version
def check_api_version(self, context):
def check_api_version(self, ctx):
api_manager_version = self.conductor_client.call(
context.to_dict(), 'check_api_version',
api_version=self.api_version)
ctx, 'check_api_version', api_version=self.api_version)
return api_manager_version
def response(self, evt, ctx, message):
payload = {
'request_id': ctx['request_id'],
'msg': message
}
self.publish_status_event(evt, payload)
def launch(conf, service_, workers=1, restart_method='reload'):
return service.launch(conf, service_, workers, restart_method)
@@ -269,7 +288,9 @@ def prepare_service(argv=(), conf=cfg.CONF):
cfg.set_defaults(_options.log_opts,
default_log_levels=_DEFAULT_LOG_LEVELS)
log.setup(conf, 'python-watcher')
conf.log_opt_values(LOG, logging.DEBUG)
conf.log_opt_values(LOG, log.DEBUG)
objects.register_all()
gmr.TextGuruMeditation.register_section(_('Plugins'), opts.show_plugins)
gmr.TextGuruMeditation.setup_autorun(version)
gmr.TextGuruMeditation.register_section(
_('Plugins'), plugins_conf.show_plugins)
gmr.TextGuruMeditation.setup_autorun(version, conf=conf)

View File

@@ -0,0 +1,50 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2016 Servionica
##
# 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
@six.add_metaclass(abc.ABCMeta)
class ServiceManager(object):
@abc.abstractproperty
def service_name(self):
raise NotImplementedError()
@abc.abstractproperty
def api_version(self):
raise NotImplementedError()
@abc.abstractproperty
def publisher_id(self):
raise NotImplementedError()
@abc.abstractproperty
def conductor_topic(self):
raise NotImplementedError()
@abc.abstractproperty
def notification_topics(self):
raise NotImplementedError()
@abc.abstractproperty
def conductor_endpoints(self):
raise NotImplementedError()
@abc.abstractproperty
def notification_endpoints(self):
raise NotImplementedError()

View File

@@ -16,29 +16,21 @@
"""Utilities and helper functions."""
from jsonschema import validators
from oslo_config import cfg
from oslo_log import log as logging
from watcher.common import exception
import re
import six
import uuid
from jsonschema import validators
from oslo_log import log as logging
from oslo_utils import strutils
from oslo_utils import timeutils
from oslo_utils import uuidutils
import six
from watcher._i18n import _LW
from watcher.common import exception
UTILS_OPTS = [
cfg.StrOpt('rootwrap_config',
default="/etc/watcher/rootwrap.conf",
help='Path to the rootwrap configuration file to use for '
'running commands as root.'),
cfg.StrOpt('tempdir',
help='Explicitly specify the temporary working directory.'),
]
from watcher import conf
CONF = cfg.CONF
CONF.register_opts(UTILS_OPTS)
CONF = conf.CONF
LOG = logging.getLogger(__name__)
@@ -66,6 +58,12 @@ class Struct(dict):
raise AttributeError(name)
generate_uuid = uuidutils.generate_uuid
is_uuid_like = uuidutils.is_uuid_like
is_int_like = strutils.is_int_like
strtime = timeutils.strtime
def safe_rstrip(value, chars=None):
"""Removes trailing characters from a string if that does not make it empty
@@ -83,31 +81,6 @@ def safe_rstrip(value, chars=None):
return value.rstrip(chars) or value
def generate_uuid():
return str(uuid.uuid4())
def is_int_like(val):
"""Check if a value looks like an int."""
try:
return str(int(val)) == str(val)
except Exception:
return False
def is_uuid_like(val):
"""Returns validation of a value as a UUID.
For our purposes, a UUID is a canonical form string:
aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
"""
try:
return str(uuid.UUID(val)) == val
except (TypeError, ValueError, AttributeError):
return False
def is_hostname_safe(hostname):
"""Determine if the supplied hostname is RFC compliant.
@@ -133,10 +106,6 @@ def get_cls_import_path(cls):
return module + '.' + cls.__name__
def strtime(at):
return at.strftime("%Y-%m-%dT%H:%M:%S.%f")
# Default value feedback extension as jsonschema doesn't support it
def extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
@@ -165,7 +134,7 @@ def extend_with_strict_schema(validator_class):
raise exception.AuditParameterNotAllowed(parameter=para)
for error in validate_properties(
validator, properties, instance, schema
validator, properties, instance, schema
):
yield error
@@ -173,3 +142,5 @@ def extend_with_strict_schema(validator_class):
StrictDefaultValidatingDraft4Validator = extend_with_default(
extend_with_strict_schema(validators.Draft4Validator))
Draft4Validator = validators.Draft4Validator

54
watcher/conf/__init__.py Normal file
View File

@@ -0,0 +1,54 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
# Copyright (c) 2016 Intel Corp
#
# Authors: Vincent FRANCOISE <vincent.francoise@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.conf import api
from watcher.conf import applier
from watcher.conf import ceilometer_client
from watcher.conf import cinder_client
from watcher.conf import clients_auth
from watcher.conf import db
from watcher.conf import decision_engine
from watcher.conf import exception
from watcher.conf import glance_client
from watcher.conf import neutron_client
from watcher.conf import nova_client
from watcher.conf import paths
from watcher.conf import planner
from watcher.conf import service
from watcher.conf import utils
CONF = cfg.CONF
service.register_opts(CONF)
api.register_opts(CONF)
utils.register_opts(CONF)
paths.register_opts(CONF)
exception.register_opts(CONF)
db.register_opts(CONF)
planner.register_opts(CONF)
applier.register_opts(CONF)
decision_engine.register_opts(CONF)
nova_client.register_opts(CONF)
glance_client.register_opts(CONF)
cinder_client.register_opts(CONF)
ceilometer_client.register_opts(CONF)
neutron_client.register_opts(CONF)
clients_auth.register_opts(CONF)

60
watcher/conf/_opts.py Normal file
View File

@@ -0,0 +1,60 @@
# -*- encoding: utf-8 -*-
# Copyright 2014
# The Cloudscaling Group, Inc.
# Copyright (c) 2016 Intel Corp
#
# 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 keystoneauth1 import loading as ka_loading
from watcher.conf import api as conf_api
from watcher.conf import applier as conf_applier
from watcher.conf import ceilometer_client as conf_ceilometer_client
from watcher.conf import cinder_client as conf_cinder_client
from watcher.conf import db
from watcher.conf import decision_engine as conf_de
from watcher.conf import exception
from watcher.conf import glance_client as conf_glance_client
from watcher.conf import neutron_client as conf_neutron_client
from watcher.conf import nova_client as conf_nova_client
from watcher.conf import paths
from watcher.conf import planner as conf_planner
from watcher.conf import utils
def list_opts():
"""Legacy aggregation of all the watcher config options"""
return [
('DEFAULT',
(conf_api.AUTH_OPTS +
exception.EXC_LOG_OPTS +
paths.PATH_OPTS +
utils.UTILS_OPTS)),
('api', conf_api.API_SERVICE_OPTS),
('database', db.SQL_OPTS),
('watcher_planner', conf_planner.WATCHER_PLANNER_OPTS),
('watcher_applier', conf_applier.APPLIER_MANAGER_OPTS),
('watcher_decision_engine',
(conf_de.WATCHER_DECISION_ENGINE_OPTS +
conf_de.WATCHER_CONTINUOUS_OPTS)),
('nova_client', conf_nova_client.NOVA_CLIENT_OPTS),
('glance_client', conf_glance_client.GLANCE_CLIENT_OPTS),
('cinder_client', conf_cinder_client.CINDER_CLIENT_OPTS),
('ceilometer_client', conf_ceilometer_client.CEILOMETER_CLIENT_OPTS),
('neutron_client', conf_neutron_client.NEUTRON_CLIENT_OPTS),
('watcher_clients_auth',
(ka_loading.get_auth_common_conf_options() +
ka_loading.get_auth_plugin_conf_options('password') +
ka_loading.get_session_conf_options()))
]

67
watcher/conf/api.py Normal file
View File

@@ -0,0 +1,67 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 Intel Corp
#
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.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
api = cfg.OptGroup(name='api',
title='Options for the Watcher API service')
AUTH_OPTS = [
cfg.BoolOpt('enable_authentication',
default=True,
help='This option enables or disables user authentication '
'via keystone. Default value is True.'),
]
API_SERVICE_OPTS = [
cfg.PortOpt('port',
default=9322,
help='The port for the watcher API server'),
cfg.StrOpt('host',
default='127.0.0.1',
help='The listen IP address for the watcher API server'),
cfg.IntOpt('max_limit',
default=1000,
help='The maximum number of items returned in a single '
'response from a collection resource'),
cfg.IntOpt('workers',
min=1,
help='Number of workers for Watcher API service. '
'The default is equal to the number of CPUs available '
'if that can be determined, else a default worker '
'count of 1 is returned.'),
cfg.BoolOpt('enable_ssl_api',
default=False,
help="Enable the integrated stand-alone API to service "
"requests via HTTPS instead of HTTP. If there is a "
"front-end service performing HTTPS offloading from "
"the service, this option should be False; note, you "
"will want to change public API endpoint to represent "
"SSL termination URL with 'public_endpoint' option."),
]
def register_opts(conf):
conf.register_group(api)
conf.register_opts(API_SERVICE_OPTS, group=api)
conf.register_opts(AUTH_OPTS)
def list_opts():
return [('api', API_SERVICE_OPTS), ('DEFAULT', AUTH_OPTS)]

53
watcher/conf/applier.py Normal file
View File

@@ -0,0 +1,53 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 Intel Corp
#
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.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
watcher_applier = cfg.OptGroup(name='watcher_applier',
title='Options for the Applier messaging'
'core')
APPLIER_MANAGER_OPTS = [
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('publisher_id',
default='watcher.applier.api',
help='The identifier used by watcher '
'module on the message broker'),
cfg.StrOpt('workflow_engine',
default='taskflow',
required=True,
help='Select the engine to use to execute the workflow')
]
def register_opts(conf):
conf.register_group(watcher_applier)
conf.register_opts(APPLIER_MANAGER_OPTS, group=watcher_applier)
def list_opts():
return [('watcher_applier', APPLIER_MANAGER_OPTS)]

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