Compare commits

...

94 Commits
1.2.0 ... 1.3.0

Author SHA1 Message Date
licanwei
5817f6833c Fix gate-watcher-python27-ubuntu-xenial FAILURE
There is no exc.HTTPUnauthorized in monascaclient now.

Change-Id: I1942b32d4581d1770819aa083a640f394c17d25a
Closes-Bug: #1706858
2017-07-27 15:20:51 +08:00
Jenkins
67754102c8 Merge "Remove all sphinx warnings" 2017-07-26 01:07:44 +00:00
Jenkins
6545f9c2ad Merge "Update the documention for doc migration" 2017-07-25 13:25:42 +00:00
Jenkins
583c23b9d9 Merge "Update URLs in documents according to document migration" 2017-07-25 13:25:36 +00:00
Jenkins
ea9ab8d6e5 Merge "Update default ironic endpoint type" 2017-07-25 13:20:59 +00:00
Alexander Chadin
8fd57276be Remove all sphinx warnings
This patch set fixes all sphinx warnings and makes
https://docs.openstack.org/watcher/latest/configuration/index.html
available.

Change-Id: I76a715c10cb1d582419ff37869093fa9b8678310
2017-07-25 07:31:53 +00:00
zhengwei6082
1d197e5e8f Update the documention for doc migration
Change-Id: I003f4b0abde102b07646ef180e2506bca19162ff
2017-07-25 06:22:03 +00:00
Hidekazu Nakamura
b33337b7bd Replace voluptuous with JSONSchema in BaseAction
Now that we replaced voluptuous with JSONSchema in all actions,
this patch replaces voluptuous with JSONSchema in BaseAction
and removes validate_parameters method in each action.

Partially Implements: blueprint jsonschema-validation

Change-Id: I07c907ddfa4a568d7fff42776df02218330d56a0
2017-07-25 13:19:13 +09:00
melissaml
d0b9b6ce38 Update URLs in documents according to document migration
Change-Id: I52396f2bed4b0fde889b37cabb6a15225a63cc0d
2017-07-25 10:07:44 +08:00
OpenStack Proposal Bot
3cd847e2ab Updated from global requirements
Change-Id: I5631ff4426442335051cc55c1af2f855b08e19f7
2017-07-23 19:11:17 +00:00
Jenkins
ba907f4905 Merge "Remove testenv for install-guide" 2017-07-22 01:20:07 +00:00
Alexander Chadin
8e787d0a87 Remove testenv for install-guide
Since install-guide is placed in doc/source/
we don't need additional env to build it.

Change-Id: Idcc711a4d5f4a88e960f22795b7e101ae388551e
2017-07-21 12:54:06 +03:00
Jenkins
b45c7b678e Merge "Add title to administrator guide" 2017-07-21 09:37:52 +00:00
Jenkins
ec64e04cc5 Merge "[Doc] Add gnocchi to system architecture diagram" 2017-07-21 07:41:31 +00:00
Jenkins
f206c2d425 Merge "Enable migration to rely on nova-scheduler" 2017-07-21 02:44:31 +00:00
Jenkins
1500bee1c6 Merge "Fix devstack plugin" 2017-07-20 09:27:49 +00:00
Jenkins
9f813fb90d Merge "Update weekly meetings time in docs" 2017-07-20 09:23:31 +00:00
Alexander Chadin
8167eec625 Fix devstack plugin
This patch set fixes devstack plugin by removing head
keyword from watcher-db-manage.

It is related to https://review.openstack.org/#/c/483825/

Change-Id: I3dcea6ae799c94a882e68d66920c5cd87d83d85e
2017-07-20 08:48:57 +00:00
aditi
6731851383 Enable migration to rely on nova-scheduler
This patch removes the requirement to specify destination node,
for migrate action. There can be some strategies that wants to rely
on nova-scheduler for destination node. It will not impact any current
strategy behavior.

Change-Id: Ia12b2f0b8369e632a7959b28e485d86b6cff83e9
Closes-Bug: #1691056
2017-07-20 05:25:08 +00:00
Jenkins
0ddfa278ef Merge "remove useless logging" 2017-07-19 12:42:52 +00:00
Jenkins
aae7699bc5 Merge "Fix dbmanage upgrade and downgrade" 2017-07-19 12:37:59 +00:00
licanwei
8521608e19 Update default ironic endpoint type
The default ironic endpoint type is publicURL in ironicclient.

Change-Id: Ief78c085adafb08abb09c77af7429fbe6c3d7405
2017-07-19 11:04:12 +08:00
Jenkins
2266e2baa3 Merge "New cron type for audit interval" 2017-07-19 02:46:25 +00:00
OpenStack Proposal Bot
e08a0e9af2 Updated from global requirements
Change-Id: Ib1f8d483a2286fde752a5051f8d3a14d1fee63a8
2017-07-18 01:59:22 +00:00
licanwei
844577e9cc remove useless logging
There are many uselees logging in the decision log file:
Arguments dropped when creating context: {u'global_request_id': None}

Change-Id: I7583b2ff6ea1cc15173536ca1cf08cc9f0ecb20d
Closes-Bug: #1704736
2017-07-17 16:15:23 +08:00
Alexander Chadin
0b44492da7 New cron type for audit interval
This patch set adds cron supporting for audit.

Implements: blueprint cron-based-continuous-audits
Change-Id: I8570bebb13332dfba80185e912aeda45b6b4cd70
2017-07-14 11:50:46 +03:00
licanwei
b146e29c39 Fix dbmanage upgrade and downgrade
The argument is '--revision'

Change-Id: Icf3a37ed42dd1dfe4fc9c52525dd4f0fb851417c
2017-07-14 14:12:41 +08:00
Alexander Chadin
0cabd5bd3a Update weekly meetings time in docs
Change-Id: Ic6ebbda653d5b90d6e1e3d866f4180e6d6e07452
2017-07-13 15:52:28 +03:00
Jenkins
728acc091b Merge "Abort operation for live migration" 2017-07-13 09:05:56 +00:00
Hidekazu Nakamura
7340decf73 Add title to administrator guide
As of now, Watcher administrator guide has no title[1]
This patch fix it.

[1] https://docs.openstack.org/watcher/latest/admin/

Change-Id: I995faf02f9217187b824974ee391918dfd6ed920
2017-07-13 16:01:26 +09:00
Jenkins
84e8eb4cb0 Merge "Cinder model integration" 2017-07-12 14:55:10 +00:00
aditi
5283871af5 Abort operation for live migration
This patch adds abort operation for live migration
to support abort in cancel action plan.

Change-Id: I458e93d9bd09dc4cf80cc941104129fc7600a6b1
Partially-Implements: blueprint cancel-action-plan
2017-07-12 06:09:23 +00:00
Hidekazu Nakamura
916fd73186 [Doc] Add gnocchi to system architecture diagram
This patch set adds gnocchi to system architecture diagram.

Change-Id: I8e3a53910ad9ea861f174a6f178199626ed2247f
2017-07-11 00:16:55 +09:00
xiaoxue
63c1aabdd2 bug fix: Can't get sample through CeilometerHelper
wrong parameter passing of function "query_sample"
the default parameter will be recognized as "user_id"

Change-Id: I293b130c3f709dc93cd4b1b7382ae8895a54765d
Closes-Bug: #1703297
2017-07-10 14:37:33 +08:00
Jenkins
03569db6c3 Merge "move doc/source/webapi content to doc/source/api" 2017-07-07 08:14:07 +00:00
Jenkins
19cf05fd75 Merge "Replace voluptuous with JSONSchema to validate change_node_power_state" 2017-07-07 05:49:51 +00:00
Jenkins
251ca83ddc Merge "Update Documentation link in README" 2017-07-07 05:47:06 +00:00
akihito-inoh
5b349b4e89 Replace voluptuous with JSONSchema to validate change_node_power_state
Now in watcher,both JSONSchema and voluptuous are used to validate
JSON payloads. We want to remove voluptuous and Use
JSONSchema as our only JSON validation tool to keep consistence
and also to make it easier to expose the validation schema
through our API in future work.

In this patch, we replace voluptuous with JSONSchema to validate
the change_node_power_state action in watcher applier.

Partially Implements: blueprint jsonschema-validation

Change-Id: If9ffe5e0b107e0da5673247e4d5ec9917790827f
2017-07-06 15:58:49 +00:00
Alexander Chadin
b0d03ae6b8 move doc/source/webapi content to doc/source/api
Change-Id: I5b1023f2d7ac2760895bba8f7d0cb10260061754
2017-07-06 15:55:12 +03:00
Jenkins
78de029a57 Merge "Adapt watcher documentation for new standards" 2017-07-06 10:23:29 +00:00
Hidekazu Nakamura
489356da3a Cinder model integration
This patch adds Cinder model integration.

Change-Id: I31d5bc5e2bbed885d074d66bf7999d42cec15f10
Implements: blueprint cinder-model-integration
2017-07-05 08:55:10 +09:00
Alexander Chadin
db4339c371 Update Documentation link in README
This patch set updates documentation link in README
because of doc migrations.

Depends-On: Iecb4f60efb015a56b9b37331859848b287112842
Change-Id: I5b8a5ec8a328ae275fe6271965ea0077ae301814
2017-07-04 16:01:07 +03:00
Alexander Chadin
c7ec186576 Adapt watcher documentation for new standards
This patch set makes the following changes:

 * Add index file to each subdirectory of doc/source
 * Update doc/source/index.rst with new links
 * Move content of install-guide to the doc/source/install
 * Minor changes

Depends-On: Ifc5512c0e2373cf3387e0e0498268eab092e52bb
Change-Id: Iecb4f60efb015a56b9b37331859848b287112842
2017-07-04 15:49:24 +03:00
Hidekazu Nakamura
12bdfca0d9 Replace default neutron endpoint type
The default neutron endpoint type is publicURL in neutronclient.
This patch replaces default neutron endpoint type from
internalURL to publicURL.

Change-Id: I8162b6178051df5f086488ecfb5d0bdc569ef9cd
Related-Bug: #1686298
2017-07-01 19:51:06 +09:00
Jenkins
5f179609d0 Merge "Replace default glance endpoint type" 2017-06-30 17:21:28 +00:00
aditi
a842bc1c91 switch to openstackdocs theme.
Change-Id: I95a1f6813225134fafdc22384fb8a822ab9e013d
2017-06-29 08:50:03 +00:00
Hidekazu Nakamura
5eb1d91335 Replace default glance endpoint type
The default glance endpoint type is publicURL in glanceclient.
This patch replaces default glance endpoint type from
internalURL to publicURL.

Change-Id: I39451ba89f191693475a694bd5c9045bf3bf539a
Related-Bug: #1686298
2017-06-29 16:42:39 +09:00
Jenkins
264b0fe9a1 Merge "Noisy Neighbor Strategy" 2017-06-28 12:06:15 +00:00
Jenkins
a487a4efc8 Merge "Fix test_list_with_limit failed" 2017-06-28 11:39:51 +00:00
Jenkins
034f0bf68a Merge "Replace the usage of 'manager' with 'os_primary'" 2017-06-28 08:51:02 +00:00
licanwei
87b95bb639 Fix test_list_with_limit failed
There is no action plan if creating audit was cancelled.
In this case, we just return none.

Change-Id: Ia7a93dab5978d181557d7dd7499e83655aec6f40
Closes-Bug: #1700901
2017-06-28 14:40:46 +08:00
Jenkins
e081ac91b4 Merge "Updated from global requirements" 2017-06-28 06:09:56 +00:00
rajat29
3574dba9da Replace the usage of 'manager' with 'os_primary'
In tempest, alias 'manager' has been moved to 'os_primary'
in version Pike, and it will be removed in version Queens.
This patch is to replace the usage of 'manager' with 'os_primary'.

For other details, please check [1] and [2]
[1] https://review.openstack.org/#/c/468036/
[2] https://review.openstack.org/#/c/463484/

Change-Id: I582758c42f61a85a2fd4aac5f7a97cd6021ecf68
2017-06-28 10:36:43 +05:30
Jenkins
8fc9c6c1d8 Merge "avoid repeated actions in the solution" 2017-06-27 23:48:16 +00:00
OpenStack Proposal Bot
51f17f1d7d Updated from global requirements
Change-Id: I5298796b492396d0ecfccbcf601c11069c20b624
2017-06-27 12:25:18 +00:00
Jenkins
313f156394 Merge "Update .gitignore" 2017-06-27 11:36:03 +00:00
suzhengwei
129de26419 avoid repeated actions in the solution
Change-Id: If163aee969b51764d69d42655c05e0651e4f150c
2017-06-27 09:02:27 +00:00
Jenkins
d38bc4e716 Merge "node.status for vm_workload_consolidation" 2017-06-27 08:34:33 +00:00
Jenkins
6ffa5feaaf Merge "Fix get_action_plan_list filter error" 2017-06-27 08:33:50 +00:00
Jenkins
bdd3a6ab89 Merge "Pass environment variables of proxy to tox" 2017-06-27 07:37:31 +00:00
iswarya_vakati
8c5f363844 Update .gitignore
Because egg* already ignores egg-info.

Change-Id: I0afedfa82bc1c91d40e42f21fdcd90eccc0faacf
2017-06-26 17:30:19 +05:30
Jenkins
e61140edfb Merge "Enable some off-by-default checks" 2017-06-25 12:07:09 +00:00
Kien Nguyen
abc019829f Pass environment variables of proxy to tox
When a development environment is under a proxy, tox is failed even if
environment variables of the proxy are set.

This patch fix this problem. Refer patch set [1]

[1] https://review.openstack.org/#/c/189569

Change-Id: I6c0b896a6de1b7193dd4b77b6bc4433d0c75732d
2017-06-25 18:36:45 +07:00
Jenkins
0dd5826dd3 Merge "fix Keyerror in test_nova_cdmc" 2017-06-24 03:41:26 +00:00
zhengwei6082
227a9e9b63 Enable some off-by-default checks
Some of the available checks are disabled by default, like:
[H106] Don’t put vim configuration in source files
[H203] Use assertIs(Not)None to check for None

Change-Id: I369cff1c0f7f3cd3f5bcf3785b6904c9326c6759
2017-06-23 17:35:08 +08:00
Jenkins
5b6768140f Merge "Replace voluptuous with JSONSchema to validate sleep action" 2017-06-21 14:39:14 +00:00
Jenkins
1ac7fbec34 Merge "Updated from global requirements" 2017-06-21 10:01:02 +00:00
Jenkins
ebb8885ece Merge "Add action for compute node power on/off" 2017-06-21 09:59:13 +00:00
Jenkins
3fb4cadd76 Merge "Replace voluptuous with JSONSchema to validate resize action" 2017-06-21 09:45:39 +00:00
OpenStack Proposal Bot
c1d3f8094b Updated from global requirements
Change-Id: I1a5694edcdeefad2ee93b0c893f79c1ddf792692
2017-06-19 21:15:54 +00:00
licanwei
59649f6a81 Fix get_action_plan_list filter error
When using 'query.filter_by(deleted_at=None)'
Will generate the incorrect SQL 'audits.deleted_at', as below:
SELECT * FROM action_plans INNER JOIN audits ON audits.id =
action_plans.audit_id WHERE audits.uuid = '' AND
audits.deleted_at IS NULL ORDER BY action_plans.id ASC

The correct filter field is 'action_plans.deleted_at'

Change-Id: Ie05f35233f78e82bc7af2c26c9effd62ea5f86ab
Closes-Bug: #1698720
2017-06-19 16:23:05 +08:00
suzhengwei
045404095d node.status for vm_workload_consolidation
The primary usage of "node.state" is wrong, it should be 'node.status'.
So correct it and refactor the method 'get_state_str'.

Change-Id: I9004c85bfb09f9b41fc56ecb5a56122d523a661f
2017-06-16 07:04:08 +00:00
Prudhvi Rao Shedimbi
606a340b5b Noisy Neighbor Strategy
Added strategy to identify and migrate a Noisy Neighbor - a low priority VM
that negatively affects peformance of a high priority VM by over utilizing
Last Level Cache.

Partially Implements: blueprint noisy-neighbor-strategy

Change-Id: Ia13f7e91a695024410b8f3b3d3d1646a0dd687d4
2017-06-15 14:51:10 -07:00
OpenStack Proposal Bot
35fdbbe16e Updated from global requirements
Change-Id: I1fea346ba9a7f592d456101e09d00c45a91ff311
2017-06-15 16:37:35 +00:00
Yumeng_Bao
2db279e019 fix Keyerror in test_nova_cdmc
fix keyerror in watcher.tests.decision_engine.cluster.test_nova_cdmc.
TestNovaClusterDataModelCollector.test_nova_cdmc_execute

Change-Id: I44810ae539d8783173dfbb9dd462101e21025802
Closes-Bug: #1698123
2017-06-15 19:08:59 +08:00
Jenkins
986ba9872f Merge "Replace voluptuous with JSONSchema to validate migration action" 2017-06-15 06:21:05 +00:00
Jenkins
44a5a1573c Merge "Replace voluptuous with JSONSchema to validate nop action" 2017-06-14 14:59:22 +00:00
Jenkins
fe34b420a5 Merge "Remove deprecated oslo_messaging.get_transport" 2017-06-14 13:13:34 +00:00
Jenkins
43da4a6d21 Merge "Replace voluptuous with JSONSchema to validate change_nova_service_state" 2017-06-14 13:12:34 +00:00
Jenkins
7ba1bf2237 Merge "Remove log translations and hacking" 2017-06-14 07:41:50 +00:00
Jenkins
206bb413e2 Merge "fix multinode tempest test failure" 2017-06-14 02:27:25 +00:00
licanwei
1188d4263d Add action for compute node power on/off
Add action for compute node power on/off

Change-Id: I7b0c0a7500f72f49af8201547640b2322c64baff
Implements: blueprint add-power-on-off
2017-06-13 15:07:33 +08:00
Yumeng_Bao
1451d9c134 Replace voluptuous with JSONSchema to validate migration action
Now in watcher,both JSONSchema and voluptuous are used to validate
JSON payloads. We want to remove voluptuous and Use
JSONSchema as our only JSON validation tool to keep consistence
and also to make it easier to expose the validation schema
through our API in future work.

In this patch, we replace voluptuous with JSONSchema to validate
the migration action in watcher applier.

Partially Implements: blueprint jsonschema-validation

Change-Id: I02bff5db9bd06567bcc33b61a316c42c805bb20e
2017-06-12 17:40:07 +08:00
OpenStack Proposal Bot
6d739ae1d0 Updated from global requirements
Change-Id: I8efcebd4dbbaa7fd35085d6378907e5ffa0dd27b
2017-06-10 11:50:38 +00:00
Yumeng_Bao
f2751b4818 Replace voluptuous with JSONSchema to validate change_nova_service_state
Now in watcher,both JSONSchema and voluptuous are used to validate
JSON payloads. We want to remove voluptuous and Use
JSONSchema as our only JSON validation tool to keep consistence
and also to make it easier to expose the validation schema
through our API in future work.

In this patch, we replace voluptuous with JSONSchema to validate
the change_nova_service_state action in watcher applier.

Partially Implements: blueprint jsonschema-validation

Change-Id: I09a03fff96d9555024a74ba255c6951affc39de8
2017-06-10 13:51:25 +08:00
Yumeng_Bao
e2bf82607e Replace voluptuous with JSONSchema to validate resize action
Now in watcher,both JSONSchema and voluptuous are used to validate
JSON payloads. We want to remove voluptuous and Use JSONSchema
as our only JSON validation tool to keep consistence and also
to make it easier to expose the validation schema through
our API in future work.

In this patch, we replace voluptuous with JSONSchema to validate
the resize action in watcher applier.

Partially Implements: blueprint jsonschema-validation

Change-Id: I0ee4ba010a9f437658af81d5c971449aefc7f9c4
2017-06-10 13:40:29 +08:00
Yumeng_Bao
d76b5d2f7e Replace voluptuous with JSONSchema to validate sleep action
Now in watcher,both JSONSchema and voluptuous are used to validate
JSON payloads. We want to remove voluptuous and Use
JSONSchema as our only JSON validation tool to keep consistence
and also to make it easier to expose the validation schema
through our API in future work.

In this patch, we replace voluptuous with JSONSchema to validate
the sleep action in watcher applier.

Partially Implements: blueprint jsonschema-validation

Change-Id: I3032490236536a11e7045a56ad0bd40ef979407e
2017-06-10 13:22:36 +08:00
Yumeng_Bao
69730d80b2 Replace voluptuous with JSONSchema to validate nop action
Now in watcher,both JSONSchema and voluptuous are used to validate
JSON payloads. We want to remove voluptuous and Use
JSONSchema as our only JSON validation tool to keep consistence
and also to make it easier to expose the validation schema
through our API in future work.

In this patch, we replace voluptuous with JSONSchema to validate
the nop action in watcher applier.

Partially Implements: blueprint jsonschema-validation

Change-Id: Idf42b3359c36ac9480bd1f1bdd31e756214628ef
2017-06-09 13:14:29 +08:00
Ngo Quoc Cuong
17aa1122da Remove log translations and hacking
Log translation is no longer used, remove translate functions

Change-Id: Ibe205266debd6d39dab0a652a327ab19081ff27a
2017-06-08 09:48:26 +07:00
Ngo Quoc Cuong
d0f5a9fdf5 Remove deprecated oslo_messaging.get_transport
DeprecationWarning:
Using function/method 'oslo_messaging.transport.get_transport()'
is deprecated: use get_rpc_transport or get_notification_transport.

Replace get_transport with get_rpc_transport.

Change-Id: I13455842235a16463e61e5500c9e250a5cc9f86e
2017-06-08 08:34:39 +07:00
Jenkins
32dacdca8b Merge "Cancel Action Plan" 2017-06-07 14:09:32 +00:00
Jenkins
fbfb7159e3 Merge "fix clod_migrate problem" 2017-06-07 10:26:13 +00:00
licanwei
65c63a9351 fix multinode tempest test failure
fix all five tempest tests:
watcher_tempest_plugin.tests.api.admin.test_service.
TestShowListService.test_show_service

watcher_tempest_plugin.tests.api.admin.test_service.
TestShowListService.test_show_service_with_links

tearDownClass (watcher_tempest_plugin.tests.api.admin.
test_action_plan.TestCreateDeleteExecuteActionPlan)

watcher_tempest_plugin.tests.scenario.test_execute_basic_optim
.TestExecuteBasicStrategy.test_execute_basic_action_plan

watcher_tempest_plugin.tests.scenario.test_execute_workload_balancing
.TestExecuteWorkloadBalancingStrategy
.test_execute_workload_stabilization

Change-Id: I4d8945cf2dedea3fa32029d6c07d24a411c1f2e4
Closes-Bug: #1695225
2017-06-07 11:56:23 +08:00
suzhengwei
27c56a19e4 fix clod_migrate problem
1.TypeError: watcher_non_live_migrate_instance() got an unexpected keyword
  argument 'dest_hostname'
2.Conflict: Cannot 'stop' instance ****** while it is in vm_state stopped
  (HTTP 409)

Closes-Bug: #1693434

Change-Id: I7293dd5d08e33c2e534d072da8592172bc438c9e
2017-05-25 07:27:26 +00:00
166 changed files with 4564 additions and 826 deletions

9
.gitignore vendored
View File

@@ -4,8 +4,7 @@
*.so
# Packages
*.egg
*.egg-info
*.egg*
dist
build
eggs
@@ -43,9 +42,11 @@ output/*/index.html
# Sphinx
doc/build
doc/source/api
doc/source/api/*
doc/source/samples
doc/source/watcher.conf.sample
doc/source/_static/*.sample
!doc/source/api/index.rst
!doc/source/api/v1.rst
# pbr generates these
AUTHORS

View File

@@ -28,4 +28,4 @@ migration, increased energy efficiency-and more!
* Wiki: https://wiki.openstack.org/wiki/Watcher
* Source: https://github.com/openstack/watcher
* Bugs: https://bugs.launchpad.net/watcher
* Documentation: https://docs.openstack.org/developer/watcher/
* Documentation: https://docs.openstack.org/watcher/latest/

View File

@@ -235,7 +235,7 @@ function init_watcher {
recreate_database watcher
# Create watcher schema
$WATCHER_BIN_DIR/watcher-db-manage --config-file $WATCHER_CONF upgrade head
$WATCHER_BIN_DIR/watcher-db-manage --config-file $WATCHER_CONF upgrade
fi
create_watcher_cache_dir
}

View File

View File

@@ -0,0 +1,12 @@
===================
Administrator Guide
===================
.. toctree::
:maxdepth: 2
apache-mod-wsgi
gmr
policy
ways-to-install
../strategies/index

View File

@@ -4,12 +4,13 @@
https://creativecommons.org/licenses/by/3.0/
==================
Installing Watcher
==================
=======================
Ways to install Watcher
=======================
This document describes how to install Watcher in order to use it. If you are
intending to develop on or with Watcher, please read :doc:`../dev/environment`.
This document describes some ways to install Watcher in order to use it.
If you are intending to develop on or with Watcher,
please read :doc:`../contributor/environment`.
Prerequisites
-------------
@@ -77,9 +78,10 @@ Install the Watcher modules dependencies:
# pip install -r requirements.txt
From here, refer to :doc:`configuration` to declare Watcher as a new service
into Keystone and to configure its different modules. Once configured, you
should be able to run the Watcher services by issuing these commands:
From here, refer to :doc:`../configuration/configuring` to declare Watcher
as a new service into Keystone and to configure its different modules.
Once configured, you should be able to run the Watcher services by issuing
these commands:
.. code-block:: bash
@@ -107,7 +109,7 @@ installed on your system.
Once installed, you still need to declare Watcher as a new service into
Keystone and to configure its different modules, which you can find described
in :doc:`configuration`.
in :doc:`../configuration/configuring`.
Installing from packages: Debian (experimental)

4
doc/source/api/index.rst Normal file
View File

@@ -0,0 +1,4 @@
.. toctree::
:maxdepth: 1
v1

View File

@@ -20,6 +20,17 @@ Goals
.. autotype:: watcher.api.controllers.v1.goal.Goal
:members:
Strategies
==========
.. rest-controller:: watcher.api.controllers.v1.strategy:StrategiesController
:webprefix: /v1/strategies
.. autotype:: watcher.api.controllers.v1.strategy.StrategyCollection
:members:
.. autotype:: watcher.api.controllers.v1.strategy.Strategy
:members:
Audit Templates
===============

View File

@@ -31,8 +31,7 @@ sys.path.insert(0, os.path.abspath('./'))
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'oslo_config.sphinxconfiggen',
'oslosphinx',
'oslo_config.sphinxext',
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinxcontrib.httpdomain',
@@ -41,10 +40,14 @@ extensions = [
'wsmeext.sphinxext',
'ext.term',
'ext.versioned_notifications',
'oslo_config.sphinxconfiggen',
'openstackdocstheme',
]
wsme_protocols = ['restjson']
config_generator_config_file = '../../etc/watcher/watcher-config-generator.conf'
config_generator_config_file = [(
'../../etc/watcher/oslo-config-generator/watcher.conf',
'_static/watcher')]
sample_config_basename = 'watcher'
# autodoc generation is a bit aggressive and a nuisance when doing heavy
@@ -92,6 +95,8 @@ add_function_parentheses = True
# unit titles (such as .. function::).
add_module_names = True
suppress_warnings = ['app.add_directive']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
@@ -117,12 +122,20 @@ man_pages = [
# Sphinx are currently 'default' and 'sphinxdoc'.
# html_theme_path = ["."]
# html_theme = '_theme'
html_theme = 'openstackdocs'
# html_static_path = ['static']
html_theme_options = {'incubating': True}
# html_theme_options = {}
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
html_last_updated_fmt = '%Y-%m-%d %H:%M'
#openstackdocstheme options
repository_name = 'openstack/watcher'
bug_project = 'watcher'
bug_tag = ''
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).

View File

@@ -1 +0,0 @@
../../etc/watcher/watcher-config-generator.conf

View File

@@ -0,0 +1,5 @@
.. toctree::
:maxdepth: 1
configuring
watcher

View File

@@ -0,0 +1,11 @@
.. _watcher_sample_configuration_files:
------------
watcher.conf
------------
The ``watcher.conf`` file contains most of the options to configure the
Watcher services.
.. show-options::
:config-file: etc/watcher/oslo-config-generator/watcher.conf

View File

@@ -64,8 +64,9 @@ IRC Channel
``#openstack-watcher`` (changelog_)
Weekly Meetings
on Wednesdays at 14:00 UTC on even weeks, 9:00 UTC on odd weeks, in the
``#openstack-meeting-4`` IRC channel (`meetings logs`_)
On Wednesdays at 14:00 UTC on even weeks in the ``#openstack-meeting-4``
IRC channel, 13:00 UTC on odd weeks in the ``#openstack-meeting-alt``
IRC channel (`meetings logs`_)
.. _changelog: http://eavesdrop.openstack.org/irclogs/%23openstack-watcher/
.. _meetings logs: http://eavesdrop.openstack.org/meetings/watcher/

View File

@@ -17,7 +17,7 @@ To install Watcher from packaging, refer instead to Watcher `User
Documentation`_.
.. _`Git Repository`: https://git.openstack.org/cgit/openstack/watcher
.. _`User Documentation`: https://docs.openstack.org/developer/watcher/
.. _`User Documentation`: https://docs.openstack.org/watcher/latest/
Prerequisites
=============
@@ -194,7 +194,7 @@ still need to configure the following sections:
message bus
So if you need some more details on how to configure one or more of these
sections, please do have a look at :doc:`../deploy/configuration` before
sections, please do have a look at :doc:`../configuration/configuring` before
continuing.
@@ -240,7 +240,7 @@ To run the Watcher Applier service, use:
(watcher) $ watcher-applier
Default configuration of these services are available into ``/etc/watcher``
directory. See :doc:`../deploy/configuration` for details on how Watcher is
directory. See :doc:`../configuration/configuring` for details on how Watcher is
configured. By default, Watcher is configured with SQL backends.

View File

@@ -0,0 +1,8 @@
.. toctree::
:maxdepth: 1
environment
devstack
notifications
testing
rally_link

View File

@@ -123,8 +123,9 @@ Here below is how you would proceed to register ``NewGoal`` using pbr_:
new_goal = thirdparty.new:NewGoal
To get a better understanding on how to implement a more advanced goal,
have a look at the :py:class:`~.ServerConsolidation` class.
To get a better understanding on how to implement a more advanced goal, have
a look at the
:py:class:`watcher.decision_engine.goal.goals.ServerConsolidation` class.
.. _pbr: http://docs.openstack.org/developer/pbr/

View File

@@ -0,0 +1,11 @@
.. toctree::
:maxdepth: 1
base-setup
action-plugin
cdmc-plugin
goal-plugin
planner-plugin
scoring-engine-plugin
strategy-plugin
plugins

View File

@@ -37,7 +37,8 @@ Create a new scoring engine plugin
In order to create a new scoring engine you have to:
- Extend the :py:class:`~.ScoringEngine` class
- Extend the :py:class:`watcher.decision_engine.scoring.base.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
@@ -124,7 +125,8 @@ scoring engine deployed as a web service on external servers:
Abstract Plugin Class
=====================
Here below is the abstract :py:class:`~.ScoringEngine` class:
Here below is the abstract
:py:class:`watcher.decision_engine.scoring.base.ScoringEngine` class:
.. autoclass:: watcher.decision_engine.scoring.base.ScoringEngine
:members:

View File

@@ -1,14 +0,0 @@
.. _watcher_sample_configuration_files:
==================================
Watcher sample configuration files
==================================
watcher.conf
~~~~~~~~~~~~
The ``watcher.conf`` file contains most of the options to configure the
Watcher services.
.. literalinclude:: ../watcher.conf.sample
:language: ini

View File

@@ -65,6 +65,14 @@ Audit
.. _audit_template_definition:
Audit Scope
===========
An Audit Scope is a set of audited resources. Audit Scope should be defined
in each Audit Template (which contains the Audit settings).
.. _audit_scope_definition:
Audit Template
==============

View File

@@ -13,7 +13,7 @@
height="210mm"
id="svg4946"
version="1.1"
inkscape:version="0.48.4 r9939"
inkscape:version="0.91 r13725"
sodipodi:docname="architecture.svg">
<defs
id="defs4948">
@@ -325,6 +325,20 @@
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.8,0,0,-0.8,4.8,0)" />
</marker>
<marker
inkscape:stockid="EmptyTriangleInL"
orient="auto"
refY="0"
refX="0"
id="EmptyTriangleInL-5"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path7091-3"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.8,0,0,-0.8,4.8,0)" />
</marker>
</defs>
<sodipodi:namedview
inkscape:document-units="mm"
@@ -333,17 +347,21 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:zoom="1.4142136"
inkscape:cx="261.24633"
inkscape:cy="108.90512"
inkscape:current-layer="g5356"
id="namedview4950"
showgrid="false"
inkscape:window-width="1846"
inkscape:window-height="1053"
inkscape:window-x="1911"
inkscape:window-y="37"
inkscape:window-maximized="1" />
showgrid="true"
inkscape:window-width="1215"
inkscape:window-height="776"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid4203" />
</sodipodi:namedview>
<metadata
id="metadata4952">
<rdf:RDF>
@@ -410,7 +428,7 @@
sodipodi:cy="358.94418"
sodipodi:rx="70.677063"
sodipodi:ry="70.677063"
d="m 394.67978,358.94418 c 0,39.03387 -31.6432,70.67707 -70.67706,70.67707 -39.03387,0 -70.67707,-31.6432 -70.67707,-70.67707 0,-39.03386 31.6432,-70.67706 70.67707,-70.67706 39.03386,0 70.67706,31.6432 70.67706,70.67706 z"
d="m 394.67978,358.94418 a 70.677063,70.677063 0 0 1 -70.67706,70.67707 70.677063,70.677063 0 0 1 -70.67707,-70.67707 70.677063,70.677063 0 0 1 70.67707,-70.67706 70.677063,70.677063 0 0 1 70.67706,70.67706 z"
transform="matrix(0.36664048,0,0,0.36664048,0.86684619,-80.697844)" />
<g
id="g5262">
@@ -536,29 +554,29 @@
</g>
<rect
y="754.5235"
x="225.29219"
x="231.29219"
height="42.939091"
width="118.56741"
id="rect4267-4-7-7-3-3"
style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-opacity:1;display:inline" />
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:1;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text5037-4-6-9-1-4"
y="783.03412"
x="284.34656"
style="font-size:20.86613655px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
x="290.34656"
style="font-style:normal;font-weight:normal;font-size:20.86613655px;line-height:125%;font-family:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"><tspan
id="tspan5184-3-5-2-1"
style="font-size:20px;text-align:center;text-anchor:middle"
y="783.03412"
x="284.34656"
x="290.34656"
sodipodi:role="line">ceilometer</tspan></text>
<g
transform="matrix(1.7775787,0,0,1.7775787,593.58186,304.14299)"
transform="matrix(1.7775787,0,0,1.7775787,599.58186,304.14299)"
id="g4982"
style="display:inline">
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:0.562563;stroke-opacity:1;display:inline"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:0.562563;stroke-opacity:1"
id="rect4267-4-7-7-3-3-1"
width="66.701637"
height="24.155943"
@@ -566,7 +584,7 @@
y="253.36743" />
<text
xml:space="preserve"
style="font-size:11.73851585px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
style="font-style:normal;font-weight:normal;font-size:11.73851585px;line-height:125%;font-family:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none"
x="-100.86156"
y="268.36258"
id="text5037-4-6-9-1-4-3"
@@ -620,12 +638,6 @@
style="font-size:11.2512598px;text-align:center;text-anchor:middle"
id="tspan5022">drivers</tspan></text>
</g>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path5110"
d="m 376.75141,726.9703 -57.95106,0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline" />
<g
transform="matrix(1.7775787,0,0,1.7775787,704.59677,780.35846)"
id="g5267"
@@ -1052,8 +1064,8 @@
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path5112-89"
d="m 328.87061,655.34778 0,71.7093"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline" />
d="m 324.87061,655.34778 0,71.7093"
style="display:inline;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
@@ -1178,7 +1190,7 @@
sodipodi:role="line">extensions</tspan></text>
<path
transform="translate(68.397849,130.15566)"
d="m 519.41589,367.58817 c 0,4.01434 -2.01125,7.2686 -4.49224,7.2686 -2.481,0 -4.49225,-3.25426 -4.49225,-7.2686 0,-4.01434 2.01125,-7.26861 4.49225,-7.26861 2.48099,0 4.49224,3.25427 4.49224,7.26861 z"
d="m 519.41589,367.58817 a 4.4922457,7.2686057 0 0 1 -4.49224,7.2686 4.4922457,7.2686057 0 0 1 -4.49225,-7.2686 4.4922457,7.2686057 0 0 1 4.49225,-7.26861 4.4922457,7.2686057 0 0 1 4.49224,7.26861 z"
sodipodi:ry="7.2686057"
sodipodi:rx="4.4922457"
sodipodi:cy="367.58817"
@@ -1188,7 +1200,7 @@
sodipodi:type="arc" />
<path
transform="matrix(0.36538461,0,0,0.36538461,397.30905,368.17877)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1198,7 +1210,7 @@
sodipodi:type="arc" />
<path
transform="matrix(0.36538461,0,0,0.36538461,-27.122619,99.964199)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1214,7 +1226,7 @@
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:3.99999997, 1.99999998, 0.99999999, 1.99999998;stroke-dashoffset:0;marker-start:none;display:inline" />
<path
transform="matrix(0.36538461,0,0,0.36538461,396.74123,268.71562)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1224,7 +1236,7 @@
sodipodi:type="arc" />
<path
transform="matrix(0.36538461,0,0,0.36538461,428.69747,559.36511)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1234,7 +1246,7 @@
sodipodi:type="arc" />
<path
transform="matrix(0.36538461,0,0,0.36538461,531.88959,559.36511)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1250,7 +1262,7 @@
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1.99999998, 1.99999998;stroke-dashoffset:0;marker-start:none;display:inline" />
<path
transform="matrix(0.36538461,0,0,0.36538461,748.91653,525.25993)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1260,7 +1272,7 @@
sodipodi:type="arc" />
<path
transform="matrix(0.36538461,0,0,0.36538461,748.91653,495.84628)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1313,7 +1325,7 @@
</g>
<path
transform="matrix(0.36538461,0,0,0.36538461,396.74122,268.71562)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1323,7 +1335,7 @@
sodipodi:type="arc" />
<path
transform="matrix(0.36538461,0,0,0.36538461,-27.122619,74.87915)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1339,7 +1351,7 @@
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:3.99999997, 1.99999998, 0.99999999, 1.99999998;stroke-dashoffset:0;marker-start:url(#TriangleInL);display:inline" />
<path
transform="matrix(0.36538461,0,0,0.36538461,422.9095,223.59883)"
d="m 521.38126,368.43045 c 0,4.03162 -3.26828,7.2999 -7.2999,7.2999 -4.03162,0 -7.2999,-3.26828 -7.2999,-7.2999 0,-4.03162 3.26828,-7.2999 7.2999,-7.2999 4.03162,0 7.2999,3.26828 7.2999,7.2999 z"
d="m 521.38126,368.43045 a 7.2998991,7.2998991 0 0 1 -7.2999,7.2999 7.2998991,7.2998991 0 0 1 -7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,-7.2999 7.2998991,7.2998991 0 0 1 7.2999,7.2999 z"
sodipodi:ry="7.2998991"
sodipodi:rx="7.2998991"
sodipodi:cy="368.43045"
@@ -1351,15 +1363,45 @@
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path3284-4-2-3-40"
d="m 319.30136,753.47677 0,-27.00021"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL);display:inline" />
d="m 325.23661,753.47677 0,-27.00021"
style="display:inline;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL)" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path3284-4-2-3-8"
d="m 376.2546,753.65849 0,-27.00021"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL);display:inline" />
d="m 414.2546,753.65849 0,-27.00021"
style="display:inline;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL)" />
<rect
y="754.5235"
x="102.92204"
height="42.939091"
width="118.56741"
id="rect4267-4-7-7-3-3-3"
style="display:inline;fill:#ffffff;stroke:#000000;stroke-width:1;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text5037-4-6-9-1-4-6"
y="783.03412"
x="160.34656"
style="font-style:normal;font-weight:normal;font-size:20.86613655px;line-height:125%;font-family:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"><tspan
id="tspan5184-3-5-2-1-7"
style="font-size:20px;text-align:center;text-anchor:middle"
y="783.03412"
x="160.34656"
sodipodi:role="line">gnocchi</tspan></text>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path3284-4-2-3-40-5"
d="m 191.30136,753.47677 0,-27.00021"
style="display:inline;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#EmptyTriangleInL-5)" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path5110-9-6"
d="m 192.18905,726.66568 221.85496,0"
style="display:inline;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -43,7 +43,7 @@ Introduction
glossary
architecture
dev/contributing
contributor/contributing
Getting Started
@@ -52,14 +52,7 @@ Getting Started
.. toctree::
:maxdepth: 1
dev/environment
dev/devstack
deploy/configuration
deploy/conf-files
deploy/apache-mod-wsgi
dev/notifications
dev/testing
dev/rally_link
contributor/index
API References
--------------
@@ -67,7 +60,7 @@ API References
.. toctree::
:maxdepth: 1
webapi/v1
api/index
Plugins
-------
@@ -75,30 +68,38 @@ Plugins
.. toctree::
:maxdepth: 1
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
dev/plugin/planner-plugin
dev/plugins
contributor/plugin/index
Installation
============
.. toctree::
:maxdepth: 2
install/index
Watcher Configuration Options
=============================
.. toctree::
:maxdepth: 2
configuration/index
Admin Guide
===========
Introduction
------------
.. toctree::
:maxdepth: 2
admin/index
User Guide
==========
.. toctree::
:maxdepth: 1
:maxdepth: 2
deploy/installation
deploy/user-guide
deploy/policy
deploy/gmr
strategies/strategies
user/index
Watcher Manual Pages
====================
@@ -107,7 +108,7 @@ Watcher Manual Pages
:glob:
:maxdepth: 1
man/*
man/index
.. # NOTE(mriedem): This is the section where we hide things that we don't
# actually want in the table of contents but sphinx build would fail if

View File

@@ -15,13 +15,13 @@ you must create a database, service credentials, and API endpoints.
* Create the ``watcher`` database:
.. code-block:: none
.. code-block:: console
CREATE DATABASE watcher CHARACTER SET utf8;
* Grant proper access to the ``watcher`` database:
.. code-block:: none
.. code-block:: console
GRANT ALL PRIVILEGES ON watcher.* TO 'watcher'@'localhost' \
IDENTIFIED BY 'WATCHER_DBPASS';
@@ -32,7 +32,7 @@ you must create a database, service credentials, and API endpoints.
* Exit the database access client.
.. code-block:: none
.. code-block:: console
exit;

View File

@@ -23,15 +23,15 @@ of data center operating costs, increased system performance
via intelligent virtual machine migration, increased energy
efficiency—and more!
watcher also supports a pluggable architecture by which custom
Watcher also supports a pluggable architecture by which custom
optimization algorithms, data metrics and data profilers can be
developed and inserted into the Watcher framework.
check the documentation for watcher optimization strategies at
https://docs.openstack.org/developer/watcher/strategies
Check the documentation for watcher optimization strategies at
https://docs.openstack.org/watcher/latest/strategies/index.html
check watcher glossary at
https://docs.openstack.org/developer/watcher/glossary.html
Check watcher glossary at
https://docs.openstack.org/watcher/latest/glossary.html
This chapter assumes a working setup of OpenStack following the

View File

@@ -4,8 +4,9 @@
Install and configure for openSUSE and SUSE Linux Enterprise
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This section describes how to install and configure the Infrastructure Optimization service
for openSUSE Leap 42.1 and SUSE Linux Enterprise Server 12 SP1.
This section describes how to install and configure the Infrastructure
Optimization service for openSUSE Leap 42.1 and
SUSE Linux Enterprise Server 12 SP1.
.. include:: common_prerequisites.rst

View File

@@ -4,8 +4,8 @@ Install and configure for Red Hat Enterprise Linux and CentOS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This section describes how to install and configure the Infrastructure Optimization service
for Red Hat Enterprise Linux 7 and CentOS 7.
This section describes how to install and configure the Infrastructure
Optimization service for Red Hat Enterprise Linux 7 and CentOS 7.
.. include:: common_prerequisites.rst

View File

@@ -3,8 +3,8 @@
Install and configure for Ubuntu
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This section describes how to install and configure the Infrastructure Optimization
service for Ubuntu 14.04 (LTS).
This section describes how to install and configure the Infrastructure
Optimization service for Ubuntu 14.04 (LTS).
.. include:: common_prerequisites.rst

View File

@@ -3,8 +3,8 @@
Install and configure
~~~~~~~~~~~~~~~~~~~~~
This section describes how to install and configure the
Infrastructure Optimization service, code-named watcher, on the controller node.
This section describes how to install and configure the Infrastructure
Optimization service, code-named watcher, on the controller node.
This section assumes that you already have a working OpenStack
environment with at least the following components installed:

8
doc/source/man/index.rst Normal file
View File

@@ -0,0 +1,8 @@
.. toctree::
:glob:
:maxdepth: 1
watcher-api
watcher-applier
watcher-db-manage
watcher-decision-engine

View File

@@ -57,7 +57,7 @@ Planner
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
.. watcher-term:: watcher.decision_engine.planner.weight.WeightPlanner
Configuration
-------------

View File

@@ -61,7 +61,7 @@ Planner
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
.. watcher-term:: watcher.decision_engine.planner.weight.WeightPlanner
Configuration
-------------

View File

@@ -59,7 +59,7 @@ Planner
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
.. watcher-term:: watcher.decision_engine.planner.weight.WeightPlanner
Configuration
-------------

View File

@@ -67,7 +67,7 @@ Planner
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
.. watcher-term:: watcher.decision_engine.planner.weight.WeightPlanner
Configuration

View File

@@ -58,7 +58,7 @@ Planner
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
.. watcher-term:: watcher.decision_engine.planner.weight.WeightPlanner
Configuration
-------------

View File

@@ -56,7 +56,7 @@ Planner
Default Watcher's planner:
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
.. watcher-term:: watcher.decision_engine.planner.weight.WeightPlanner
Configuration
-------------

View File

@@ -0,0 +1,4 @@
.. toctree::
:maxdepth: 2
user-guide

View File

@@ -11,7 +11,7 @@ Watcher User Guide
==================
See the
`architecture page <http://docs.openstack.org/developer/watcher/architecture.html>`_
`architecture page <https://docs.openstack.org/watcher/latest/architecture.html>`_
for an architectural overview of the different components of Watcher and how
they fit together.

View File

@@ -1,301 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import os
# import sys
import openstackdocstheme
# 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('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
# TODO(ajaeger): enable PDF building, for example add 'rst2pdf.pdfbuilder'
# extensions =
# Add any paths that contain templates here, relative to this directory.
# templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Installation Guide for Infrastructure Optimization Service'
bug_tag = u'install-guide'
copyright = u'2016, OpenStack contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.1'
# The full version, including alpha/beta/rc tags.
release = '0.1'
# A few variables have to be set for the log-a-bug feature.
# giturl: The location of conf.py on Git. Must be set manually.
# gitsha: The SHA checksum of the bug description. Automatically extracted
# from git log.
# bug_tag: Tag for categorizing the bug. Must be set manually.
# These variables are passed to the logabug code via html_context.
giturl = u'http://git.openstack.org/cgit/openstack/watcher/tree/install-guide/source' # noqa
git_cmd = "/usr/bin/git log | head -n1 | cut -f2 -d' '"
gitsha = os.popen(git_cmd).read().strip('\n')
html_context = {"gitsha": gitsha, "bug_tag": bug_tag,
"giturl": giturl,
"bug_project": "watcher"}
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ["common_prerequisites.rst", "common_configure.rst"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'openstackdocs'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = [openstackdocstheme.get_html_theme_path()]
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = []
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# So that we can enable "log-a-bug" links from each output HTML page, this
# variable must be set to a format that includes year, month, day, hours and
# minutes.
html_last_updated_fmt = '%Y-%m-%d %H:%M'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}
# If false, no module index is generated.
# html_domain_indices = True
# If false, no index is generated.
html_use_index = False
# If true, the index is split into individual pages for each letter.
# html_split_index = False
# If true, links to the reST sources are added to the pages.
html_show_sourcelink = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'install-guide'
# If true, publish source files
html_copy_source = False
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'InstallGuide.tex', u'Install Guide',
u'OpenStack contributors', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False
# If true, show page references after internal links.
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
# latex_appendices = []
# If false, no module index is generated.
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'installguide', u'Install Guide',
[u'OpenStack contributors'], 1)
]
# If true, show URL addresses after external links.
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'InstallGuide', u'Install Guide',
u'OpenStack contributors', 'InstallGuide',
'This guide shows OpenStack end users how to install '
'an OpenStack cloud.', 'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
# texinfo_appendices = []
# If false, no module index is generated.
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
# texinfo_no_detailmenu = False
# -- Options for Internationalization output ------------------------------
locale_dirs = ['locale/']
# -- Options for PDF output --------------------------------------------------
pdf_documents = [
('index', u'InstallGuide', u'Install Guide',
u'OpenStack contributors')
]

View File

@@ -0,0 +1,3 @@
---
features:
- Add action for compute node power on/off

View File

@@ -0,0 +1,4 @@
---
features:
- |
Added cinder cluster data model

View File

@@ -38,7 +38,7 @@ from watcher import version as watcher_version
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['reno.sphinxext',
'oslosphinx']
'openstackdocstheme']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -104,7 +104,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = 'openstackdocs'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the

View File

@@ -5,20 +5,22 @@
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.20.0 # Apache-2.0
keystoneauth1>=3.0.1 # Apache-2.0
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
keystonemiddleware>=4.12.0 # Apache-2.0
lxml!=3.7.0,>=2.3 # BSD
croniter>=0.3.4 # MIT License
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.cache>=1.5.0 # Apache-2.0
oslo.config>=4.0.0 # Apache-2.0
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
oslo.context>=2.14.0 # Apache-2.0
oslo.db>=4.21.1 # Apache-2.0
oslo.db>=4.24.0 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
oslo.messaging!=5.25.0,>=5.24.2 # Apache-2.0
oslo.policy>=1.17.0 # Apache-2.0
oslo.policy>=1.23.0 # Apache-2.0
oslo.reports>=0.6.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
oslo.service>=1.10.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
oslo.versionedobjects>=1.17.0 # Apache-2.0
@@ -29,14 +31,14 @@ PrettyTable<0.8,>=0.7.1 # BSD
voluptuous>=0.8.9 # BSD License
gnocchiclient>=2.7.0 # Apache-2.0
python-ceilometerclient>=2.5.0 # Apache-2.0
python-cinderclient>=2.1.0 # Apache-2.0
python-cinderclient>=3.0.0 # Apache-2.0
python-glanceclient>=2.7.0 # Apache-2.0
python-keystoneclient>=3.8.0 # Apache-2.0
python-monascaclient>=1.1.0 # Apache-2.0
python-neutronclient>=6.3.0 # Apache-2.0
python-novaclient>=7.1.0 # Apache-2.0
python-novaclient>=9.0.0 # Apache-2.0
python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0
python-ironicclient>=1.11.0 # Apache-2.0
python-ironicclient>=1.14.0 # Apache-2.0
six>=1.9.0 # MIT
SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT
stevedore>=1.20.0 # Apache-2.0

View File

@@ -5,7 +5,7 @@ description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://docs.openstack.org/developer/watcher/
home-page = https://docs.openstack.org/watcher/latest/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -53,6 +53,7 @@ watcher_goals =
thermal_optimization = watcher.decision_engine.goal.goals:ThermalOptimization
workload_balancing = watcher.decision_engine.goal.goals:WorkloadBalancing
airflow_optimization = watcher.decision_engine.goal.goals:AirflowOptimization
noisy_neighbor = watcher.decision_engine.goal.goals:NoisyNeighborOptimization
watcher_scoring_engines =
dummy_scorer = watcher.decision_engine.scoring.dummy_scorer:DummyScorer
@@ -70,6 +71,7 @@ watcher_strategies =
workload_stabilization = watcher.decision_engine.strategy.strategies.workload_stabilization:WorkloadStabilization
workload_balance = watcher.decision_engine.strategy.strategies.workload_balance:WorkloadBalance
uniform_airflow = watcher.decision_engine.strategy.strategies.uniform_airflow:UniformAirflow
noisy_neighbor = watcher.decision_engine.strategy.strategies.noisy_neighbor:NoisyNeighbor
watcher_actions =
migrate = watcher.applier.actions.migration:Migrate
@@ -77,6 +79,7 @@ watcher_actions =
sleep = watcher.applier.actions.sleep:Sleep
change_nova_service_state = watcher.applier.actions.change_nova_service_state:ChangeNovaServiceState
resize = watcher.applier.actions.resize:Resize
change_node_power_state = watcher.applier.actions.change_node_power_state:ChangeNodePowerState
watcher_workflow_engines =
taskflow = watcher.applier.workflow_engine.default:DefaultWorkFlowEngine
@@ -87,6 +90,8 @@ watcher_planners =
watcher_cluster_data_model_collectors =
compute = watcher.decision_engine.model.collector.nova:NovaClusterDataModelCollector
storage = watcher.decision_engine.model.collector.cinder:CinderClusterDataModelCollector
[pbr]
warnerrors = true
@@ -104,6 +109,7 @@ source-dir = doc/source
build-dir = doc/build
fresh_env = 1
all_files = 1
warning-is-error = 1
[upload_sphinx]
upload-dir = doc/build/html

View File

@@ -15,9 +15,8 @@ testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
# Doc requirements
openstackdocstheme>=1.5.0 # Apache-2.0
oslosphinx>=4.7.0 # Apache-2.0
sphinx!=1.6.1,>=1.5.1 # BSD
openstackdocstheme>=1.11.0 # Apache-2.0
sphinx>=1.6.2 # BSD
sphinxcontrib-pecanwsme>=0.8 # Apache-2.0

View File

@@ -15,6 +15,7 @@ commands =
rm -f .testrepository/times.dbm
find . -type f -name "*.py[c|o]" -delete
ostestr --concurrency=6 {posargs}
passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
[testenv:pep8]
commands =
@@ -43,12 +44,13 @@ commands = oslo_debug_helper -t watcher/tests {posargs}
[testenv:genconfig]
sitepackages = False
commands =
oslo-config-generator --config-file etc/watcher/watcher-config-generator.conf
oslo-config-generator --config-file etc/watcher/oslo-config-generator/watcher.conf
[flake8]
show-source=True
ignore= H105,E123,E226,N320
ignore= H105,E123,E226,N320,H202
builtins= _
enable-extensions = H106,H203
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,*sqlalchemy/alembic/versions/*,demo/,releasenotes
[testenv:wheel]
@@ -69,6 +71,3 @@ 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 -s B320
[testenv:install-guide]
commands = sphinx-build -a -E -W -d install-guide/build/doctrees -b html install-guide/source install-guide/build/html

View File

@@ -32,16 +32,6 @@ _C = _translators.contextual_form
# The plural translation function using the name "_P"
_P = _translators.plural_form
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
def lazy_translation_enabled():
return _lazy.USE_LAZY

View File

@@ -65,7 +65,7 @@ class AuditPostType(wtypes.Base):
parameters = wtypes.wsattr({wtypes.text: types.jsontype}, mandatory=False,
default={})
interval = wsme.wsattr(int, mandatory=False)
interval = wsme.wsattr(types.interval_or_cron, mandatory=False)
scope = wtypes.wsattr(types.jsontype, readonly=True)
@@ -261,7 +261,7 @@ class Audit(base.APIBase):
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link and associated audit links"""
interval = wsme.wsattr(int, mandatory=False)
interval = wsme.wsattr(wtypes.text, mandatory=False)
"""Launch audit periodically (in seconds)"""
scope = wsme.wsattr(types.jsontype, mandatory=False)
@@ -270,6 +270,9 @@ class Audit(base.APIBase):
auto_trigger = wsme.wsattr(bool, mandatory=False, default=False)
"""Autoexecute action plan once audit is succeeded"""
next_run_time = wsme.wsattr(datetime.datetime, mandatory=False)
"""The next time audit launch"""
def __init__(self, **kwargs):
self.fields = []
fields = list(objects.Audit.fields)
@@ -301,7 +304,8 @@ class Audit(base.APIBase):
audit.unset_fields_except(['uuid', 'audit_type', 'state',
'goal_uuid', 'interval', 'scope',
'strategy_uuid', 'goal_name',
'strategy_name', 'auto_trigger'])
'strategy_name', 'auto_trigger',
'next_run_time'])
audit.links = [link.Link.make_link('self', url,
'audits', audit.uuid),
@@ -325,9 +329,10 @@ class Audit(base.APIBase):
created_at=datetime.datetime.utcnow(),
deleted_at=None,
updated_at=datetime.datetime.utcnow(),
interval=7200,
interval='7200',
scope=[],
auto_trigger=False)
auto_trigger=False,
next_run_time=datetime.datetime.utcnow())
sample.goal_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
sample.strategy_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ff'

View File

@@ -550,7 +550,7 @@ class AuditTemplatesController(rest.RestController):
def get_one(self, audit_template):
"""Retrieve information about the given audit template.
:param audit audit_template: UUID or name of an audit template.
:param audit_template: UUID or name of an audit template.
"""
if self.from_audit_templates:
raise exception.OperationNotPermitted
@@ -597,7 +597,7 @@ class AuditTemplatesController(rest.RestController):
def patch(self, audit_template, patch):
"""Update an existing audit template.
:param audit template_uuid: UUID of a audit template.
:param template_uuid: UUID of a audit template.
:param patch: a json PATCH document to apply to this audit template.
"""
if self.from_audit_templates:
@@ -645,7 +645,7 @@ class AuditTemplatesController(rest.RestController):
def delete(self, audit_template):
"""Delete a audit template.
:param audit template_uuid: UUID or name of an audit template.
:param template_uuid: UUID or name of an audit template.
"""
context = pecan.request.context
audit_template_to_delete = api_utils.get_resource('AuditTemplate',

View File

@@ -43,6 +43,28 @@ class UuidOrNameType(wtypes.UserType):
return UuidOrNameType.validate(value)
class IntervalOrCron(wtypes.UserType):
"""A simple int value or cron syntax type"""
basetype = wtypes.text
name = 'interval_or_cron'
@staticmethod
def validate(value):
if not (utils.is_int_like(value) or utils.is_cron_like(value)):
raise exception.InvalidIntervalOrCron(name=value)
return value
@staticmethod
def frombasetype(value):
if value is None:
return None
return IntervalOrCron.validate(value)
interval_or_cron = IntervalOrCron()
class NameType(wtypes.UserType):
"""A simple logical name type."""

View File

@@ -21,7 +21,6 @@ from oslo_log import log
from oslo_utils import timeutils
import six
from watcher._i18n import _LW
from watcher.common import context as watcher_context
from watcher.common import scheduling
from watcher import notifications
@@ -67,9 +66,8 @@ class APISchedulingService(scheduling.BackgroundSchedulerService):
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'),
LOG.warning('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)})

View File

@@ -18,6 +18,7 @@
import abc
import jsonschema
import six
from watcher.common import clients
@@ -130,8 +131,11 @@ class BaseAction(loadable.Loadable):
raise NotImplementedError()
def validate_parameters(self):
self.schema(self.input_parameters)
return True
try:
jsonschema.validate(self.input_parameters, self.schema)
return True
except jsonschema.ValidationError as e:
raise e
@abc.abstractmethod
def get_description(self):
@@ -139,4 +143,10 @@ class BaseAction(loadable.Loadable):
raise NotImplementedError()
def check_abort(self):
return bool(self.__class__.__name__ in self.ABORT_TRUE)
if self.__class__.__name__ is 'Migrate':
if self.migration_type == self.LIVE_MIGRATION:
return True
else:
return False
else:
return bool(self.__class__.__name__ in self.ABORT_TRUE)

View File

@@ -0,0 +1,118 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2017 ZTE
#
# Authors: Li Canwei
#
# 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 enum
from watcher._i18n import _
from watcher.applier.actions import base
from watcher.common import exception
class NodeState(enum.Enum):
POWERON = 'on'
POWEROFF = 'off'
class ChangeNodePowerState(base.BaseAction):
"""Compute node power on/off
By using this action, you will be able to on/off the power of a
compute node.
The action schema is::
schema = Schema({
'resource_id': str,
'state': str,
})
The `resource_id` references a ironic node id (list of available
ironic node is returned by this command: ``ironic node-list``).
The `state` value should either be `on` or `off`.
"""
STATE = 'state'
@property
def schema(self):
return {
'type': 'object',
'properties': {
'resource_id': {
'type': 'string',
"minlength": 1
},
'state': {
'type': 'string',
'enum': [NodeState.POWERON.value,
NodeState.POWEROFF.value]
}
},
'required': ['resource_id', 'state'],
'additionalProperties': False,
}
@property
def node_uuid(self):
return self.resource_id
@property
def state(self):
return self.input_parameters.get(self.STATE)
def execute(self):
target_state = self.state
return self._node_manage_power(target_state)
def revert(self):
if self.state == NodeState.POWERON.value:
target_state = NodeState.POWEROFF.value
elif self.state == NodeState.POWEROFF.value:
target_state = NodeState.POWERON.value
return self._node_manage_power(target_state)
def _node_manage_power(self, state):
if state is None:
raise exception.IllegalArgumentException(
message=_("The target state is not defined"))
result = False
ironic_client = self.osc.ironic()
nova_client = self.osc.nova()
if state == NodeState.POWEROFF.value:
node_info = ironic_client.node.get(self.node_uuid).to_dict()
compute_node_id = node_info['extra']['compute_node_id']
compute_node = nova_client.hypervisors.get(compute_node_id)
compute_node = compute_node.to_dict()
if (compute_node['running_vms'] == 0):
result = ironic_client.node.set_power_state(
self.node_uuid, state)
else:
result = ironic_client.node.set_power_state(self.node_uuid, state)
return result
def pre_condition(self):
pass
def post_condition(self):
pass
def get_description(self):
"""Description of the action"""
return ("Compute node power on/off through ironic.")

View File

@@ -17,9 +17,6 @@
# limitations under the License.
#
import six
import voluptuous
from watcher._i18n import _
from watcher.applier.actions import base
from watcher.common import exception
@@ -51,15 +48,24 @@ class ChangeNovaServiceState(base.BaseAction):
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.RESOURCE_ID):
voluptuous.All(
voluptuous.Any(*six.string_types),
voluptuous.Length(min=1)),
voluptuous.Required(self.STATE):
voluptuous.Any(*[state.value
for state in list(element.ServiceState)]),
})
return {
'type': 'object',
'properties': {
'resource_id': {
'type': 'string',
"minlength": 1
},
'state': {
'type': 'string',
'enum': [element.ServiceState.ONLINE.value,
element.ServiceState.OFFLINE.value,
element.ServiceState.ENABLED.value,
element.ServiceState.DISABLED.value]
}
},
'required': ['resource_id', 'state'],
'additionalProperties': False,
}
@property
def host(self):

View File

@@ -17,15 +17,12 @@
# limitations under the License.
#
from oslo_log import log
import six
import voluptuous
from oslo_log import log
from watcher._i18n import _
from watcher.applier.actions import base
from watcher.common import exception
from watcher.common import nova_helper
from watcher.common import utils
LOG = log.getLogger(__name__)
@@ -62,28 +59,36 @@ class Migrate(base.BaseAction):
DESTINATION_NODE = 'destination_node'
SOURCE_NODE = 'source_node'
def check_resource_id(self, value):
if (value is not None and
len(value) > 0 and not
utils.is_uuid_like(value)):
raise voluptuous.Invalid(_("The parameter "
"resource_id is invalid."))
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.RESOURCE_ID): self.check_resource_id,
voluptuous.Required(
self.MIGRATION_TYPE, default=self.LIVE_MIGRATION):
voluptuous.Any(
*[self.LIVE_MIGRATION, self.COLD_MIGRATION]),
voluptuous.Required(self.DESTINATION_NODE):
voluptuous.All(voluptuous.Any(*six.string_types),
voluptuous.Length(min=1)),
voluptuous.Required(self.SOURCE_NODE):
voluptuous.All(voluptuous.Any(*six.string_types),
voluptuous.Length(min=1)),
})
return {
'type': 'object',
'properties': {
'destination_node': {
"anyof": [
{'type': 'string', "minLength": 1},
{'type': 'None'}
]
},
'migration_type': {
'type': 'string',
"enum": ["live", "cold"]
},
'resource_id': {
'type': 'string',
"minlength": 1,
"pattern": ("^([a-fA-F0-9]){8}-([a-fA-F0-9]){4}-"
"([a-fA-F0-9]){4}-([a-fA-F0-9]){4}-"
"([a-fA-F0-9]){12}$")
},
'source_node': {
'type': 'string',
"minLength": 1
}
},
'required': ['migration_type', 'resource_id', 'source_node'],
'additionalProperties': False,
}
@property
def instance_uuid(self):
@@ -137,13 +142,28 @@ class Migrate(base.BaseAction):
LOG.critical("Unexpected error occurred. Migration failed for "
"instance %s. Leaving instance on previous "
"host.", self.instance_uuid)
return result
def migrate(self, destination):
def _abort_cold_migrate(self, nova):
# TODO(adisky): currently watcher uses its own version of cold migrate
# implement cold migrate using nova dependent on the blueprint
# https://blueprints.launchpad.net/nova/+spec/cold-migration-with-target
# Abort operation for cold migrate is dependent on blueprint
# https://blueprints.launchpad.net/nova/+spec/abort-cold-migration
LOG.warning("Abort operation for cold migration is not implemented")
def _abort_live_migrate(self, nova, source, destination):
return nova.abort_live_migrate(instance_id=self.instance_uuid,
source=source, destination=destination)
def migrate(self, destination=None):
nova = nova_helper.NovaHelper(osc=self.osc)
LOG.debug("Migrate instance %s to %s", self.instance_uuid,
destination)
if destination is None:
LOG.debug("Migrating instance %s, destination node will be "
"determined by nova-scheduler", self.instance_uuid)
else:
LOG.debug("Migrate instance %s to %s", self.instance_uuid,
destination)
instance = nova.find_instance(self.instance_uuid)
if instance:
if self.migration_type == self.LIVE_MIGRATION:
@@ -165,8 +185,17 @@ class Migrate(base.BaseAction):
return self.migrate(destination=self.source_node)
def abort(self):
# TODO(adisky): implement abort for migration
LOG.warning("Abort for migration not implemented")
nova = nova_helper.NovaHelper(osc=self.osc)
instance = nova.find_instance(self.instance_uuid)
if instance:
if self.migration_type == self.COLD_MIGRATION:
return self._abort_cold_migrate(nova)
elif self.migration_type == self.LIVE_MIGRATION:
return self._abort_live_migrate(
nova, source=self.source_node,
destination=self.destination_node)
else:
raise exception.InstanceNotFound(name=self.instance_uuid)
def pre_condition(self):
# TODO(jed): check if the instance exists / check if the instance is on

View File

@@ -18,8 +18,6 @@
#
from oslo_log import log
import six
import voluptuous
from watcher.applier.actions import base
@@ -42,10 +40,16 @@ class Nop(base.BaseAction):
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.MESSAGE): voluptuous.Any(
voluptuous.Any(*six.string_types), None)
})
return {
'type': 'object',
'properties': {
'message': {
'type': ['string', 'null']
}
},
'required': ['message'],
'additionalProperties': False,
}
@property
def message(self):
@@ -71,3 +75,4 @@ class Nop(base.BaseAction):
def abort(self):
LOG.debug("Abort action NOP")
return True

View File

@@ -18,13 +18,9 @@
#
from oslo_log import log
import six
import voluptuous
from watcher._i18n import _
from watcher.applier.actions import base
from watcher.common import nova_helper
from watcher.common import utils
LOG = log.getLogger(__name__)
@@ -49,21 +45,26 @@ class Resize(base.BaseAction):
# input parameters constants
FLAVOR = 'flavor'
def check_resource_id(self, value):
if (value is not None and
len(value) > 0 and not
utils.is_uuid_like(value)):
raise voluptuous.Invalid(_("The parameter "
"resource_id is invalid."))
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.RESOURCE_ID): self.check_resource_id,
voluptuous.Required(self.FLAVOR):
voluptuous.All(voluptuous.Any(*six.string_types),
voluptuous.Length(min=1)),
})
return {
'type': 'object',
'properties': {
'resource_id': {
'type': 'string',
'minlength': 1,
'pattern': ('^([a-fA-F0-9]){8}-([a-fA-F0-9]){4}-'
'([a-fA-F0-9]){4}-([a-fA-F0-9]){4}-'
'([a-fA-F0-9]){12}$')
},
'flavor': {
'type': 'string',
'minlength': 1,
},
},
'required': ['resource_id', 'flavor'],
'additionalProperties': False,
}
@property
def instance_uuid(self):

View File

@@ -20,8 +20,6 @@
import time
from oslo_log import log
import voluptuous
from watcher.applier.actions import base
LOG = log.getLogger(__name__)
@@ -43,10 +41,17 @@ class Sleep(base.BaseAction):
@property
def schema(self):
return voluptuous.Schema({
voluptuous.Required(self.DURATION, default=1):
voluptuous.All(float, voluptuous.Range(min=0))
})
return {
'type': 'object',
'properties': {
'duration': {
'type': 'number',
'minimum': 0
},
},
'required': ['duration'],
'additionalProperties': False,
}
@property
def duration(self):
@@ -73,3 +78,4 @@ class Sleep(base.BaseAction):
def abort(self):
LOG.debug("Abort action sleep")
return True

View File

@@ -88,10 +88,6 @@ class BaseWorkFlowEngine(loadable.Loadable):
def notify(self, action, state):
db_action = objects.Action.get_by_uuid(self.context, action.uuid,
eager=True)
if (db_action.state in [objects.action.State.CANCELLING,
objects.action.State.CANCELLED] and
state == objects.action.State.SUCCEEDED):
return
db_action.state = state
db_action.save()
@@ -204,7 +200,7 @@ class BaseTaskFlowActionContainer(flow_task.Task):
objects.action.State.FAILED] or
action_plan_object.state in CANCEL_STATE):
break
time.sleep(2)
time.sleep(1)
try:
# NOTE: kill the action execution thread, if action plan is
# cancelled for all other cases wait for the result from action
@@ -246,15 +242,19 @@ class BaseTaskFlowActionContainer(flow_task.Task):
# if due to some other exception keep the flow intact.
if action_plan.state not in CANCEL_STATE:
self.do_revert()
return
action_object = objects.Action.get_by_uuid(
self.engine.context, self._db_action.uuid, eager=True)
if action_object.state == objects.action.State.ONGOING:
action_object.state = objects.action.State.CANCELLING
action_object.save()
self.abort()
if action_object.state == objects.action.State.PENDING:
elif action_object.state == objects.action.State.PENDING:
action_object.state = objects.action.State.CANCELLED
action_object.save()
else:
pass
def abort(self, *args, **kwargs):
self.do_abort(*args, **kwargs)

View File

@@ -143,8 +143,14 @@ class TaskFlowActionContainer(base.BaseTaskFlowActionContainer):
def do_abort(self, *args, **kwargs):
LOG.warning("Aborting action: %s", self.name)
try:
self.action.abort()
self.engine.notify(self._db_action, objects.action.State.CANCELLED)
result = self.action.abort()
if result:
# Aborted the action.
self.engine.notify(self._db_action,
objects.action.State.CANCELLED)
else:
self.engine.notify(self._db_action,
objects.action.State.SUCCEEDED)
except Exception as e:
self.engine.notify(self._db_action, objects.action.State.FAILED)
LOG.exception(e)

View File

@@ -71,7 +71,7 @@ def add_command_parsers(subparsers):
"Optionally, use --revision to specify an alembic revision "
"string to upgrade to.")
parser.set_defaults(func=DBCommand.upgrade)
parser.add_argument('revision', nargs='?')
parser.add_argument('--revision', nargs='?')
parser = subparsers.add_parser(
'downgrade',
@@ -79,7 +79,7 @@ def add_command_parsers(subparsers):
"While optional, one should generally use --revision to "
"specify the alembic revision string to downgrade to.")
parser.set_defaults(func=DBCommand.downgrade)
parser.add_argument('revision', nargs='?')
parser.add_argument('--revision', nargs='?')
parser = subparsers.add_parser('stamp')
parser.add_argument('revision', nargs='?')

View File

@@ -0,0 +1,79 @@
# 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.common import clients
from watcher.common import exception
LOG = log.getLogger(__name__)
class CinderHelper(object):
def __init__(self, osc=None):
""":param osc: an OpenStackClients instance"""
self.osc = osc if osc else clients.OpenStackClients()
self.cinder = self.osc.cinder()
def get_storage_node_list(self):
return list(self.cinder.services.list(binary='cinder-volume'))
def get_storage_node_by_name(self, name):
"""Get storage node by name(host@backendname)"""
try:
storages = list(filter(lambda storage:
storage.host == name,
self.get_storage_node_list()))
if len(storages) != 1:
raise exception.StorageNodeNotFound(name=name)
return storages[0]
except Exception as exc:
LOG.exception(exc)
raise exception.StorageNodeNotFound(name=name)
def get_storage_pool_list(self):
return self.cinder.pools.list(detailed=True)
def get_storage_pool_by_name(self, name):
"""Get pool by name(host@backend#poolname)"""
try:
pools = list(filter(lambda pool:
pool.name == name,
self.get_storage_pool_list()))
if len(pools) != 1:
raise exception.PoolNotFound(name=name)
return pools[0]
except Exception as exc:
LOG.exception(exc)
raise exception.PoolNotFound(name=name)
def get_volume_list(self):
return self.cinder.volumes.list(search_opts={'all_tenants': True})
def get_volume_type_list(self):
return self.cinder.volume_types.list()
def get_volume_type_by_backendname(self, backendname):
volume_type_list = self.get_volume_type_list()
volume_type = list(filter(
lambda volume_type:
volume_type.extra_specs.get(
'volume_backend_name') == backendname, volume_type_list))
if volume_type:
return volume_type[0].name
else:
return ""

View File

@@ -63,6 +63,7 @@ class RequestContext(context.RequestContext):
# oslo_context's RequestContext.to_dict() generates this field, we can
# safely ignore this as we don't use it.
kwargs.pop('user_identity', None)
kwargs.pop('global_request_id', None)
if kwargs:
LOG.warning('Arguments dropped when creating context: %s',
str(kwargs))

View File

@@ -202,6 +202,10 @@ class InvalidUuidOrName(Invalid):
msg_fmt = _("Expected a logical name or uuid but received %(name)s")
class InvalidIntervalOrCron(Invalid):
msg_fmt = _("Expected an interval or cron syntax but received %(name)s")
class GoalNotFound(ResourceNotFound):
msg_fmt = _("Goal %(goal)s could not be found")
@@ -418,6 +422,10 @@ class WildcardCharacterIsUsed(WatcherException):
"wildcard character.")
class CronFormatIsInvalid(WatcherException):
msg_fmt = _("Provided cron is invalid: %(message)s")
# Model
class ComputeResourceNotFound(WatcherException):
@@ -432,6 +440,22 @@ class ComputeNodeNotFound(ComputeResourceNotFound):
msg_fmt = _("The compute node %(name)s could not be found")
class StorageResourceNotFound(WatcherException):
msg_fmt = _("The storage resource '%(name)s' could not be found")
class StorageNodeNotFound(StorageResourceNotFound):
msg_fmt = _("The storage node %(name)s could not be found")
class PoolNotFound(StorageResourceNotFound):
msg_fmt = _("The pool %(name)s could not be found")
class VolumeNotFound(StorageResourceNotFound):
msg_fmt = _("The volume '%(name)s' could not be found")
class LoadingError(WatcherException):
msg_fmt = _("Error loading plugin '%(name)s'")

View File

@@ -85,6 +85,20 @@ class NovaHelper(object):
def find_instance(self, instance_id):
return self.nova.servers.get(instance_id)
def confirm_resize(self, instance, previous_status, retry=60):
instance.confirm_resize()
instance = self.nova.servers.get(instance.id)
while instance.status != previous_status and retry:
instance = self.nova.servers.get(instance.id)
retry -= 1
time.sleep(1)
if instance.status == previous_status:
return True
else:
LOG.debug("confirm resize failed for the "
"instance %s" % instance.id)
return False
def wait_for_volume_status(self, volume, status, timeout=60,
poll_interval=1):
"""Wait until volume reaches given status.
@@ -105,8 +119,9 @@ class NovaHelper(object):
% (volume.id, status, timeout))
return volume.status == status
def watcher_non_live_migrate_instance(self, instance_id, node_id,
keep_original_image_name=True):
def watcher_non_live_migrate_instance(self, instance_id, dest_hostname,
keep_original_image_name=True,
retry=120):
"""This method migrates a given instance
using an image of this instance and creating a new instance
@@ -118,6 +133,9 @@ class NovaHelper(object):
It returns True if the migration was successful,
False otherwise.
if destination hostname not given, this method calls nova api
to migrate the instance.
:param instance_id: the unique id of the instance to migrate.
:param keep_original_image_name: flag indicating whether the
image name from which the original instance was built must be
@@ -125,10 +143,8 @@ class NovaHelper(object):
If this flag is False, a temporary image name is built
"""
new_image_name = ""
LOG.debug(
"Trying a non-live migrate of instance '%s' "
"using a temporary image ..." % instance_id)
"Trying a non-live migrate of instance '%s' " % instance_id)
# Looking for the instance to migrate
instance = self.find_instance(instance_id)
@@ -136,10 +152,38 @@ class NovaHelper(object):
LOG.debug("Instance %s not found !" % instance_id)
return False
else:
# NOTE: If destination node is None call Nova API to migrate
# instance
host_name = getattr(instance, "OS-EXT-SRV-ATTR:host")
LOG.debug(
"Instance %s found on host '%s'." % (instance_id, host_name))
if dest_hostname is None:
previous_status = getattr(instance, 'status')
instance.migrate()
instance = self.nova.servers.get(instance_id)
while (getattr(instance, 'status') not in
["VERIFY_RESIZE", "ERROR"] and retry):
instance = self.nova.servers.get(instance.id)
time.sleep(2)
retry -= 1
new_hostname = getattr(instance, 'OS-EXT-SRV-ATTR:host')
if (host_name != new_hostname and
instance.status == 'VERIFY_RESIZE'):
if not self.confirm_resize(instance, previous_status):
return False
LOG.debug(
"cold migration succeeded : "
"instance %s is now on host '%s'." % (
instance_id, new_hostname))
return True
else:
LOG.debug(
"cold migration for instance %s failed" % instance_id)
return False
if not keep_original_image_name:
# randrange gives you an integral value
irand = random.randint(0, 1000)
@@ -268,7 +312,7 @@ class NovaHelper(object):
# We create the new instance from
# the intermediate image of the original instance
new_instance = self. \
create_instance(node_id,
create_instance(dest_hostname,
instance_name,
image_uuid,
flavor_name,
@@ -389,15 +433,15 @@ class NovaHelper(object):
False otherwise.
:param instance_id: the unique id of the instance to migrate.
:param dest_hostname: the name of the destination compute node.
:param dest_hostname: the name of the destination compute node, if
destination_node is None, nova scheduler choose
the destination host
:param block_migration: No shared storage is required.
"""
LOG.debug("Trying a live migrate of instance %s to host '%s'" % (
instance_id, dest_hostname))
LOG.debug("Trying to live migrate instance %s " % (instance_id))
# Looking for the instance to migrate
instance = self.find_instance(instance_id)
if not instance:
LOG.debug("Instance not found: %s" % instance_id)
return False
@@ -409,6 +453,29 @@ class NovaHelper(object):
instance.live_migrate(host=dest_hostname,
block_migration=block_migration,
disk_over_commit=True)
instance = self.nova.servers.get(instance_id)
# NOTE: If destination host is not specified for live migration
# let nova scheduler choose the destination host.
if dest_hostname is None:
while (instance.status not in ['ACTIVE', 'ERROR'] and retry):
instance = self.nova.servers.get(instance.id)
LOG.debug(
'Waiting the migration of {0}'.format(instance.id))
time.sleep(1)
retry -= 1
new_hostname = getattr(instance, 'OS-EXT-SRV-ATTR:host')
if host_name != new_hostname and instance.status == 'ACTIVE':
LOG.debug(
"Live migration succeeded : "
"instance %s is now on host '%s'." % (
instance_id, new_hostname))
return True
else:
return False
while getattr(instance,
'OS-EXT-SRV-ATTR:host') != dest_hostname \
and retry:
@@ -432,6 +499,43 @@ class NovaHelper(object):
return True
def abort_live_migrate(self, instance_id, source, destination, retry=240):
LOG.debug("Aborting live migration of instance %s" % instance_id)
migration = self.get_running_migration(instance_id)
if migration:
migration_id = getattr(migration[0], "id")
try:
self.nova.server_migrations.live_migration_abort(
server=instance_id, migration=migration_id)
except exception as e:
# Note: Does not return from here, as abort request can't be
# accepted but migration still going on.
LOG.exception(e)
else:
LOG.debug(
"No running migrations found for instance %s" % instance_id)
while retry:
instance = self.nova.servers.get(instance_id)
if (getattr(instance, 'OS-EXT-STS:task_state') is None and
getattr(instance, 'status') in ['ACTIVE', 'ERROR']):
break
time.sleep(2)
retry -= 1
instance_host = getattr(instance, 'OS-EXT-SRV-ATTR:host')
instance_status = getattr(instance, 'status')
# Abort live migration successfull, action is cancelled
if instance_host == source and instance_status == 'ACTIVE':
return True
# Nova Unable to abort live migration, action is succeded
elif instance_host == destination and instance_status == 'ACTIVE':
return False
else:
raise Exception("Live migration execution and abort both failed "
"for the instance %s" % instance_id)
def enable_service_nova_compute(self, hostname):
if self.nova.services.enable(host=hostname,
binary='nova-compute'). \
@@ -573,6 +677,9 @@ class NovaHelper(object):
if not instance:
LOG.debug("Instance not found: %s" % instance_id)
return False
elif getattr(instance, 'OS-EXT-STS:vm_state') == "stopped":
LOG.debug("Instance has been stopped: %s" % instance_id)
return True
else:
self.nova.servers.stop(instance_id)
@@ -754,3 +861,6 @@ class NovaHelper(object):
instance.flavor[attr] = default
continue
instance.flavor[attr] = getattr(flavor, attr, default)
def get_running_migration(self, instance_id):
return self.nova.server_migrations.list(server=instance_id)

View File

@@ -53,11 +53,10 @@ JsonPayloadSerializer = messaging.JsonPayloadSerializer
def init(conf):
global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER
exmods = get_allowed_exmods()
TRANSPORT = messaging.get_transport(conf,
allowed_remote_exmods=exmods)
TRANSPORT = messaging.get_rpc_transport(
conf, allowed_remote_exmods=exmods)
NOTIFICATION_TRANSPORT = messaging.get_notification_transport(
conf,
allowed_remote_exmods=exmods)
conf, allowed_remote_exmods=exmods)
serializer = RequestContextSerializer(JsonPayloadSerializer())
if not conf.notification_level:

View File

@@ -210,7 +210,7 @@ class Service(service.ServiceBase):
@property
def transport(self):
if self._transport is None:
self._transport = om.get_transport(CONF)
self._transport = om.get_rpc_transport(CONF)
return self._transport
@property

View File

@@ -16,8 +16,11 @@
"""Utilities and helper functions."""
import datetime
import re
from croniter import croniter
from jsonschema import validators
from oslo_log import log as logging
from oslo_utils import strutils
@@ -63,6 +66,15 @@ is_int_like = strutils.is_int_like
strtime = timeutils.strtime
def is_cron_like(value):
"""Return True is submitted value is like cron syntax"""
try:
croniter(value, datetime.datetime.now())
except Exception as e:
raise exception.CronFormatIsInvalid(message=str(e))
return True
def safe_rstrip(value, chars=None):
"""Removes trailing characters from a string if that does not make it empty
@@ -111,7 +123,7 @@ def extend_with_default(validator_class):
def set_defaults(validator, properties, instance, schema):
for prop, subschema in properties.items():
if "default" in subschema:
if "default" in subschema and instance is not None:
instance.setdefault(prop, subschema["default"])
for error in validate_properties(
@@ -128,6 +140,9 @@ def extend_with_strict_schema(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
def strict_schema(validator, properties, instance, schema):
if instance is None:
return
for para in instance.keys():
if para not in properties.keys():
raise exception.AuditParameterNotAllowed(parameter=para)

View File

@@ -49,7 +49,7 @@ WATCHER_DECISION_ENGINE_OPTS = [
'-whose number of hours has been offset by this value-'
' is older that the current time.'),
cfg.IntOpt('check_periodic_interval',
default=30*60,
default=30 * 60,
help='Interval (in seconds) for checking action plan expiry.')
]

View File

@@ -26,10 +26,10 @@ GLANCE_CLIENT_OPTS = [
default='2',
help='Version of Glance API to use in glanceclient.'),
cfg.StrOpt('endpoint_type',
default='internalURL',
default='publicURL',
help='Type of endpoint to use in glanceclient.'
'Supported values: internalURL, publicURL, adminURL'
'The default is internalURL.')]
'The default is publicURL.')]
def register_opts(conf):

View File

@@ -26,10 +26,10 @@ IRONIC_CLIENT_OPTS = [
default=1,
help='Version of Ironic API to use in ironicclient.'),
cfg.StrOpt('endpoint_type',
default='internalURL',
default='publicURL',
help='Type of endpoint to use in ironicclient.'
'Supported values: internalURL, publicURL, adminURL'
'The default is internalURL.')]
'The default is publicURL.')]
def register_opts(conf):

View File

@@ -26,10 +26,10 @@ NEUTRON_CLIENT_OPTS = [
default='2.0',
help='Version of Neutron API to use in neutronclient.'),
cfg.StrOpt('endpoint_type',
default='internalURL',
default='publicURL',
help='Type of endpoint to use in neutronclient.'
'Supported values: internalURL, publicURL, adminURL'
'The default is internalURL.')]
'The default is publicURL.')]
def register_opts(conf):

View File

@@ -162,9 +162,10 @@ class CeilometerHelper(object):
return item_value
def get_last_sample_values(self, resource_id, meter_name, limit=1):
samples = self.query_sample(meter_name=meter_name,
query=self.build_query(resource_id),
limit=limit)
samples = self.query_sample(
meter_name=meter_name,
query=self.build_query(resource_id=resource_id),
limit=limit)
values = []
for index, sample in enumerate(samples):
values.append(
@@ -174,8 +175,9 @@ class CeilometerHelper(object):
return values
def get_last_sample_value(self, resource_id, meter_name):
samples = self.query_sample(meter_name=meter_name,
query=self.build_query(resource_id))
samples = self.query_sample(
meter_name=meter_name,
query=self.build_query(resource_id=resource_id))
if samples:
return samples[-1]._info['counter_volume']
else:

View File

@@ -36,13 +36,13 @@ To run the offline migration between specific migration versions::
Upgrade the database incrementally::
$ watcher-db-manage --config-file /path/to/watcher.conf upgrade --delta \
$ watcher-db-manage --config-file /path/to/watcher.conf upgrade --revision \
<# of revs>
Downgrade the database by a certain number of revisions::
$ watcher-db-manage --config-file /path/to/watcher.conf downgrade --delta \
$ watcher-db-manage --config-file /path/to/watcher.conf downgrade --revision \
<# of revs>

View File

@@ -0,0 +1,26 @@
"""Add cron support for audit table
Revision ID: d098df6021e2
Revises: 0f6042416884
Create Date: 2017-06-08 16:21:35.746752
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'd098df6021e2'
down_revision = '0f6042416884'
def upgrade():
op.alter_column('audits', 'interval', existing_type=sa.String(36),
nullable=True)
op.add_column('audits',
sa.Column('next_run_time', sa.DateTime(), nullable=True))
def downgrade():
op.alter_column('audits', 'interval', existing_type=sa.Integer(),
nullable=True)
op.drop_column('audits', 'next_run_time')

View File

@@ -815,7 +815,7 @@ class Connection(api.BaseConnection):
query = self._set_eager_options(models.ActionPlan, query)
query = self._add_action_plans_filters(query, filters)
if not context.show_deleted:
query = query.filter_by(deleted_at=None)
query = query.filter(models.ActionPlan.deleted_at.is_(None))
return _paginate_query(models.ActionPlan, limit, marker,
sort_key, sort_dir, query)

View File

@@ -173,11 +173,12 @@ class Audit(Base):
audit_type = Column(String(20))
state = Column(String(20), nullable=True)
parameters = Column(JSONEncodedDict, nullable=True)
interval = Column(Integer, nullable=True)
interval = Column(String(36), nullable=True)
goal_id = Column(Integer, ForeignKey('goals.id'), nullable=False)
strategy_id = Column(Integer, ForeignKey('strategies.id'), nullable=True)
scope = Column(JSONEncodedList, nullable=True)
auto_trigger = Column(Boolean, nullable=False)
next_run_time = Column(DateTime, nullable=True)
goal = orm.relationship(Goal, foreign_keys=goal_id, lazy=None)
strategy = orm.relationship(Strategy, foreign_keys=strategy_id, lazy=None)

View File

@@ -19,11 +19,14 @@
import datetime
from dateutil import tz
from apscheduler.jobstores import memory
from croniter import croniter
from watcher.common import context
from watcher.common import scheduling
from watcher.common import utils
from watcher import conf
from watcher.db.sqlalchemy import api as sq_api
from watcher.db.sqlalchemy import job_store
@@ -81,11 +84,38 @@ class ContinuousAuditHandler(base.AuditHandler):
plan.save()
return solution
def _next_cron_time(self, audit):
if utils.is_cron_like(audit.interval):
return croniter(audit.interval, datetime.datetime.utcnow()
).get_next(datetime.datetime)
@classmethod
def execute_audit(cls, audit, request_context):
self = cls()
if not self._is_audit_inactive(audit):
self.execute(audit, request_context)
try:
self.execute(audit, request_context)
except Exception:
raise
finally:
if utils.is_int_like(audit.interval):
audit.next_run_time = (
datetime.datetime.utcnow() +
datetime.timedelta(seconds=int(audit.interval)))
else:
audit.next_run_time = self._next_cron_time(audit)
audit.save()
def _add_job(self, trigger, audit, audit_context, **trigger_args):
time_var = 'next_run_time' if trigger_args.get(
'next_run_time') else 'run_date'
# We should convert UTC time to local time without tzinfo
trigger_args[time_var] = trigger_args[time_var].replace(
tzinfo=tz.tzutc()).astimezone(tz.tzlocal()).replace(tzinfo=None)
self.scheduler.add_job(self.execute_audit, trigger,
args=[audit, audit_context],
name='execute_audit',
**trigger_args)
def launch_audits_periodically(self):
audit_context = context.RequestContext(is_admin=True)
@@ -101,13 +131,34 @@ class ContinuousAuditHandler(base.AuditHandler):
job.args for job in self.scheduler.get_jobs()
if job.name == 'execute_audit']
for audit in audits:
# if audit is not presented in scheduled audits yet.
if audit.uuid not in [arg[0].uuid for arg in scheduler_job_args]:
self.scheduler.add_job(
self.execute_audit, 'interval',
args=[audit, audit_context],
seconds=audit.interval,
name='execute_audit',
next_run_time=datetime.datetime.now())
# if interval is provided with seconds
if utils.is_int_like(audit.interval):
# if audit has already been provided and we need
# to restore it after shutdown
if audit.next_run_time is not None:
old_run_time = audit.next_run_time
current = datetime.datetime.utcnow()
if old_run_time < current:
delta = datetime.timedelta(
seconds=(int(audit.interval) - (
current - old_run_time).seconds %
int(audit.interval)))
audit.next_run_time = current + delta
next_run_time = audit.next_run_time
# if audit is new one
else:
next_run_time = datetime.datetime.utcnow()
self._add_job('interval', audit, audit_context,
seconds=int(audit.interval),
next_run_time=next_run_time)
else:
audit.next_run_time = self._next_cron_time(audit)
self._add_job('date', audit, audit_context,
run_date=audit.next_run_time)
audit.save()
def start(self):
self.scheduler.add_job(

View File

@@ -21,6 +21,8 @@ ServerConsolidation = goals.ServerConsolidation
ThermalOptimization = goals.ThermalOptimization
Unclassified = goals.Unclassified
WorkloadBalancing = goals.WorkloadBalancing
NoisyNeighbor = goals.NoisyNeighborOptimization
__all__ = ("Dummy", "ServerConsolidation", "ThermalOptimization",
"Unclassified", "WorkloadBalancing", )
"Unclassified", "WorkloadBalancing",
"NoisyNeighborOptimization",)

View File

@@ -166,3 +166,29 @@ class AirflowOptimization(base.Goal):
def get_efficacy_specification(cls):
"""The efficacy spec for the current goal"""
return specs.Unclassified()
class NoisyNeighborOptimization(base.Goal):
"""NoisyNeighborOptimization
This goal is used to identify and migrate a Noisy Neighbor -
a low priority VM that negatively affects peformance of a high priority VM
in terms of IPC by over utilizing Last Level Cache.
"""
@classmethod
def get_name(cls):
return "noisy_neighbor"
@classmethod
def get_display_name(cls):
return _("Noisy Neighbor")
@classmethod
def get_translatable_display_name(cls):
return "Noisy Neighbor"
@classmethod
def get_efficacy_specification(cls):
"""The efficacy spec for the current goal"""
return specs.Unclassified()

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