Compare commits
166 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1caf89686c | ||
|
|
ed3224835a | ||
|
|
8a7e316f73 | ||
|
|
112ac3bbdf | ||
|
|
d1ab697612 | ||
|
|
095ca0ffb2 | ||
|
|
e1131a65d8 | ||
|
|
5e507e56f4 | ||
|
|
62cb8a8d29 | ||
|
|
50e5e86c75 | ||
|
|
e3dd5c2a7e | ||
|
|
630c2cbb79 | ||
|
|
d49c6c16a6 | ||
|
|
e51e7e4317 | ||
|
|
244d28afa6 | ||
|
|
7ac1d0d048 | ||
|
|
52d701a56e | ||
|
|
ea1fd5967a | ||
|
|
547bf0529f | ||
|
|
659cbf3207 | ||
|
|
eb5a362287 | ||
|
|
198d827645 | ||
|
|
ecb0e218a9 | ||
|
|
4228647d15 | ||
|
|
4f1d758a40 | ||
|
|
b4433db20a | ||
|
|
e03f56e7c7 | ||
|
|
d925166a25 | ||
|
|
4a5d8cf709 | ||
|
|
e3c6db11c6 | ||
|
|
59c5adc8ad | ||
|
|
840d422b01 | ||
|
|
e9d8a2882f | ||
|
|
6e09cdb5ac | ||
|
|
edd3d219d5 | ||
|
|
d433d6b3c8 | ||
|
|
c5d4f9cb40 | ||
|
|
41f579d464 | ||
|
|
1a2fa9addf | ||
|
|
9e5ca76116 | ||
|
|
fa63b2a2b3 | ||
|
|
22cad5651e | ||
|
|
a912977336 | ||
|
|
2d7a375338 | ||
|
|
45b82e1898 | ||
|
|
926dbc8392 | ||
|
|
0e440d37ee | ||
|
|
66934f29d3 | ||
|
|
7039a9d247 | ||
|
|
bbbae0b105 | ||
|
|
1d08d2eea1 | ||
|
|
23442a4da6 | ||
|
|
ed88b436af | ||
|
|
a48a16596f | ||
|
|
8f4a856bd2 | ||
|
|
85a46ce4d4 | ||
|
|
35066dfe60 | ||
|
|
fc9eb6e995 | ||
|
|
aaf6fd7959 | ||
|
|
5e077f37ea | ||
|
|
2ec8bc10cd | ||
|
|
89cea83c85 | ||
|
|
2dd00a2037 | ||
|
|
59e13fd1f3 | ||
|
|
4235ef7c24 | ||
|
|
a015af1bd2 | ||
|
|
dad90b63fd | ||
|
|
c6e5f98008 | ||
|
|
1341c0ee02 | ||
|
|
6e99fcffc3 | ||
|
|
a57f54ab8f | ||
|
|
d1490e3fa7 | ||
|
|
1324baf9f5 | ||
|
|
0adc7d91e6 | ||
|
|
0d7ded0bb3 | ||
|
|
80dfbd6334 | ||
|
|
7d40b3d4c3 | ||
|
|
cedf70559e | ||
|
|
cc561c528f | ||
|
|
87b494d52a | ||
|
|
d0bca1f2ab | ||
|
|
068178f12a | ||
|
|
99e6c4aebb | ||
|
|
b446f8afd2 | ||
|
|
7783ebfb71 | ||
|
|
22cfc495f4 | ||
|
|
360b0119d6 | ||
|
|
48fc90d7b6 | ||
|
|
c5ff387ae9 | ||
|
|
1bc6b0e605 | ||
|
|
345083e090 | ||
|
|
695ddf8ae7 | ||
|
|
8fd5057cd0 | ||
|
|
3db81564f4 | ||
|
|
10066ed8fd | ||
|
|
e4c5f4f050 | ||
|
|
53c896dd24 | ||
|
|
40a46c6663 | ||
|
|
ed21e452e0 | ||
|
|
80e77a5b81 | ||
|
|
74112dd7cf | ||
|
|
9e4bf718da | ||
|
|
5c79074e9c | ||
|
|
ac6848dad3 | ||
|
|
648715eb5c | ||
|
|
1a17c4b7ac | ||
|
|
c4dfbd5855 | ||
|
|
1981f3964e | ||
|
|
3b5ef5d625 | ||
|
|
7fd486bd65 | ||
|
|
3cf4b315d3 | ||
|
|
d792e3cfae | ||
|
|
25d84ba662 | ||
|
|
7908af3150 | ||
|
|
04fdea2aa0 | ||
|
|
cee9cfb62c | ||
|
|
e1912fe03e | ||
|
|
d859f3ac1f | ||
|
|
7a72371df8 | ||
|
|
82bb097e9f | ||
|
|
a9ef9f3a94 | ||
|
|
8e7ba3c44a | ||
|
|
9a2ca8c4b7 | ||
|
|
c08666b2fa | ||
|
|
6638f921a3 | ||
|
|
f66eb463ca | ||
|
|
6a323ed54f | ||
|
|
1f2a854d6a | ||
|
|
d252d47cc0 | ||
|
|
d0e46d81fc | ||
|
|
4e240b945b | ||
|
|
e09188d862 | ||
|
|
a4b1df2fce | ||
|
|
22933e4d79 | ||
|
|
40a5c98382 | ||
|
|
5b21b9a17e | ||
|
|
02d1850be7 | ||
|
|
5f1f10e3d3 | ||
|
|
e4732e1375 | ||
|
|
715f6fa1cd | ||
|
|
6daa09f489 | ||
|
|
7feced419c | ||
|
|
3319748367 | ||
|
|
a84f52dfe3 | ||
|
|
3f8e4451f5 | ||
|
|
d082c9ac41 | ||
|
|
9080180309 | ||
|
|
55893043df | ||
|
|
19074f615a | ||
|
|
295c8d914c | ||
|
|
99735fa39a | ||
|
|
b80229f3d0 | ||
|
|
5e9ba463ee | ||
|
|
120c116655 | ||
|
|
c9cfd3bfbd | ||
|
|
31f2b4172e | ||
|
|
5151b666fd | ||
|
|
578138e432 | ||
|
|
7e2fd7ed9a | ||
|
|
74cb93fca8 | ||
|
|
876f3adb22 | ||
|
|
06682fe7c3 | ||
|
|
eaaa2b1b69 | ||
|
|
88187a8ba9 | ||
|
|
0b6979b71c | ||
|
|
8eb99ef76e |
@@ -2,3 +2,4 @@
|
|||||||
host=review.openstack.org
|
host=review.openstack.org
|
||||||
port=29418
|
port=29418
|
||||||
project=openstack/watcher.git
|
project=openstack/watcher.git
|
||||||
|
defaultbranch=stable/ocata
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
========================
|
||||||
|
Team and repository tags
|
||||||
|
========================
|
||||||
|
|
||||||
|
.. image:: http://governance.openstack.org/badges/watcher.svg
|
||||||
|
:target: http://governance.openstack.org/reference/tags/index.html
|
||||||
|
|
||||||
|
.. Change things from this point on
|
||||||
|
|
||||||
..
|
..
|
||||||
Except where otherwise noted, this document is licensed under Creative
|
Except where otherwise noted, this document is licensed under Creative
|
||||||
Commons Attribution 3.0 License. You can view the license at:
|
Commons Attribution 3.0 License. You can view the license at:
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ function create_watcher_conf {
|
|||||||
iniset $WATCHER_CONF oslo_messaging_notifications driver "messaging"
|
iniset $WATCHER_CONF oslo_messaging_notifications driver "messaging"
|
||||||
|
|
||||||
iniset $NOVA_CONF oslo_messaging_notifications topics "notifications,watcher_notifications"
|
iniset $NOVA_CONF oslo_messaging_notifications topics "notifications,watcher_notifications"
|
||||||
|
iniset $NOVA_CONF notifications notify_on_state_change "vm_and_task_state"
|
||||||
|
|
||||||
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR
|
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR
|
||||||
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR "watcher_clients_auth"
|
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR "watcher_clients_auth"
|
||||||
@@ -187,7 +188,7 @@ function init_watcher {
|
|||||||
recreate_database watcher
|
recreate_database watcher
|
||||||
|
|
||||||
# Create watcher schema
|
# Create watcher schema
|
||||||
$WATCHER_BIN_DIR/watcher-db-manage --config-file $WATCHER_CONF create_schema
|
$WATCHER_BIN_DIR/watcher-db-manage --config-file $WATCHER_CONF upgrade head
|
||||||
fi
|
fi
|
||||||
create_watcher_cache_dir
|
create_watcher_cache_dir
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,3 +44,6 @@ LOGDAYS=2
|
|||||||
[[post-config|$NOVA_CONF]]
|
[[post-config|$NOVA_CONF]]
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
compute_monitors=cpu.virt_driver
|
compute_monitors=cpu.virt_driver
|
||||||
|
notify_on_state_change = vm_and_task_state
|
||||||
|
[notifications]
|
||||||
|
notify_on_state_change = vm_and_task_state
|
||||||
|
|||||||
@@ -45,3 +45,6 @@ LOGDAYS=2
|
|||||||
[[post-config|$NOVA_CONF]]
|
[[post-config|$NOVA_CONF]]
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
compute_monitors=cpu.virt_driver
|
compute_monitors=cpu.virt_driver
|
||||||
|
notify_on_state_change = vm_and_task_state
|
||||||
|
[notifications]
|
||||||
|
notify_on_state_change = vm_and_task_state
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
# Copyright (c) 2015 b<>com
|
# Copyright (c) 2015 b<>com
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|||||||
54
doc/notification_samples/action_plan-create.json
Normal file
54
doc/notification_samples/action_plan-create.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"publisher_id": "infra-optim:node0",
|
||||||
|
"payload": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"strategy": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"display_name": "test strategy",
|
||||||
|
"name": "TEST",
|
||||||
|
"updated_at": null,
|
||||||
|
"parameters_spec": {},
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"deleted_at": null
|
||||||
|
},
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "StrategyPayload"
|
||||||
|
},
|
||||||
|
"created_at": null,
|
||||||
|
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"audit": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"audit_type": "ONESHOT",
|
||||||
|
"scope": [],
|
||||||
|
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
|
"parameters": {},
|
||||||
|
"interval": null,
|
||||||
|
"deleted_at": null,
|
||||||
|
"state": "PENDING",
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"updated_at": null
|
||||||
|
},
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "TerseAuditPayload"
|
||||||
|
},
|
||||||
|
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
||||||
|
"global_efficacy": {},
|
||||||
|
"deleted_at": null,
|
||||||
|
"state": "RECOMMENDED",
|
||||||
|
"updated_at": null
|
||||||
|
},
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "ActionPlanCreatePayload"
|
||||||
|
},
|
||||||
|
"priority": "INFO",
|
||||||
|
"message_id": "5148bff1-ea06-4ad6-8e4e-8c85ca5eb629",
|
||||||
|
"event_type": "action_plan.create",
|
||||||
|
"timestamp": "2016-10-18 09:52:05.219414"
|
||||||
|
}
|
||||||
54
doc/notification_samples/action_plan-delete.json
Normal file
54
doc/notification_samples/action_plan-delete.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"publisher_id": "infra-optim:node0",
|
||||||
|
"timestamp": "2016-10-18 09:52:05.219414",
|
||||||
|
"payload": {
|
||||||
|
"watcher_object.data": {
|
||||||
|
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"audit": {
|
||||||
|
"watcher_object.data": {
|
||||||
|
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
|
"interval": null,
|
||||||
|
"audit_type": "ONESHOT",
|
||||||
|
"scope": [],
|
||||||
|
"updated_at": null,
|
||||||
|
"deleted_at": null,
|
||||||
|
"state": "PENDING",
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"parameters": {}
|
||||||
|
},
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.name": "TerseAuditPayload",
|
||||||
|
"watcher_object.namespace": "watcher"
|
||||||
|
},
|
||||||
|
"global_efficacy": {},
|
||||||
|
"updated_at": null,
|
||||||
|
"deleted_at": null,
|
||||||
|
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"strategy": {
|
||||||
|
"watcher_object.data": {
|
||||||
|
"uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"name": "TEST",
|
||||||
|
"display_name": "test strategy",
|
||||||
|
"deleted_at": null,
|
||||||
|
"updated_at": null,
|
||||||
|
"parameters_spec": {}
|
||||||
|
},
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.name": "StrategyPayload",
|
||||||
|
"watcher_object.namespace": "watcher"
|
||||||
|
},
|
||||||
|
"state": "DELETED"
|
||||||
|
},
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.name": "ActionPlanDeletePayload",
|
||||||
|
"watcher_object.namespace": "watcher"
|
||||||
|
},
|
||||||
|
"event_type": "action_plan.delete",
|
||||||
|
"message_id": "3d137686-a1fd-4683-ab40-c4210aac2140",
|
||||||
|
"priority": "INFO"
|
||||||
|
}
|
||||||
55
doc/notification_samples/action_plan-execution-end.json
Normal file
55
doc/notification_samples/action_plan-execution-end.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"event_type": "action_plan.execution.end",
|
||||||
|
"payload": {
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "ActionPlanActionPayload",
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"deleted_at": null,
|
||||||
|
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"audit": {
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "TerseAuditPayload",
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"deleted_at": null,
|
||||||
|
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
|
"scope": [],
|
||||||
|
"audit_type": "ONESHOT",
|
||||||
|
"state": "SUCCEEDED",
|
||||||
|
"parameters": {},
|
||||||
|
"interval": null,
|
||||||
|
"updated_at": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
||||||
|
"fault": null,
|
||||||
|
"state": "ONGOING",
|
||||||
|
"global_efficacy": {},
|
||||||
|
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"strategy": {
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "StrategyPayload",
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"deleted_at": null,
|
||||||
|
"name": "TEST",
|
||||||
|
"uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"parameters_spec": {},
|
||||||
|
"display_name": "test strategy",
|
||||||
|
"updated_at": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"updated_at": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"priority": "INFO",
|
||||||
|
"message_id": "3984dc2b-8aef-462b-a220-8ae04237a56e",
|
||||||
|
"timestamp": "2016-10-18 09:52:05.219414",
|
||||||
|
"publisher_id": "infra-optim:node0"
|
||||||
|
}
|
||||||
65
doc/notification_samples/action_plan-execution-error.json
Normal file
65
doc/notification_samples/action_plan-execution-error.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"event_type": "action_plan.execution.error",
|
||||||
|
"publisher_id": "infra-optim:node0",
|
||||||
|
"priority": "ERROR",
|
||||||
|
"message_id": "9a45c5ae-0e21-4300-8fa0-5555d52a66d9",
|
||||||
|
"payload": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "ActionPlanActionPayload",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"fault": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "ExceptionPayload",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"exception_message": "TEST",
|
||||||
|
"module_name": "watcher.tests.notifications.test_action_plan_notification",
|
||||||
|
"function_name": "test_send_action_plan_action_with_error",
|
||||||
|
"exception": "WatcherException"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"strategy": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "StrategyPayload",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"name": "TEST",
|
||||||
|
"updated_at": null,
|
||||||
|
"display_name": "test strategy",
|
||||||
|
"parameters_spec": {},
|
||||||
|
"deleted_at": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"updated_at": null,
|
||||||
|
"deleted_at": null,
|
||||||
|
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"audit": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "TerseAuditPayload",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"parameters": {},
|
||||||
|
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"scope": [],
|
||||||
|
"updated_at": null,
|
||||||
|
"audit_type": "ONESHOT",
|
||||||
|
"interval": null,
|
||||||
|
"deleted_at": null,
|
||||||
|
"state": "PENDING"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"global_efficacy": {},
|
||||||
|
"state": "ONGOING"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timestamp": "2016-10-18 09:52:05.219414"
|
||||||
|
}
|
||||||
55
doc/notification_samples/action_plan-execution-start.json
Normal file
55
doc/notification_samples/action_plan-execution-start.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"event_type": "action_plan.execution.start",
|
||||||
|
"payload": {
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "ActionPlanActionPayload",
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"deleted_at": null,
|
||||||
|
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"audit": {
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "TerseAuditPayload",
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"deleted_at": null,
|
||||||
|
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
|
"scope": [],
|
||||||
|
"audit_type": "ONESHOT",
|
||||||
|
"state": "PENDING",
|
||||||
|
"parameters": {},
|
||||||
|
"interval": null,
|
||||||
|
"updated_at": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
||||||
|
"fault": null,
|
||||||
|
"state": "ONGOING",
|
||||||
|
"global_efficacy": {},
|
||||||
|
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"strategy": {
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "StrategyPayload",
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"deleted_at": null,
|
||||||
|
"name": "TEST",
|
||||||
|
"uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"parameters_spec": {},
|
||||||
|
"display_name": "test strategy",
|
||||||
|
"updated_at": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"updated_at": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"priority": "INFO",
|
||||||
|
"message_id": "3984dc2b-8aef-462b-a220-8ae04237a56e",
|
||||||
|
"timestamp": "2016-10-18 09:52:05.219414",
|
||||||
|
"publisher_id": "infra-optim:node0"
|
||||||
|
}
|
||||||
63
doc/notification_samples/action_plan-update.json
Normal file
63
doc/notification_samples/action_plan-update.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"payload": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"audit": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"audit_type": "ONESHOT",
|
||||||
|
"scope": [],
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
|
"interval": null,
|
||||||
|
"updated_at": null,
|
||||||
|
"state": "PENDING",
|
||||||
|
"deleted_at": null,
|
||||||
|
"parameters": {}
|
||||||
|
},
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "TerseAuditPayload"
|
||||||
|
},
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
||||||
|
"updated_at": null,
|
||||||
|
"state_update": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"old_state": "PENDING",
|
||||||
|
"state": "ONGOING"
|
||||||
|
},
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "ActionPlanStateUpdatePayload"
|
||||||
|
},
|
||||||
|
"state": "ONGOING",
|
||||||
|
"deleted_at": null,
|
||||||
|
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"strategy": {
|
||||||
|
"watcher_object.version": "1.0",
|
||||||
|
"watcher_object.data": {
|
||||||
|
"name": "TEST",
|
||||||
|
"uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
||||||
|
"display_name": "test strategy",
|
||||||
|
"created_at": "2016-10-18T09:52:05Z",
|
||||||
|
"updated_at": null,
|
||||||
|
"deleted_at": null,
|
||||||
|
"parameters_spec": {}
|
||||||
|
},
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "StrategyPayload"
|
||||||
|
},
|
||||||
|
"global_efficacy": {}
|
||||||
|
},
|
||||||
|
"watcher_object.namespace": "watcher",
|
||||||
|
"watcher_object.name": "ActionPlanUpdatePayload"
|
||||||
|
},
|
||||||
|
"publisher_id": "infra-optim:node0",
|
||||||
|
"priority": "INFO",
|
||||||
|
"timestamp": "2016-10-18 09:52:05.219414",
|
||||||
|
"event_type": "action_plan.update",
|
||||||
|
"message_id": "0a8a7329-fd5a-4ec6-97d7-2b776ce51a4c"
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"state": "PENDING",
|
"state": "PENDING",
|
||||||
"updated_at": null,
|
"updated_at": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
},
|
},
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"scope": [],
|
"scope": [],
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"parameters_spec": {
|
"parameters_spec": {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"state": "DELETED",
|
"state": "DELETED",
|
||||||
"updated_at": null,
|
"updated_at": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
},
|
},
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"scope": [],
|
"scope": [],
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"parameters_spec": {
|
"parameters_spec": {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"updated_at": null,
|
"updated_at": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"fault": null,
|
"fault": null,
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
},
|
},
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"scope": [],
|
"scope": [],
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"parameters_spec": {
|
"parameters_spec": {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"watcher_object.namespace": "watcher",
|
"watcher_object.namespace": "watcher",
|
||||||
"watcher_object.version": "1.0"
|
"watcher_object.version": "1.0"
|
||||||
},
|
},
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
},
|
},
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"scope": [],
|
"scope": [],
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"parameters_spec": {
|
"parameters_spec": {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"updated_at": null,
|
"updated_at": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"fault": null,
|
"fault": null,
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
},
|
},
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"scope": [],
|
"scope": [],
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"parameters_spec": {
|
"parameters_spec": {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"updated_at": null,
|
"updated_at": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"fault": null,
|
"fault": null,
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
},
|
},
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"scope": [],
|
"scope": [],
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"parameters_spec": {
|
"parameters_spec": {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"watcher_object.namespace": "watcher",
|
"watcher_object.namespace": "watcher",
|
||||||
"watcher_object.version": "1.0"
|
"watcher_object.version": "1.0"
|
||||||
},
|
},
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
},
|
},
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"scope": [],
|
"scope": [],
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"parameters_spec": {
|
"parameters_spec": {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"updated_at": null,
|
"updated_at": null,
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"fault": null,
|
"fault": null,
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
"uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
},
|
},
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"scope": [],
|
"scope": [],
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
"parameters_spec": {
|
"parameters_spec": {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"payload": {
|
"payload": {
|
||||||
"watcher_object.name": "AuditUpdatePayload",
|
"watcher_object.name": "AuditUpdatePayload",
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
|
"strategy_uuid": "75234dfe-87e3-4f11-a0e0-3c3305d86a39",
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"watcher_object.name": "StrategyPayload",
|
"watcher_object.name": "StrategyPayload",
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
"scope": [],
|
"scope": [],
|
||||||
"created_at": "2016-11-04T16:51:21Z",
|
"created_at": "2016-11-04T16:51:21Z",
|
||||||
"uuid": "f1e0d912-afd9-4bf2-91ef-c99cd08cc1ef",
|
"uuid": "f1e0d912-afd9-4bf2-91ef-c99cd08cc1ef",
|
||||||
|
"goal_uuid": "bc830f84-8ae3-4fc6-8bc6-e3dd15e8b49a",
|
||||||
"goal": {
|
"goal": {
|
||||||
"watcher_object.name": "GoalPayload",
|
"watcher_object.name": "GoalPayload",
|
||||||
"watcher_object.data": {
|
"watcher_object.data": {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Overview
|
|||||||
Below you will find a diagram, showing the main components of Watcher:
|
Below you will find a diagram, showing the main components of Watcher:
|
||||||
|
|
||||||
.. image:: ./images/architecture.svg
|
.. image:: ./images/architecture.svg
|
||||||
:width: 100%
|
:width: 110%
|
||||||
|
|
||||||
|
|
||||||
.. _components_definition:
|
.. _components_definition:
|
||||||
@@ -37,13 +37,12 @@ AMQP Bus
|
|||||||
The AMQP message bus handles internal asynchronous communications between the
|
The AMQP message bus handles internal asynchronous communications between the
|
||||||
different Watcher components.
|
different Watcher components.
|
||||||
|
|
||||||
.. _cluster_history_db_definition:
|
.. _cluster_datasource_definition:
|
||||||
|
|
||||||
Cluster History Database
|
Datasource
|
||||||
------------------------
|
----------
|
||||||
|
|
||||||
This component stores the data related to the
|
This component stores the metrics related to the cluster.
|
||||||
:ref:`Cluster History <cluster_history_definition>`.
|
|
||||||
|
|
||||||
It can potentially rely on any appropriate storage system (InfluxDB, OpenTSDB,
|
It can potentially rely on any appropriate storage system (InfluxDB, OpenTSDB,
|
||||||
MongoDB,...) but will probably be more performant when using
|
MongoDB,...) but will probably be more performant when using
|
||||||
@@ -51,14 +50,6 @@ MongoDB,...) but will probably be more performant when using
|
|||||||
which are optimized for handling time series data, which are arrays of numbers
|
which are optimized for handling time series data, which are arrays of numbers
|
||||||
indexed by time (a datetime or a datetime range).
|
indexed by time (a datetime or a datetime range).
|
||||||
|
|
||||||
.. _cluster_model_db_definition:
|
|
||||||
|
|
||||||
Cluster Model Database
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
This component stores the data related to the
|
|
||||||
:ref:`Cluster Data Model <cluster_data_model_definition>`.
|
|
||||||
|
|
||||||
.. _archi_watcher_api_definition:
|
.. _archi_watcher_api_definition:
|
||||||
|
|
||||||
Watcher API
|
Watcher API
|
||||||
@@ -193,8 +184,8 @@ data:
|
|||||||
:ref:`Managed resources <managed_resource_definition>` (e.g., the data stored
|
:ref:`Managed resources <managed_resource_definition>` (e.g., the data stored
|
||||||
in the Nova database). These models gives a strategy the ability to reason on
|
in the Nova database). These models gives a strategy the ability to reason on
|
||||||
the current state of a given :ref:`cluster <cluster_definition>`.
|
the current state of a given :ref:`cluster <cluster_definition>`.
|
||||||
- The data stored in the :ref:`Cluster History Database
|
- The data stored in the :ref:`Cluster Datasource
|
||||||
<cluster_history_db_definition>` which provides information about the past of
|
<cluster_datasource_definition>` which provides information about the past of
|
||||||
the :ref:`Cluster <cluster_definition>`.
|
the :ref:`Cluster <cluster_definition>`.
|
||||||
|
|
||||||
Here below is a sequence diagram showing how the Decision Engine builds and
|
Here below is a sequence diagram showing how the Decision Engine builds and
|
||||||
@@ -316,7 +307,7 @@ This method finds an appropriate scheduling of
|
|||||||
:ref:`Actions <action_definition>` taking into account some scheduling rules
|
:ref:`Actions <action_definition>` taking into account some scheduling rules
|
||||||
(such as priorities between actions).
|
(such as priorities between actions).
|
||||||
It generates a new :ref:`Action Plan <action_plan_definition>` with status
|
It generates a new :ref:`Action Plan <action_plan_definition>` with status
|
||||||
**RECOMMENDED** and saves it into the:ref:`Watcher Database
|
**RECOMMENDED** and saves it into the :ref:`Watcher Database
|
||||||
<watcher_database_definition>`. The saved action plan is now a scheduled flow
|
<watcher_database_definition>`. The saved action plan is now a scheduled flow
|
||||||
of actions to which a global efficacy is associated alongside a number of
|
of actions to which a global efficacy is associated alongside a number of
|
||||||
:ref:`Efficacy Indicators <efficacy_indicator_definition>` as specified by the
|
:ref:`Efficacy Indicators <efficacy_indicator_definition>` as specified by the
|
||||||
@@ -452,6 +443,10 @@ state may be one of the following:
|
|||||||
- **CANCELLED** : the :ref:`Action Plan <action_plan_definition>` was in
|
- **CANCELLED** : the :ref:`Action Plan <action_plan_definition>` was in
|
||||||
**RECOMMENDED**, **PENDING** or **ONGOING** state and was cancelled by the
|
**RECOMMENDED**, **PENDING** or **ONGOING** state and was cancelled by the
|
||||||
:ref:`Administrator <administrator_definition>`
|
:ref:`Administrator <administrator_definition>`
|
||||||
|
- **SUPERSEDED** : the :ref:`Action Plan <action_plan_definition>` was in
|
||||||
|
RECOMMENDED state and was automatically superseded by Watcher, due to an
|
||||||
|
expiration delay or an update of the
|
||||||
|
:ref:`Cluster data model <cluster_data_model_definition>`
|
||||||
|
|
||||||
|
|
||||||
The following diagram shows the different possible states of an
|
The following diagram shows the different possible states of an
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Service overview
|
|||||||
================
|
================
|
||||||
|
|
||||||
The Watcher system is a collection of services that provides support to
|
The Watcher system is a collection of services that provides support to
|
||||||
optimize your IAAS plateform. The Watcher service may, depending upon
|
optimize your IAAS platform. The Watcher service may, depending upon
|
||||||
configuration, interact with several other OpenStack services. This includes:
|
configuration, interact with several other OpenStack services. This includes:
|
||||||
|
|
||||||
- the OpenStack Identity service (`keystone`_) for request authentication and
|
- the OpenStack Identity service (`keystone`_) for request authentication and
|
||||||
@@ -37,7 +37,7 @@ The Watcher service includes the following components:
|
|||||||
- `watcher-dashboard`_: An Horizon plugin for interacting with the Watcher
|
- `watcher-dashboard`_: An Horizon plugin for interacting with the Watcher
|
||||||
service.
|
service.
|
||||||
|
|
||||||
Additionally, the Bare Metal service has certain external dependencies, which
|
Additionally, the Watcher service has certain external dependencies, which
|
||||||
are very similar to other OpenStack services:
|
are very similar to other OpenStack services:
|
||||||
|
|
||||||
- A database to store audit and action plan information and state. You can set
|
- A database to store audit and action plan information and state. You can set
|
||||||
@@ -86,7 +86,6 @@ Configure the Identity service for the Watcher service
|
|||||||
--tenant=KEYSTONE_SERVICE_PROJECT_NAME
|
--tenant=KEYSTONE_SERVICE_PROJECT_NAME
|
||||||
$ keystone user-role-add --user=watcher \
|
$ keystone user-role-add --user=watcher \
|
||||||
--tenant=KEYSTONE_SERVICE_PROJECT_NAME --role=admin
|
--tenant=KEYSTONE_SERVICE_PROJECT_NAME --role=admin
|
||||||
$ keystone user-role-add --user=watcher --tenant=admin --role=admin
|
|
||||||
|
|
||||||
or (by using python-openstackclient 1.8.0+)
|
or (by using python-openstackclient 1.8.0+)
|
||||||
|
|
||||||
@@ -97,7 +96,6 @@ Configure the Identity service for the Watcher service
|
|||||||
--project=KEYSTONE_SERVICE_PROJECT_NAME
|
--project=KEYSTONE_SERVICE_PROJECT_NAME
|
||||||
$ openstack role add --project KEYSTONE_SERVICE_PROJECT_NAME \
|
$ openstack role add --project KEYSTONE_SERVICE_PROJECT_NAME \
|
||||||
--user watcher admin
|
--user watcher admin
|
||||||
$ openstack role add --user watcher --project admin admin
|
|
||||||
|
|
||||||
|
|
||||||
#. You must register the Watcher Service with the Identity Service so that
|
#. You must register the Watcher Service with the Identity Service so that
|
||||||
@@ -169,7 +167,7 @@ these following commands::
|
|||||||
|
|
||||||
$ git clone git://git.openstack.org/openstack/watcher
|
$ git clone git://git.openstack.org/openstack/watcher
|
||||||
$ cd watcher/
|
$ cd watcher/
|
||||||
$ tox -econfig
|
$ tox -e genconfig
|
||||||
$ vi etc/watcher/watcher.conf.sample
|
$ vi etc/watcher/watcher.conf.sample
|
||||||
|
|
||||||
|
|
||||||
@@ -368,7 +366,7 @@ Configure Nova compute
|
|||||||
Please check your hypervisor configuration to correctly handle
|
Please check your hypervisor configuration to correctly handle
|
||||||
`instance migration`_.
|
`instance migration`_.
|
||||||
|
|
||||||
.. _`instance migration`: http://docs.openstack.org/admin-guide-cloud/compute-configuring-migrations.html
|
.. _`instance migration`: http://docs.openstack.org/admin-guide/compute-live-migration-usage.html
|
||||||
|
|
||||||
Configure Measurements
|
Configure Measurements
|
||||||
======================
|
======================
|
||||||
|
|||||||
@@ -193,6 +193,37 @@ must exist in every other compute node's stack user's authorized_keys file and
|
|||||||
every compute node's public ECDSA key needs to be in every other compute
|
every compute node's public ECDSA key needs to be in every other compute
|
||||||
node's root user's known_hosts file.
|
node's root user's known_hosts file.
|
||||||
|
|
||||||
|
Disable serial console
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Serial console needs to be disabled for live migration to work.
|
||||||
|
|
||||||
|
On both the controller and compute node, in /etc/nova/nova.conf
|
||||||
|
|
||||||
|
[serial_console]
|
||||||
|
enabled = False
|
||||||
|
|
||||||
|
Alternatively, in devstack's local.conf:
|
||||||
|
|
||||||
|
[[post-config|$NOVA_CONF]]
|
||||||
|
[serial_console]
|
||||||
|
#enabled=false
|
||||||
|
|
||||||
|
|
||||||
|
VNC server configuration
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The VNC server listening parameter needs to be set to any address so
|
||||||
|
that the server can accept connections from all of the compute nodes.
|
||||||
|
|
||||||
|
On both the controller and compute node, in /etc/nova/nova.conf
|
||||||
|
|
||||||
|
vncserver_listen = 0.0.0.0
|
||||||
|
|
||||||
|
Alternatively, in devstack's local.conf:
|
||||||
|
|
||||||
|
VNCSERVER_LISTEN=0.0.0.0
|
||||||
|
|
||||||
|
|
||||||
Environment final checkup
|
Environment final checkup
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ your platform.
|
|||||||
|
|
||||||
$ sudo yum install openssl-devel libffi-devel mysql-devel
|
$ sudo yum install openssl-devel libffi-devel mysql-devel
|
||||||
|
|
||||||
|
* CentOS 7::
|
||||||
|
|
||||||
|
$ sudo yum install gcc python-devel libxml2-devel libxslt-devel mariadb-devel
|
||||||
|
|
||||||
PyPi Packages and VirtualEnv
|
PyPi Packages and VirtualEnv
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ implement:
|
|||||||
implement. This is the first function to be called by the
|
implement. This is the first function to be called by the
|
||||||
:ref:`applier <watcher_applier_definition>` before any further processing
|
:ref:`applier <watcher_applier_definition>` before any further processing
|
||||||
and its role is to validate the input parameters that were provided to it.
|
and its role is to validate the input parameters that were provided to it.
|
||||||
- The :py:meth:`~.BaseAction.precondition` is called before the execution of
|
- The :py:meth:`~.BaseAction.pre_condition` is called before the execution of
|
||||||
an action. This method is a hook that can be used to perform some
|
an action. This method is a hook that can be used to perform some
|
||||||
initializations or to make some more advanced validation on its input
|
initializations or to make some more advanced validation on its input
|
||||||
parameters. If you wish to block the execution based on this factor, you
|
parameters. If you wish to block the execution based on this factor, you
|
||||||
simply have to ``raise`` an exception.
|
simply have to ``raise`` an exception.
|
||||||
- The :py:meth:`~.BaseAction.postcondition` is called after the execution of
|
- The :py:meth:`~.BaseAction.post_condition` is called after the execution of
|
||||||
an action. As this function is called regardless of whether an action
|
an action. As this function is called regardless of whether an action
|
||||||
succeeded or not, this can prove itself useful to perform cleanup
|
succeeded or not, this can prove itself useful to perform cleanup
|
||||||
operations.
|
operations.
|
||||||
@@ -71,11 +71,11 @@ Here is an example showing how you can write a plugin called ``DummyAction``:
|
|||||||
# Does nothing
|
# Does nothing
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def precondition(self):
|
def pre_condition(self):
|
||||||
# No pre-checks are done here
|
# No pre-checks are done here
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def postcondition(self):
|
def post_condition(self):
|
||||||
# Nothing done here
|
# Nothing done here
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -268,5 +268,5 @@ At this point, you can use your new cluster data model plugin in your
|
|||||||
# [...]
|
# [...]
|
||||||
dummy_collector = self.collector_manager.get_cluster_model_collector(
|
dummy_collector = self.collector_manager.get_cluster_model_collector(
|
||||||
"dummy") # "dummy" is the name of the entry point we declared earlier
|
"dummy") # "dummy" is the name of the entry point we declared earlier
|
||||||
dummy_model = collector.get_latest_cluster_data_model()
|
dummy_model = dummy_collector.get_latest_cluster_data_model()
|
||||||
# Do some stuff with this model
|
# Do some stuff with this model
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ Here is an example showing how you can define a new ``NewGoal`` goal plugin:
|
|||||||
# import path: thirdparty.new
|
# import path: thirdparty.new
|
||||||
|
|
||||||
from watcher._i18n import _
|
from watcher._i18n import _
|
||||||
|
from watcher.decision_engine.goal import base
|
||||||
from watcher.decision_engine.goal.efficacy import specs
|
from watcher.decision_engine.goal.efficacy import specs
|
||||||
from watcher.decision_engine.strategy.strategies import base
|
|
||||||
|
|
||||||
class NewGoal(base.Goal):
|
class NewGoal(base.Goal):
|
||||||
|
|
||||||
@@ -79,11 +79,11 @@ Here is an example showing how you can define a new ``NewGoal`` goal plugin:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_efficacy_specification(cls):
|
def get_efficacy_specification(cls):
|
||||||
return specs.UnclassifiedStrategySpecification()
|
return specs.Unclassified()
|
||||||
|
|
||||||
|
|
||||||
As you may have noticed, the :py:meth:`~.Goal.get_efficacy_specification`
|
As you may have noticed, the :py:meth:`~.Goal.get_efficacy_specification`
|
||||||
method returns an :py:meth:`~.UnclassifiedStrategySpecification` instance which
|
method returns an :py:meth:`~.Unclassified` instance which
|
||||||
is provided by Watcher. This efficacy specification is useful during the
|
is provided by Watcher. This efficacy specification is useful during the
|
||||||
development process of your goal as it corresponds to an empty specification.
|
development process of your goal as it corresponds to an empty specification.
|
||||||
If you want to learn more about what efficacy specifications are used for or to
|
If you want to learn more about what efficacy specifications are used for or to
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ Here is an example showing how you can write a planner plugin called
|
|||||||
|
|
||||||
# Filepath = third-party/third_party/dummy.py
|
# Filepath = third-party/third_party/dummy.py
|
||||||
# Import path = third_party.dummy
|
# Import path = third_party.dummy
|
||||||
import uuid
|
from oslo_utils import uuidutils
|
||||||
from watcher.decision_engine.planner import base
|
from watcher.decision_engine.planner import base
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ Here is an example showing how you can write a planner plugin called
|
|||||||
|
|
||||||
def _create_action_plan(self, context, audit_id):
|
def _create_action_plan(self, context, audit_id):
|
||||||
action_plan_dict = {
|
action_plan_dict = {
|
||||||
'uuid': uuid.uuid4(),
|
'uuid': uuidutils.generate_uuid(),
|
||||||
'audit_id': audit_id,
|
'audit_id': audit_id,
|
||||||
'first_action_id': None,
|
'first_action_id': None,
|
||||||
'state': objects.action_plan.State.RECOMMENDED
|
'state': objects.action_plan.State.RECOMMENDED
|
||||||
|
|||||||
@@ -245,75 +245,70 @@ Querying metrics
|
|||||||
|
|
||||||
A large set of metrics, generated by OpenStack modules, can be used in your
|
A large set of metrics, generated by OpenStack modules, can be used in your
|
||||||
strategy implementation. To collect these metrics, Watcher provides a
|
strategy implementation. To collect these metrics, Watcher provides a
|
||||||
`Helper`_ to the Ceilometer API, which makes this API reusable and easier
|
`Helper`_ for two data sources which are `Ceilometer`_ and `Monasca`_. If you
|
||||||
to used.
|
wish to query metrics from a different data source, you can implement your own
|
||||||
|
and directly use it from within your new strategy. Indeed, strategies in
|
||||||
|
Watcher have the cluster data models decoupled from the data sources which
|
||||||
|
means that you may keep the former while changing the latter.
|
||||||
|
The recommended way for you to support a new data source is to implement a new
|
||||||
|
helper that would encapsulate within separate methods the queries you need to
|
||||||
|
perform. To then use it, you would just have to instantiate it within your
|
||||||
|
strategy.
|
||||||
|
|
||||||
If you want to use your own metrics database backend, please refer to the
|
If you want to use Ceilometer but with your own metrics database backend,
|
||||||
`Ceilometer developer guide`_. Indeed, Ceilometer's pluggable model allows
|
please refer to the `Ceilometer developer guide`_. The list of the available
|
||||||
for various types of backends. A list of the available backends is located
|
Ceilometer backends is located here_. The `Ceilosca`_ project is a good example
|
||||||
here_. The Ceilosca project is a good example of how to create your own
|
of how to create your own pluggable backend. Moreover, if your strategy
|
||||||
pluggable backend.
|
requires new metrics not covered by Ceilometer, you can add them through a
|
||||||
|
`Ceilometer plugin`_.
|
||||||
|
|
||||||
Finally, if your strategy requires new metrics not covered by Ceilometer, you
|
|
||||||
can add them through a Ceilometer `plugin`_.
|
|
||||||
|
|
||||||
.. _`Helper`: https://github.com/openstack/watcher/blob/master/watcher/decision_engine/cluster/history/ceilometer.py
|
.. _`Helper`: https://github.com/openstack/watcher/blob/master/watcher/decision_engine/cluster/history/ceilometer.py
|
||||||
.. _`Ceilometer developer guide`: http://docs.openstack.org/developer/ceilometer/architecture.html#storing-the-data
|
.. _`Ceilometer developer guide`: http://docs.openstack.org/developer/ceilometer/architecture.html#storing-the-data
|
||||||
|
.. _`Ceilometer`: http://docs.openstack.org/developer/ceilometer/
|
||||||
|
.. _`Monasca`: https://github.com/openstack/monasca-api/blob/master/docs/monasca-api-spec.md
|
||||||
.. _`here`: http://docs.openstack.org/developer/ceilometer/install/dbreco.html#choosing-a-database-backend
|
.. _`here`: http://docs.openstack.org/developer/ceilometer/install/dbreco.html#choosing-a-database-backend
|
||||||
.. _`plugin`: http://docs.openstack.org/developer/ceilometer/plugins.html
|
.. _`Ceilometer plugin`: http://docs.openstack.org/developer/ceilometer/plugins.html
|
||||||
.. _`Ceilosca`: https://github.com/openstack/monasca-ceilometer/blob/master/ceilosca/ceilometer/storage/impl_monasca.py
|
.. _`Ceilosca`: https://github.com/openstack/monasca-ceilometer/blob/master/ceilosca/ceilometer/storage/impl_monasca.py
|
||||||
|
|
||||||
|
Read usage metrics using the Watcher Datasource Helper
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
Read usage metrics using the Python binding
|
The following code snippet shows how to invoke a Datasource Helper class:
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
You can find the information about the Ceilometer Python binding on the
|
|
||||||
OpenStack `ceilometer client python API documentation
|
|
||||||
<http://docs.openstack.org/developer/python-ceilometerclient/api.html>`_
|
|
||||||
|
|
||||||
To facilitate the process, Watcher provides the ``osc`` attribute to every
|
|
||||||
strategy which includes clients to major OpenStack services, including
|
|
||||||
Ceilometer. So to access it within your strategy, you can do the following:
|
|
||||||
|
|
||||||
.. code-block:: py
|
.. code-block:: py
|
||||||
|
|
||||||
# Within your strategy "execute()"
|
from watcher.datasource import ceilometer as ceil
|
||||||
cclient = self.osc.ceilometer
|
from watcher.datasource import monasca as mon
|
||||||
# TODO: Do something here
|
|
||||||
|
@property
|
||||||
|
def ceilometer(self):
|
||||||
|
if self._ceilometer is None:
|
||||||
|
self._ceilometer = ceil.CeilometerHelper(osc=self.osc)
|
||||||
|
return self._ceilometer
|
||||||
|
|
||||||
|
@property
|
||||||
|
def monasca(self):
|
||||||
|
if self._monasca is None:
|
||||||
|
self._monasca = mon.MonascaHelper(osc=self.osc)
|
||||||
|
return self._monasca
|
||||||
|
|
||||||
Using that you can now query the values for that specific metric:
|
Using that you can now query the values for that specific metric:
|
||||||
|
|
||||||
.. code-block:: py
|
.. code-block:: py
|
||||||
|
|
||||||
query = None # e.g. [{'field': 'foo', 'op': 'le', 'value': 34},]
|
if self.config.datasource == "ceilometer":
|
||||||
value_cpu = cclient.samples.list(
|
resource_id = "%s_%s" % (node.uuid, node.hostname)
|
||||||
meter_name='cpu_util',
|
return self.ceilometer.statistic_aggregation(
|
||||||
limit=10, q=query)
|
resource_id=resource_id,
|
||||||
|
meter_name='compute.node.cpu.percent',
|
||||||
|
period="7200",
|
||||||
Read usage metrics using the Watcher Cluster History Helper
|
aggregate='avg',
|
||||||
-----------------------------------------------------------
|
)
|
||||||
|
elif self.config.datasource == "monasca":
|
||||||
Here below is the abstract ``BaseClusterHistory`` class of the Helper.
|
statistics = self.monasca.statistic_aggregation(
|
||||||
|
meter_name='compute.node.cpu.percent',
|
||||||
.. autoclass:: watcher.decision_engine.cluster.history.base.BaseClusterHistory
|
dimensions=dict(hostname=node.uuid),
|
||||||
:members:
|
period=7200,
|
||||||
:noindex:
|
aggregate='avg'
|
||||||
|
)
|
||||||
The following code snippet shows how to create a Cluster History class:
|
|
||||||
|
|
||||||
.. code-block:: py
|
|
||||||
|
|
||||||
from watcher.decision_engine.cluster.history import ceilometer as ceil
|
|
||||||
|
|
||||||
query_history = ceil.CeilometerClusterHistory()
|
|
||||||
|
|
||||||
Using that you can now query the values for that specific metric:
|
|
||||||
|
|
||||||
.. code-block:: py
|
|
||||||
|
|
||||||
query_history.statistic_aggregation(resource_id=compute_node.uuid,
|
|
||||||
meter_name='compute.node.cpu.percent',
|
|
||||||
period="7200",
|
|
||||||
aggregate='avg'
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -101,12 +101,6 @@ Cluster Data Model (CDM)
|
|||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.model.collector.base
|
.. watcher-term:: watcher.decision_engine.model.collector.base
|
||||||
|
|
||||||
.. _cluster_history_definition:
|
|
||||||
|
|
||||||
Cluster History
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.cluster.history.base
|
|
||||||
|
|
||||||
.. _controller_node_definition:
|
.. _controller_node_definition:
|
||||||
|
|
||||||
@@ -132,7 +126,7 @@ Compute node
|
|||||||
============
|
============
|
||||||
|
|
||||||
Please, read `the official OpenStack definition of a Compute Node
|
Please, read `the official OpenStack definition of a Compute Node
|
||||||
<http://docs.openstack.org/openstack-ops/content/compute_nodes.html>`_.
|
<http://docs.openstack.org/ops-guide/arch-compute-nodes.html>`_.
|
||||||
|
|
||||||
.. _customer_definition:
|
.. _customer_definition:
|
||||||
|
|
||||||
|
|||||||
BIN
doc/source/image_src/plantuml/action_plan_state_machine.png
Normal file
BIN
doc/source/image_src/plantuml/action_plan_state_machine.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
@@ -9,8 +9,10 @@ FAILED --> DELETED : Administrator removes\nAction Plan
|
|||||||
SUCCEEDED --> DELETED : Administrator removes\nAction Plan
|
SUCCEEDED --> DELETED : Administrator removes\nAction Plan
|
||||||
ONGOING --> CANCELLED : Administrator cancels\nAction Plan
|
ONGOING --> CANCELLED : Administrator cancels\nAction Plan
|
||||||
RECOMMENDED --> CANCELLED : Administrator cancels\nAction Plan
|
RECOMMENDED --> CANCELLED : Administrator cancels\nAction Plan
|
||||||
|
RECOMMENDED --> SUPERSEDED : The Watcher Decision Engine supersedes\nAction Plan
|
||||||
PENDING --> CANCELLED : Administrator cancels\nAction Plan
|
PENDING --> CANCELLED : Administrator cancels\nAction Plan
|
||||||
CANCELLED --> DELETED
|
CANCELLED --> DELETED
|
||||||
|
SUPERSEDED --> DELETED
|
||||||
DELETED --> [*]
|
DELETED --> [*]
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
|
|||||||
BIN
doc/source/image_src/plantuml/watcher_db_schema_diagram.png
Normal file
BIN
doc/source/image_src/plantuml/watcher_db_schema_diagram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@@ -60,6 +60,7 @@ table(audits) {
|
|||||||
interval : Integer, nullable
|
interval : Integer, nullable
|
||||||
parameters : JSONEncodedDict, nullable
|
parameters : JSONEncodedDict, nullable
|
||||||
scope : JSONEncodedList, nullable
|
scope : JSONEncodedList, nullable
|
||||||
|
auto_trigger: Boolean
|
||||||
|
|
||||||
created_at : DateTime
|
created_at : DateTime
|
||||||
updated_at : DateTime
|
updated_at : DateTime
|
||||||
@@ -73,7 +74,6 @@ table(action_plans) {
|
|||||||
foreign_key("audit_id : Integer, nullable")
|
foreign_key("audit_id : Integer, nullable")
|
||||||
foreign_key("strategy_id : Integer")
|
foreign_key("strategy_id : Integer")
|
||||||
uuid : String[36]
|
uuid : String[36]
|
||||||
first_action_id : Integer
|
|
||||||
state : String[20], nullable
|
state : String[20], nullable
|
||||||
global_efficacy : JSONEncodedDict, nullable
|
global_efficacy : JSONEncodedDict, nullable
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ table(actions) {
|
|||||||
action_type : String[255]
|
action_type : String[255]
|
||||||
input_parameters : JSONEncodedDict, nullable
|
input_parameters : JSONEncodedDict, nullable
|
||||||
state : String[20], nullable
|
state : String[20], nullable
|
||||||
next : String[36], nullable
|
parents : JSONEncodedList, nullable
|
||||||
|
|
||||||
created_at : DateTime
|
created_at : DateTime
|
||||||
updated_at : DateTime
|
updated_at : DateTime
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 58 KiB |
@@ -71,6 +71,9 @@ parameter type default Value description
|
|||||||
be tried by the strategy while
|
be tried by the strategy while
|
||||||
searching for potential candidates.
|
searching for potential candidates.
|
||||||
To remove the limit, set it to 0
|
To remove the limit, set it to 0
|
||||||
|
``period`` Number 7200 The time interval in seconds
|
||||||
|
for getting statistic aggregation
|
||||||
|
from metric data source
|
||||||
====================== ====== ============= ===================================
|
====================== ====== ============= ===================================
|
||||||
|
|
||||||
Efficacy Indicator
|
Efficacy Indicator
|
||||||
|
|||||||
101
doc/source/strategies/outlet_temp_control.rst
Normal file
101
doc/source/strategies/outlet_temp_control.rst
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
=================================
|
||||||
|
Outlet Temperature Based Strategy
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
--------
|
||||||
|
|
||||||
|
**display name**: ``outlet_temperature``
|
||||||
|
|
||||||
|
**goal**: ``thermal_optimization``
|
||||||
|
|
||||||
|
Outlet (Exhaust Air) temperature is a new thermal telemetry which can be
|
||||||
|
used to measure the host's thermal/workload status. This strategy makes
|
||||||
|
decisions to migrate workloads to the hosts with good thermal condition
|
||||||
|
(lowest outlet temperature) when the outlet temperature of source hosts
|
||||||
|
reach a configurable threshold.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
This strategy has a dependency on the host having Intel's Power
|
||||||
|
Node Manager 3.0 or later enabled.
|
||||||
|
|
||||||
|
|
||||||
|
Metrics
|
||||||
|
*******
|
||||||
|
|
||||||
|
The *outlet_temperature* strategy requires the following metrics:
|
||||||
|
|
||||||
|
========================================= ============ ======= =======
|
||||||
|
metric service name plugins comment
|
||||||
|
========================================= ============ ======= =======
|
||||||
|
``hardware.ipmi.node.outlet_temperature`` ceilometer_ IPMI
|
||||||
|
========================================= ============ ======= =======
|
||||||
|
|
||||||
|
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#ipmi-based-meters
|
||||||
|
|
||||||
|
Cluster data model
|
||||||
|
******************
|
||||||
|
|
||||||
|
Default Watcher's Compute cluster data model:
|
||||||
|
|
||||||
|
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
|
||||||
|
|
||||||
|
Actions
|
||||||
|
*******
|
||||||
|
|
||||||
|
Default Watcher's actions:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:widths: 30 30
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - action
|
||||||
|
- description
|
||||||
|
* - ``migration``
|
||||||
|
- .. watcher-term:: watcher.applier.actions.migration.Migrate
|
||||||
|
|
||||||
|
Planner
|
||||||
|
*******
|
||||||
|
|
||||||
|
Default Watcher's planner:
|
||||||
|
|
||||||
|
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Strategy parameter is:
|
||||||
|
|
||||||
|
============== ====== ============= ====================================
|
||||||
|
parameter type default Value description
|
||||||
|
============== ====== ============= ====================================
|
||||||
|
``threshold`` Number 35.0 Temperature threshold for migration
|
||||||
|
============== ====== ============= ====================================
|
||||||
|
|
||||||
|
Efficacy Indicator
|
||||||
|
------------------
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
---------
|
||||||
|
|
||||||
|
For more information on the Outlet Temperature Based Strategy please refer to:
|
||||||
|
https://specs.openstack.org/openstack/watcher-specs/specs/mitaka/implemented/outlet-temperature-based-strategy.html
|
||||||
|
|
||||||
|
How to use it ?
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ openstack optimize audittemplate create \
|
||||||
|
at1 thermal_optimization --strategy outlet_temperature
|
||||||
|
|
||||||
|
$ openstack optimize audit create -a at1 -p threshold=31.0
|
||||||
|
|
||||||
|
External Links
|
||||||
|
--------------
|
||||||
|
|
||||||
|
- `Intel Power Node Manager 3.0 <http://www.intel.com/content/www/us/en/power-management/intelligent-power-node-manager-3-0-specification.html>`_
|
||||||
107
doc/source/strategies/uniform_airflow.rst
Normal file
107
doc/source/strategies/uniform_airflow.rst
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
==================================
|
||||||
|
Uniform Airflow Migration Strategy
|
||||||
|
==================================
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
--------
|
||||||
|
|
||||||
|
**display name**: ``uniform_airflow``
|
||||||
|
|
||||||
|
**goal**: ``airflow_optimization``
|
||||||
|
|
||||||
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.uniform_airflow
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
This strategy has a dependency on the server having Intel's Power
|
||||||
|
Node Manager 3.0 or later enabled.
|
||||||
|
|
||||||
|
Metrics
|
||||||
|
*******
|
||||||
|
|
||||||
|
The *uniform_airflow* strategy requires the following metrics:
|
||||||
|
|
||||||
|
================================== ============ ======= =======
|
||||||
|
metric service name plugins comment
|
||||||
|
================================== ============ ======= =======
|
||||||
|
``hardware.ipmi.node.airflow`` ceilometer_ IPMI
|
||||||
|
``hardware.ipmi.node.temperature`` ceilometer_ IPMI
|
||||||
|
``hardware.ipmi.node.power`` ceilometer_ IPMI
|
||||||
|
================================== ============ ======= =======
|
||||||
|
|
||||||
|
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#ipmi-based-meters
|
||||||
|
|
||||||
|
Cluster data model
|
||||||
|
******************
|
||||||
|
|
||||||
|
Default Watcher's Compute cluster data model:
|
||||||
|
|
||||||
|
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
|
||||||
|
|
||||||
|
Actions
|
||||||
|
*******
|
||||||
|
|
||||||
|
Default Watcher's actions:
|
||||||
|
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:widths: 30 30
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - action
|
||||||
|
- description
|
||||||
|
* - ``migration``
|
||||||
|
- .. watcher-term:: watcher.applier.actions.migration.Migrate
|
||||||
|
|
||||||
|
Planner
|
||||||
|
*******
|
||||||
|
|
||||||
|
Default Watcher's planner:
|
||||||
|
|
||||||
|
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Strategy parameters are:
|
||||||
|
|
||||||
|
====================== ====== ============= ===========================
|
||||||
|
parameter type default Value description
|
||||||
|
====================== ====== ============= ===========================
|
||||||
|
``threshold_airflow`` Number 400.0 Airflow threshold for
|
||||||
|
migration Unit is 0.1CFM
|
||||||
|
``threshold_inlet_t`` Number 28.0 Inlet temperature threshold
|
||||||
|
for migration decision
|
||||||
|
``threshold_power`` Number 350.0 System power threshold for
|
||||||
|
migration decision
|
||||||
|
``period`` Number 300 Aggregate time period of
|
||||||
|
ceilometer
|
||||||
|
====================== ====== ============= ===========================
|
||||||
|
|
||||||
|
Efficacy Indicator
|
||||||
|
------------------
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
---------
|
||||||
|
|
||||||
|
For more information on the Uniform Airflow Migration Strategy please refer to:
|
||||||
|
https://specs.openstack.org/openstack/watcher-specs/specs/newton/implemented/uniform-airflow-migration-strategy.html
|
||||||
|
|
||||||
|
How to use it ?
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ openstack optimize audittemplate create \
|
||||||
|
at1 airflow_optimization --strategy uniform_airflow
|
||||||
|
|
||||||
|
$ openstack optimize audit create -a at1 -p threshold_airflow=410 \
|
||||||
|
-p threshold_inlet_t=29.0 -p threshold_power=355.0 -p period=310
|
||||||
|
|
||||||
|
External Links
|
||||||
|
--------------
|
||||||
|
|
||||||
|
- `Intel Power Node Manager 3.0 <http://www.intel.com/content/www/us/en/power-management/intelligent-power-node-manager-3-0-specification.html>`_
|
||||||
@@ -89,7 +89,7 @@ How to use it ?
|
|||||||
.. code-block:: shell
|
.. code-block:: shell
|
||||||
|
|
||||||
$ openstack optimize audittemplate create \
|
$ openstack optimize audittemplate create \
|
||||||
at1 vm_consolidation --strategy vm_workload_consolidation
|
at1 server_consolidation --strategy vm_workload_consolidation
|
||||||
|
|
||||||
$ openstack optimize audit create -a at1
|
$ openstack optimize audit create -a at1
|
||||||
|
|
||||||
|
|||||||
@@ -92,12 +92,22 @@ parameter type default Value description
|
|||||||
host from list.
|
host from list.
|
||||||
``retry_count`` number 1 Count of random returned
|
``retry_count`` number 1 Count of random returned
|
||||||
hosts.
|
hosts.
|
||||||
|
``periods`` object |periods| These periods are used to get
|
||||||
|
statistic aggregation for
|
||||||
|
instance and host metrics.
|
||||||
|
The period is simply a
|
||||||
|
repeating interval of time
|
||||||
|
into which the samples are
|
||||||
|
grouped for aggregation.
|
||||||
|
Watcher uses only the last
|
||||||
|
period of all recieved ones.
|
||||||
==================== ====== ===================== =============================
|
==================== ====== ===================== =============================
|
||||||
|
|
||||||
.. |metrics| replace:: ["cpu_util", "memory.resident"]
|
.. |metrics| replace:: ["cpu_util", "memory.resident"]
|
||||||
.. |thresholds| replace:: {"cpu_util": 0.2, "memory.resident": 0.2}
|
.. |thresholds| replace:: {"cpu_util": 0.2, "memory.resident": 0.2}
|
||||||
.. |weights| replace:: {"cpu_util_weight": 1.0, "memory.resident_weight": 1.0}
|
.. |weights| replace:: {"cpu_util_weight": 1.0, "memory.resident_weight": 1.0}
|
||||||
.. |instance_metrics| replace:: {"cpu_util": "hardware.cpu.util", "memory.resident": "hardware.memory.used"}
|
.. |instance_metrics| replace:: {"cpu_util": "compute.node.cpu.percent", "memory.resident": "hardware.memory.used"}
|
||||||
|
.. |periods| replace:: {"instance": 720, "node": 600}
|
||||||
|
|
||||||
Efficacy Indicator
|
Efficacy Indicator
|
||||||
------------------
|
------------------
|
||||||
|
|||||||
98
doc/source/strategies/workload_balance.rst
Normal file
98
doc/source/strategies/workload_balance.rst
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
===================================
|
||||||
|
Workload Balance Migration Strategy
|
||||||
|
===================================
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
--------
|
||||||
|
|
||||||
|
**display name**: ``workload_balance``
|
||||||
|
|
||||||
|
**goal**: ``workload_balancing``
|
||||||
|
|
||||||
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.workload_balance
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
Metrics
|
||||||
|
*******
|
||||||
|
|
||||||
|
The *workload_balance* strategy requires the following metrics:
|
||||||
|
|
||||||
|
======================= ============ ======= =======
|
||||||
|
metric service name plugins comment
|
||||||
|
======================= ============ ======= =======
|
||||||
|
``cpu_util`` ceilometer_ none
|
||||||
|
======================= ============ ======= =======
|
||||||
|
|
||||||
|
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#openstack-compute
|
||||||
|
|
||||||
|
|
||||||
|
Cluster data model
|
||||||
|
******************
|
||||||
|
|
||||||
|
Default Watcher's Compute cluster data model:
|
||||||
|
|
||||||
|
.. watcher-term:: watcher.decision_engine.model.collector.nova.NovaClusterDataModelCollector
|
||||||
|
|
||||||
|
Actions
|
||||||
|
*******
|
||||||
|
|
||||||
|
Default Watcher's actions:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:widths: 30 30
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - action
|
||||||
|
- description
|
||||||
|
* - ``migration``
|
||||||
|
- .. watcher-term:: watcher.applier.actions.migration.Migrate
|
||||||
|
|
||||||
|
Planner
|
||||||
|
*******
|
||||||
|
|
||||||
|
Default Watcher's planner:
|
||||||
|
|
||||||
|
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Strategy parameters are:
|
||||||
|
|
||||||
|
============== ====== ============= ====================================
|
||||||
|
parameter type default Value description
|
||||||
|
============== ====== ============= ====================================
|
||||||
|
``threshold`` Number 25.0 Workload threshold for migration
|
||||||
|
``period`` Number 300 Aggregate time period of ceilometer
|
||||||
|
============== ====== ============= ====================================
|
||||||
|
|
||||||
|
Efficacy Indicator
|
||||||
|
------------------
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
Algorithm
|
||||||
|
---------
|
||||||
|
|
||||||
|
For more information on the Workload Balance Migration Strategy please refer
|
||||||
|
to: https://specs.openstack.org/openstack/watcher-specs/specs/mitaka/implemented/workload-balance-migration-strategy.html
|
||||||
|
|
||||||
|
How to use it ?
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ openstack optimize audittemplate create \
|
||||||
|
at1 workload_balancing --strategy workload_balance
|
||||||
|
|
||||||
|
$ openstack optimize audit create -a at1 -p threshold=26.0 \
|
||||||
|
-p period=310
|
||||||
|
|
||||||
|
External Links
|
||||||
|
--------------
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
To generate the sample watcher.conf file, run the following
|
To generate the sample watcher.conf file, run the following
|
||||||
command from the top level of the watcher directory:
|
command from the top level of the watcher directory:
|
||||||
|
|
||||||
tox -econfig
|
tox -e genconfig
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ To launch this task with configured Rally you just need to run:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
rally task start watcher/rally-jobs/watcher.yaml
|
rally task start watcher/rally-jobs/watcher-watcher.yaml
|
||||||
|
|
||||||
Structure
|
Structure
|
||||||
---------
|
---------
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
name: "dummy"
|
name: "dummy"
|
||||||
strategy:
|
strategy:
|
||||||
name: "dummy"
|
name: "dummy"
|
||||||
extra: {}
|
|
||||||
sla:
|
sla:
|
||||||
failure_rate:
|
failure_rate:
|
||||||
max: 0
|
max: 0
|
||||||
@@ -29,7 +28,6 @@
|
|||||||
name: "dummy"
|
name: "dummy"
|
||||||
strategy:
|
strategy:
|
||||||
name: "dummy"
|
name: "dummy"
|
||||||
extra: {}
|
|
||||||
runner:
|
runner:
|
||||||
type: "constant"
|
type: "constant"
|
||||||
times: 10
|
times: 10
|
||||||
@@ -56,12 +54,10 @@
|
|||||||
name: "workload_balancing"
|
name: "workload_balancing"
|
||||||
strategy:
|
strategy:
|
||||||
name: "workload_stabilization"
|
name: "workload_stabilization"
|
||||||
extra: {}
|
|
||||||
- goal:
|
- goal:
|
||||||
name: "dummy"
|
name: "dummy"
|
||||||
strategy:
|
strategy:
|
||||||
name: "dummy"
|
name: "dummy"
|
||||||
extra: {}
|
|
||||||
sla:
|
sla:
|
||||||
failure_rate:
|
failure_rate:
|
||||||
max: 0
|
max: 0
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add notifications related to Action plan object.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
features:
|
features:
|
||||||
- Added a generic scoring engine module, which
|
- Added a generic scoring engine module, which
|
||||||
will standarize interactions with scoring engines
|
will standardize interactions with scoring engines
|
||||||
through the common API. It is possible to use the
|
through the common API. It is possible to use the
|
||||||
scoring engine by different Strategies, which
|
scoring engine by different Strategies, which
|
||||||
improve the code and data model re-use.
|
improve the code and data model re-use.
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add notifications related to Audit object.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Watcher can continuously optimize the OpenStack cloud for a specific
|
||||||
|
strategy or goal by triggering an audit periodically which generates
|
||||||
|
an action plan and run it automatically.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Centralize all configuration options for Watcher.
|
||||||
3
releasenotes/notes/db-migration-e1a705a8b54ccdd2.yaml
Normal file
3
releasenotes/notes/db-migration-e1a705a8b54ccdd2.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Watcher database can now be upgraded thanks to Alembic.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Provides a generic way to define the scope of an audit. The set of audited
|
||||||
|
resources will be called "Audit scope" and will be defined in each audit
|
||||||
|
template (which contains the audit settings).
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
features:
|
features:
|
||||||
- Added a way to return the of available goals depending
|
- Added a way to return the of available goals depending
|
||||||
on which strategies have been deployed on the node
|
on which strategies have been deployed on the node
|
||||||
where the decison engine is running.
|
where the decision engine is running.
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- The graph model describes how VMs are associated to compute hosts.
|
||||||
|
This allows for seeing relationships upfront between the entities and hence
|
||||||
|
can be used to identify hot/cold spots in the data center and influence
|
||||||
|
a strategy decision.
|
||||||
4
releasenotes/notes/monasca-support-0b0486b8572ac38b.yaml
Normal file
4
releasenotes/notes/monasca-support-0b0486b8572ac38b.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Watcher supports multiple metrics backend and relies on Ceilometer and
|
||||||
|
Monasca.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Watcher can now run specific actions in parallel improving the performances
|
||||||
|
dramatically when executing an action plan.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add superseded state for an action plan if the cluster data model has
|
||||||
|
changed after it has been created.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Provide a notification mechanism into Watcher that supports versioning.
|
||||||
|
Whenever a Watcher object is created, updated or deleted, a versioned
|
||||||
|
notification will, if it's relevant, be automatically sent to notify in order
|
||||||
|
to allow an event-driven style of architecture within Watcher. Moreover, it
|
||||||
|
will also give other services and/or 3rd party softwares (e.g. monitoring
|
||||||
|
solutions or rules engines) the ability to react to such events.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add a service supervisor to watch Watcher deamons.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- all Watcher objects have been refactored to support OVO
|
||||||
|
(oslo.versionedobjects) which was a prerequisite step in order to implement
|
||||||
|
versioned notifications.
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# watcher documentation build configuration file, created by
|
# watcher documentation build configuration file, created by
|
||||||
# sphinx-quickstart on Fri Jun 3 11:37:52 2016.
|
# sphinx-quickstart on Fri Jun 3 11:37:52 2016.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -5,23 +5,23 @@
|
|||||||
apscheduler # MIT License
|
apscheduler # MIT License
|
||||||
enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
|
enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
|
||||||
jsonpatch>=1.1 # BSD
|
jsonpatch>=1.1 # BSD
|
||||||
keystoneauth1>=2.14.0 # Apache-2.0
|
keystoneauth1>=2.18.0 # Apache-2.0
|
||||||
keystonemiddleware!=4.5.0,>=4.2.0 # Apache-2.0
|
keystonemiddleware>=4.12.0 # Apache-2.0
|
||||||
lxml>=2.3 # BSD
|
lxml!=3.7.0,>=2.3 # BSD
|
||||||
oslo.concurrency>=3.8.0 # Apache-2.0
|
oslo.concurrency>=3.8.0 # Apache-2.0
|
||||||
oslo.cache>=1.5.0 # Apache-2.0
|
oslo.cache>=1.5.0 # Apache-2.0
|
||||||
oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
|
oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
|
||||||
oslo.context>=2.9.0 # Apache-2.0
|
oslo.context>=2.9.0 # Apache-2.0
|
||||||
oslo.db!=4.13.1,!=4.13.2,>=4.11.0 # Apache-2.0
|
oslo.db>=4.15.0 # Apache-2.0
|
||||||
oslo.i18n>=2.1.0 # Apache-2.0
|
oslo.i18n>=2.1.0 # Apache-2.0
|
||||||
oslo.log>=3.11.0 # Apache-2.0
|
oslo.log>=3.11.0 # Apache-2.0
|
||||||
oslo.messaging>=5.2.0 # Apache-2.0
|
oslo.messaging>=5.14.0 # Apache-2.0
|
||||||
oslo.policy>=1.15.0 # Apache-2.0
|
oslo.policy>=1.17.0 # Apache-2.0
|
||||||
oslo.reports>=0.6.0 # Apache-2.0
|
oslo.reports>=0.6.0 # Apache-2.0
|
||||||
oslo.serialization>=1.10.0 # Apache-2.0
|
oslo.serialization>=1.10.0 # Apache-2.0
|
||||||
oslo.service>=1.10.0 # Apache-2.0
|
oslo.service>=1.10.0 # Apache-2.0
|
||||||
oslo.utils>=3.18.0 # Apache-2.0
|
oslo.utils>=3.18.0 # Apache-2.0
|
||||||
oslo.versionedobjects>=1.13.0 # Apache-2.0
|
oslo.versionedobjects>=1.17.0 # Apache-2.0
|
||||||
PasteDeploy>=1.5.0 # MIT
|
PasteDeploy>=1.5.0 # MIT
|
||||||
pbr>=1.8 # Apache-2.0
|
pbr>=1.8 # Apache-2.0
|
||||||
pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD
|
pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD
|
||||||
@@ -30,13 +30,16 @@ voluptuous>=0.8.9 # BSD License
|
|||||||
python-ceilometerclient>=2.5.0 # Apache-2.0
|
python-ceilometerclient>=2.5.0 # Apache-2.0
|
||||||
python-cinderclient!=1.7.0,!=1.7.1,>=1.6.0 # Apache-2.0
|
python-cinderclient!=1.7.0,!=1.7.1,>=1.6.0 # Apache-2.0
|
||||||
python-glanceclient>=2.5.0 # Apache-2.0
|
python-glanceclient>=2.5.0 # Apache-2.0
|
||||||
python-keystoneclient>=3.6.0 # Apache-2.0
|
python-keystoneclient>=3.8.0 # Apache-2.0
|
||||||
|
python-monascaclient>=1.1.0 # Apache-2.0
|
||||||
python-neutronclient>=5.1.0 # Apache-2.0
|
python-neutronclient>=5.1.0 # Apache-2.0
|
||||||
python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
|
python-novaclient!=7.0.0,>=6.0.0 # Apache-2.0
|
||||||
python-openstackclient>=3.3.0 # Apache-2.0
|
python-openstackclient>=3.3.0 # Apache-2.0
|
||||||
six>=1.9.0 # MIT
|
six>=1.9.0 # MIT
|
||||||
SQLAlchemy<1.1.0,>=1.0.10 # MIT
|
SQLAlchemy<1.1.0,>=1.0.10 # MIT
|
||||||
stevedore>=1.17.1 # Apache-2.0
|
stevedore>=1.17.1 # Apache-2.0
|
||||||
taskflow>=1.26.0 # Apache-2.0
|
taskflow>=2.7.0 # Apache-2.0
|
||||||
WebOb>=1.6.0 # MIT
|
WebOb>=1.6.0 # MIT
|
||||||
WSME>=0.8 # MIT
|
WSME>=0.8 # MIT
|
||||||
|
networkx>=1.10 # BSD
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ watcher_scoring_engine_containers =
|
|||||||
watcher_strategies =
|
watcher_strategies =
|
||||||
dummy = watcher.decision_engine.strategy.strategies.dummy_strategy:DummyStrategy
|
dummy = watcher.decision_engine.strategy.strategies.dummy_strategy:DummyStrategy
|
||||||
dummy_with_scorer = watcher.decision_engine.strategy.strategies.dummy_with_scorer:DummyWithScorer
|
dummy_with_scorer = watcher.decision_engine.strategy.strategies.dummy_with_scorer:DummyWithScorer
|
||||||
|
dummy_with_resize = watcher.decision_engine.strategy.strategies.dummy_with_resize:DummyWithResize
|
||||||
basic = watcher.decision_engine.strategy.strategies.basic_consolidation:BasicConsolidation
|
basic = watcher.decision_engine.strategy.strategies.basic_consolidation:BasicConsolidation
|
||||||
outlet_temperature = watcher.decision_engine.strategy.strategies.outlet_temp_control:OutletTempControl
|
outlet_temperature = watcher.decision_engine.strategy.strategies.outlet_temp_control:OutletTempControl
|
||||||
vm_workload_consolidation = watcher.decision_engine.strategy.strategies.vm_workload_consolidation:VMWorkloadConsolidation
|
vm_workload_consolidation = watcher.decision_engine.strategy.strategies.vm_workload_consolidation:VMWorkloadConsolidation
|
||||||
@@ -76,12 +77,14 @@ watcher_actions =
|
|||||||
nop = watcher.applier.actions.nop:Nop
|
nop = watcher.applier.actions.nop:Nop
|
||||||
sleep = watcher.applier.actions.sleep:Sleep
|
sleep = watcher.applier.actions.sleep:Sleep
|
||||||
change_nova_service_state = watcher.applier.actions.change_nova_service_state:ChangeNovaServiceState
|
change_nova_service_state = watcher.applier.actions.change_nova_service_state:ChangeNovaServiceState
|
||||||
|
resize = watcher.applier.actions.resize:Resize
|
||||||
|
|
||||||
watcher_workflow_engines =
|
watcher_workflow_engines =
|
||||||
taskflow = watcher.applier.workflow_engine.default:DefaultWorkFlowEngine
|
taskflow = watcher.applier.workflow_engine.default:DefaultWorkFlowEngine
|
||||||
|
|
||||||
watcher_planners =
|
watcher_planners =
|
||||||
default = watcher.decision_engine.planner.default:DefaultPlanner
|
weight = watcher.decision_engine.planner.weight:WeightPlanner
|
||||||
|
workload_stabilization = watcher.decision_engine.planner.workload_stabilization:WorkloadStabilizationPlanner
|
||||||
|
|
||||||
watcher_cluster_data_model_collectors =
|
watcher_cluster_data_model_collectors =
|
||||||
compute = watcher.decision_engine.model.collector.nova:NovaClusterDataModelCollector
|
compute = watcher.decision_engine.model.collector.nova:NovaClusterDataModelCollector
|
||||||
|
|||||||
10
tox.ini
10
tox.ini
@@ -6,9 +6,7 @@ skipsdist = True
|
|||||||
[testenv]
|
[testenv]
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
whitelist_externals = find
|
whitelist_externals = find
|
||||||
install_command =
|
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/ocata} {opts} {packages}
|
||||||
constraints: pip install -U --force-reinstall -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
|
||||||
pip install -U {opts} {packages}
|
|
||||||
setenv =
|
setenv =
|
||||||
VIRTUAL_ENV={envdir}
|
VIRTUAL_ENV={envdir}
|
||||||
deps = -r{toxinidir}/test-requirements.txt
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
@@ -27,7 +25,9 @@ setenv = PYTHONHASHSEED=0
|
|||||||
commands = {posargs}
|
commands = {posargs}
|
||||||
|
|
||||||
[testenv:cover]
|
[testenv:cover]
|
||||||
commands = python setup.py testr --coverage --testr-args='{posargs}'
|
commands =
|
||||||
|
python setup.py testr --coverage --testr-args='{posargs}'
|
||||||
|
coverage report
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
setenv = PYTHONHASHSEED=0
|
setenv = PYTHONHASHSEED=0
|
||||||
@@ -38,7 +38,7 @@ commands =
|
|||||||
[testenv:debug]
|
[testenv:debug]
|
||||||
commands = oslo_debug_helper -t watcher/tests {posargs}
|
commands = oslo_debug_helper -t watcher/tests {posargs}
|
||||||
|
|
||||||
[testenv:config]
|
[testenv:genconfig]
|
||||||
sitepackages = False
|
sitepackages = False
|
||||||
commands =
|
commands =
|
||||||
oslo-config-generator --config-file etc/watcher/watcher-config-generator.conf
|
oslo-config-generator --config-file etc/watcher/watcher-config-generator.conf
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
@@ -17,19 +18,10 @@
|
|||||||
|
|
||||||
"""Access Control Lists (ACL's) control access the API server."""
|
"""Access Control Lists (ACL's) control access the API server."""
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
from watcher.api.middleware import auth_token
|
from watcher.api.middleware import auth_token
|
||||||
|
from watcher import conf
|
||||||
|
|
||||||
|
CONF = conf.CONF
|
||||||
AUTH_OPTS = [
|
|
||||||
cfg.BoolOpt('enable_authentication',
|
|
||||||
default=True,
|
|
||||||
help='This option enables or disables user authentication '
|
|
||||||
'via keystone. Default value is True.'),
|
|
||||||
]
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
CONF.register_opts(AUTH_OPTS)
|
|
||||||
|
|
||||||
|
|
||||||
def install(app, conf, public_routes):
|
def install(app, conf, public_routes):
|
||||||
@@ -42,7 +34,7 @@ def install(app, conf, public_routes):
|
|||||||
:return: The same WSGI application with ACL installed.
|
:return: The same WSGI application with ACL installed.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not cfg.CONF.get('enable_authentication'):
|
if not CONF.get('enable_authentication'):
|
||||||
return app
|
return app
|
||||||
return auth_token.AuthTokenMiddleware(app,
|
return auth_token.AuthTokenMiddleware(app,
|
||||||
conf=dict(conf),
|
conf=dict(conf),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
@@ -16,49 +17,14 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
import pecan
|
import pecan
|
||||||
|
|
||||||
from watcher._i18n import _
|
|
||||||
from watcher.api import acl
|
from watcher.api import acl
|
||||||
from watcher.api import config as api_config
|
from watcher.api import config as api_config
|
||||||
from watcher.api import middleware
|
from watcher.api import middleware
|
||||||
|
from watcher import conf
|
||||||
|
|
||||||
# Register options for the service
|
CONF = conf.CONF
|
||||||
API_SERVICE_OPTS = [
|
|
||||||
cfg.PortOpt('port',
|
|
||||||
default=9322,
|
|
||||||
help=_('The port for the watcher API server')),
|
|
||||||
cfg.StrOpt('host',
|
|
||||||
default='127.0.0.1',
|
|
||||||
help=_('The listen IP for the watcher API server')),
|
|
||||||
cfg.IntOpt('max_limit',
|
|
||||||
default=1000,
|
|
||||||
help=_('The maximum number of items returned in a single '
|
|
||||||
'response from a collection resource')),
|
|
||||||
cfg.IntOpt('workers',
|
|
||||||
min=1,
|
|
||||||
help=_('Number of workers for Watcher API service. '
|
|
||||||
'The default is equal to the number of CPUs available '
|
|
||||||
'if that can be determined, else a default worker '
|
|
||||||
'count of 1 is returned.')),
|
|
||||||
|
|
||||||
cfg.BoolOpt('enable_ssl_api',
|
|
||||||
default=False,
|
|
||||||
help=_("Enable the integrated stand-alone API to service "
|
|
||||||
"requests via HTTPS instead of HTTP. If there is a "
|
|
||||||
"front-end service performing HTTPS offloading from "
|
|
||||||
"the service, this option should be False; note, you "
|
|
||||||
"will want to change public API endpoint to represent "
|
|
||||||
"SSL termination URL with 'public_endpoint' option.")),
|
|
||||||
]
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
opt_group = cfg.OptGroup(name='api',
|
|
||||||
title='Options for the watcher-api service')
|
|
||||||
|
|
||||||
CONF.register_group(opt_group)
|
|
||||||
CONF.register_opts(API_SERVICE_OPTS, opt_group)
|
|
||||||
|
|
||||||
|
|
||||||
def get_pecan_config():
|
def get_pecan_config():
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ be one of the following:
|
|||||||
processed by the :ref:`Watcher Applier <watcher_applier_definition>`
|
processed by the :ref:`Watcher Applier <watcher_applier_definition>`
|
||||||
- **SUCCEEDED** : the :ref:`Action <action_definition>` has been executed
|
- **SUCCEEDED** : the :ref:`Action <action_definition>` has been executed
|
||||||
successfully
|
successfully
|
||||||
- **FAILED** : an error occured while trying to execute the
|
- **FAILED** : an error occurred while trying to execute the
|
||||||
:ref:`Action <action_definition>`
|
:ref:`Action <action_definition>`
|
||||||
- **DELETED** : the :ref:`Action <action_definition>` is still stored in the
|
- **DELETED** : the :ref:`Action <action_definition>` is still stored in the
|
||||||
:ref:`Watcher database <watcher_database_definition>` but is not returned
|
:ref:`Watcher database <watcher_database_definition>` but is not returned
|
||||||
@@ -88,7 +88,6 @@ class Action(base.APIBase):
|
|||||||
between the internal object model and the API representation of a action.
|
between the internal object model and the API representation of a action.
|
||||||
"""
|
"""
|
||||||
_action_plan_uuid = None
|
_action_plan_uuid = None
|
||||||
_next_uuid = None
|
|
||||||
|
|
||||||
def _get_action_plan_uuid(self):
|
def _get_action_plan_uuid(self):
|
||||||
return self._action_plan_uuid
|
return self._action_plan_uuid
|
||||||
@@ -105,22 +104,6 @@ class Action(base.APIBase):
|
|||||||
except exception.ActionPlanNotFound:
|
except exception.ActionPlanNotFound:
|
||||||
self._action_plan_uuid = None
|
self._action_plan_uuid = None
|
||||||
|
|
||||||
def _get_next_uuid(self):
|
|
||||||
return self._next_uuid
|
|
||||||
|
|
||||||
def _set_next_uuid(self, value):
|
|
||||||
if value == wtypes.Unset:
|
|
||||||
self._next_uuid = wtypes.Unset
|
|
||||||
elif value and self._next_uuid != value:
|
|
||||||
try:
|
|
||||||
action_next = objects.Action.get(
|
|
||||||
pecan.request.context, value)
|
|
||||||
self._next_uuid = action_next.uuid
|
|
||||||
self.next = action_next.id
|
|
||||||
except exception.ActionNotFound:
|
|
||||||
self.action_next_uuid = None
|
|
||||||
# raise e
|
|
||||||
|
|
||||||
uuid = wtypes.wsattr(types.uuid, readonly=True)
|
uuid = wtypes.wsattr(types.uuid, readonly=True)
|
||||||
"""Unique UUID for this action"""
|
"""Unique UUID for this action"""
|
||||||
|
|
||||||
@@ -138,10 +121,8 @@ class Action(base.APIBase):
|
|||||||
input_parameters = types.jsontype
|
input_parameters = types.jsontype
|
||||||
"""One or more key/value pairs """
|
"""One or more key/value pairs """
|
||||||
|
|
||||||
next_uuid = wsme.wsproperty(types.uuid, _get_next_uuid,
|
parents = wtypes.wsattr(types.jsontype, readonly=True)
|
||||||
_set_next_uuid,
|
"""UUIDs of parent actions"""
|
||||||
mandatory=True)
|
|
||||||
"""This next action UUID"""
|
|
||||||
|
|
||||||
links = wsme.wsattr([link.Link], readonly=True)
|
links = wsme.wsattr([link.Link], readonly=True)
|
||||||
"""A list containing a self link and associated action links"""
|
"""A list containing a self link and associated action links"""
|
||||||
@@ -152,7 +133,6 @@ class Action(base.APIBase):
|
|||||||
self.fields = []
|
self.fields = []
|
||||||
fields = list(objects.Action.fields)
|
fields = list(objects.Action.fields)
|
||||||
fields.append('action_plan_uuid')
|
fields.append('action_plan_uuid')
|
||||||
fields.append('next_uuid')
|
|
||||||
for field in fields:
|
for field in fields:
|
||||||
# Skip fields we do not expose.
|
# Skip fields we do not expose.
|
||||||
if not hasattr(self, field):
|
if not hasattr(self, field):
|
||||||
@@ -163,15 +143,13 @@ class Action(base.APIBase):
|
|||||||
self.fields.append('action_plan_id')
|
self.fields.append('action_plan_id')
|
||||||
setattr(self, 'action_plan_uuid', kwargs.get('action_plan_id',
|
setattr(self, 'action_plan_uuid', kwargs.get('action_plan_id',
|
||||||
wtypes.Unset))
|
wtypes.Unset))
|
||||||
setattr(self, 'next_uuid', kwargs.get('next',
|
|
||||||
wtypes.Unset))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _convert_with_links(action, url, expand=True):
|
def _convert_with_links(action, url, expand=True):
|
||||||
if not expand:
|
if not expand:
|
||||||
action.unset_fields_except(['uuid', 'state', 'next', 'next_uuid',
|
action.unset_fields_except(['uuid', 'state', 'action_plan_uuid',
|
||||||
'action_plan_uuid', 'action_plan_id',
|
'action_plan_id', 'action_type',
|
||||||
'action_type'])
|
'parents'])
|
||||||
|
|
||||||
action.links = [link.Link.make_link('self', url,
|
action.links = [link.Link.make_link('self', url,
|
||||||
'actions', action.uuid),
|
'actions', action.uuid),
|
||||||
@@ -193,9 +171,9 @@ class Action(base.APIBase):
|
|||||||
state='PENDING',
|
state='PENDING',
|
||||||
created_at=datetime.datetime.utcnow(),
|
created_at=datetime.datetime.utcnow(),
|
||||||
deleted_at=None,
|
deleted_at=None,
|
||||||
updated_at=datetime.datetime.utcnow())
|
updated_at=datetime.datetime.utcnow(),
|
||||||
|
parents=[])
|
||||||
sample._action_plan_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
|
sample._action_plan_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
|
||||||
sample._next_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
|
|
||||||
return cls._convert_with_links(sample, 'http://localhost:9322', expand)
|
return cls._convert_with_links(sample, 'http://localhost:9322', expand)
|
||||||
|
|
||||||
|
|
||||||
@@ -216,17 +194,6 @@ class ActionCollection(collection.Collection):
|
|||||||
collection.actions = [Action.convert_with_links(p, expand)
|
collection.actions = [Action.convert_with_links(p, expand)
|
||||||
for p in actions]
|
for p in actions]
|
||||||
|
|
||||||
if 'sort_key' in kwargs:
|
|
||||||
reverse = False
|
|
||||||
if kwargs['sort_key'] == 'next_uuid':
|
|
||||||
if 'sort_dir' in kwargs:
|
|
||||||
reverse = True if kwargs['sort_dir'] == 'desc' else False
|
|
||||||
collection.actions = sorted(
|
|
||||||
collection.actions,
|
|
||||||
key=lambda action: action.next_uuid or '',
|
|
||||||
reverse=reverse)
|
|
||||||
|
|
||||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
|
||||||
return collection
|
return collection
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -268,10 +235,7 @@ class ActionsController(rest.RestController):
|
|||||||
if audit_uuid:
|
if audit_uuid:
|
||||||
filters['audit_uuid'] = audit_uuid
|
filters['audit_uuid'] = audit_uuid
|
||||||
|
|
||||||
if sort_key == 'next_uuid':
|
sort_db_key = sort_key
|
||||||
sort_db_key = None
|
|
||||||
else:
|
|
||||||
sort_db_key = sort_key
|
|
||||||
|
|
||||||
actions = objects.Action.list(pecan.request.context,
|
actions = objects.Action.list(pecan.request.context,
|
||||||
limit,
|
limit,
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class ActionPlanPatchType(types.JsonPatchType):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mandatory_attrs():
|
def mandatory_attrs():
|
||||||
return ["audit_id", "state", "first_action_id"]
|
return ["audit_id", "state"]
|
||||||
|
|
||||||
|
|
||||||
class ActionPlan(base.APIBase):
|
class ActionPlan(base.APIBase):
|
||||||
@@ -120,7 +120,6 @@ class ActionPlan(base.APIBase):
|
|||||||
_audit_uuid = None
|
_audit_uuid = None
|
||||||
_strategy_uuid = None
|
_strategy_uuid = None
|
||||||
_strategy_name = None
|
_strategy_name = None
|
||||||
_first_action_uuid = None
|
|
||||||
_efficacy_indicators = None
|
_efficacy_indicators = None
|
||||||
|
|
||||||
def _get_audit_uuid(self):
|
def _get_audit_uuid(self):
|
||||||
@@ -137,21 +136,6 @@ class ActionPlan(base.APIBase):
|
|||||||
except exception.AuditNotFound:
|
except exception.AuditNotFound:
|
||||||
self._audit_uuid = None
|
self._audit_uuid = None
|
||||||
|
|
||||||
def _get_first_action_uuid(self):
|
|
||||||
return self._first_action_uuid
|
|
||||||
|
|
||||||
def _set_first_action_uuid(self, value):
|
|
||||||
if value == wtypes.Unset:
|
|
||||||
self._first_action_uuid = wtypes.Unset
|
|
||||||
elif value and self._first_action_uuid != value:
|
|
||||||
try:
|
|
||||||
first_action = objects.Action.get(pecan.request.context,
|
|
||||||
value)
|
|
||||||
self._first_action_uuid = first_action.uuid
|
|
||||||
self.first_action_id = first_action.id
|
|
||||||
except exception.ActionNotFound:
|
|
||||||
self._first_action_uuid = None
|
|
||||||
|
|
||||||
def _get_efficacy_indicators(self):
|
def _get_efficacy_indicators(self):
|
||||||
if self._efficacy_indicators is None:
|
if self._efficacy_indicators is None:
|
||||||
self._set_efficacy_indicators(wtypes.Unset)
|
self._set_efficacy_indicators(wtypes.Unset)
|
||||||
@@ -220,11 +204,6 @@ class ActionPlan(base.APIBase):
|
|||||||
uuid = wtypes.wsattr(types.uuid, readonly=True)
|
uuid = wtypes.wsattr(types.uuid, readonly=True)
|
||||||
"""Unique UUID for this action plan"""
|
"""Unique UUID for this action plan"""
|
||||||
|
|
||||||
first_action_uuid = wsme.wsproperty(
|
|
||||||
types.uuid, _get_first_action_uuid, _set_first_action_uuid,
|
|
||||||
mandatory=True)
|
|
||||||
"""The UUID of the first action this action plans links to"""
|
|
||||||
|
|
||||||
audit_uuid = wsme.wsproperty(types.uuid, _get_audit_uuid, _set_audit_uuid,
|
audit_uuid = wsme.wsproperty(types.uuid, _get_audit_uuid, _set_audit_uuid,
|
||||||
mandatory=True)
|
mandatory=True)
|
||||||
"""The UUID of the audit this port belongs to"""
|
"""The UUID of the audit this port belongs to"""
|
||||||
@@ -263,7 +242,6 @@ class ActionPlan(base.APIBase):
|
|||||||
setattr(self, field, kwargs.get(field, wtypes.Unset))
|
setattr(self, field, kwargs.get(field, wtypes.Unset))
|
||||||
|
|
||||||
self.fields.append('audit_uuid')
|
self.fields.append('audit_uuid')
|
||||||
self.fields.append('first_action_uuid')
|
|
||||||
self.fields.append('efficacy_indicators')
|
self.fields.append('efficacy_indicators')
|
||||||
|
|
||||||
setattr(self, 'audit_uuid', kwargs.get('audit_id', wtypes.Unset))
|
setattr(self, 'audit_uuid', kwargs.get('audit_id', wtypes.Unset))
|
||||||
@@ -271,16 +249,13 @@ class ActionPlan(base.APIBase):
|
|||||||
setattr(self, 'strategy_uuid', kwargs.get('strategy_id', wtypes.Unset))
|
setattr(self, 'strategy_uuid', kwargs.get('strategy_id', wtypes.Unset))
|
||||||
fields.append('strategy_name')
|
fields.append('strategy_name')
|
||||||
setattr(self, 'strategy_name', kwargs.get('strategy_id', wtypes.Unset))
|
setattr(self, 'strategy_name', kwargs.get('strategy_id', wtypes.Unset))
|
||||||
setattr(self, 'first_action_uuid',
|
|
||||||
kwargs.get('first_action_id', wtypes.Unset))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _convert_with_links(action_plan, url, expand=True):
|
def _convert_with_links(action_plan, url, expand=True):
|
||||||
if not expand:
|
if not expand:
|
||||||
action_plan.unset_fields_except(
|
action_plan.unset_fields_except(
|
||||||
['uuid', 'state', 'efficacy_indicators', 'global_efficacy',
|
['uuid', 'state', 'efficacy_indicators', 'global_efficacy',
|
||||||
'updated_at', 'audit_uuid', 'strategy_uuid', 'strategy_name',
|
'updated_at', 'audit_uuid', 'strategy_uuid', 'strategy_name'])
|
||||||
'first_action_uuid'])
|
|
||||||
|
|
||||||
action_plan.links = [
|
action_plan.links = [
|
||||||
link.Link.make_link(
|
link.Link.make_link(
|
||||||
@@ -305,7 +280,6 @@ class ActionPlan(base.APIBase):
|
|||||||
created_at=datetime.datetime.utcnow(),
|
created_at=datetime.datetime.utcnow(),
|
||||||
deleted_at=None,
|
deleted_at=None,
|
||||||
updated_at=datetime.datetime.utcnow())
|
updated_at=datetime.datetime.utcnow())
|
||||||
sample._first_action_uuid = '57eaf9ab-5aaa-4f7e-bdf7-9a140ac7a720'
|
|
||||||
sample._audit_uuid = 'abcee106-14d3-4515-b744-5a26885cf6f6'
|
sample._audit_uuid = 'abcee106-14d3-4515-b744-5a26885cf6f6'
|
||||||
sample._efficacy_indicators = [{'description': 'Test indicator',
|
sample._efficacy_indicators = [{'description': 'Test indicator',
|
||||||
'name': 'test_indicator',
|
'name': 'test_indicator',
|
||||||
@@ -481,7 +455,8 @@ class ActionPlansController(rest.RestController):
|
|||||||
:param action_plan_uuid: UUID of a action.
|
:param action_plan_uuid: UUID of a action.
|
||||||
"""
|
"""
|
||||||
context = pecan.request.context
|
context = pecan.request.context
|
||||||
action_plan = api_utils.get_resource('ActionPlan', action_plan_uuid)
|
action_plan = api_utils.get_resource(
|
||||||
|
'ActionPlan', action_plan_uuid, eager=True)
|
||||||
policy.enforce(context, 'action_plan:delete', action_plan,
|
policy.enforce(context, 'action_plan:delete', action_plan,
|
||||||
action='action_plan:delete')
|
action='action_plan:delete')
|
||||||
|
|
||||||
@@ -496,13 +471,12 @@ class ActionPlansController(rest.RestController):
|
|||||||
:param action_plan_uuid: UUID of a action plan.
|
:param action_plan_uuid: UUID of a action plan.
|
||||||
:param patch: a json PATCH document to apply to this action plan.
|
:param patch: a json PATCH document to apply to this action plan.
|
||||||
"""
|
"""
|
||||||
launch_action_plan = True
|
|
||||||
if self.from_actionsPlans:
|
if self.from_actionsPlans:
|
||||||
raise exception.OperationNotPermitted
|
raise exception.OperationNotPermitted
|
||||||
|
|
||||||
context = pecan.request.context
|
context = pecan.request.context
|
||||||
action_plan_to_update = api_utils.get_resource('ActionPlan',
|
action_plan_to_update = api_utils.get_resource(
|
||||||
action_plan_uuid)
|
'ActionPlan', action_plan_uuid, eager=True)
|
||||||
policy.enforce(context, 'action_plan:update', action_plan_to_update,
|
policy.enforce(context, 'action_plan:update', action_plan_to_update,
|
||||||
action='action_plan:update')
|
action='action_plan:update')
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,21 @@ from watcher.decision_engine import rpcapi
|
|||||||
from watcher import objects
|
from watcher import objects
|
||||||
|
|
||||||
|
|
||||||
|
ALLOWED_AUDIT_TRANSITIONS = {
|
||||||
|
objects.audit.State.PENDING:
|
||||||
|
[objects.audit.State.ONGOING, objects.audit.State.CANCELLED],
|
||||||
|
objects.audit.State.ONGOING:
|
||||||
|
[objects.audit.State.FAILED, objects.audit.State.SUCCEEDED,
|
||||||
|
objects.audit.State.CANCELLED],
|
||||||
|
objects.audit.State.FAILED:
|
||||||
|
[objects.audit.State.DELETED],
|
||||||
|
objects.audit.State.SUCCEEDED:
|
||||||
|
[objects.audit.State.DELETED],
|
||||||
|
objects.audit.State.CANCELLED:
|
||||||
|
[objects.audit.State.DELETED]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class AuditPostType(wtypes.Base):
|
class AuditPostType(wtypes.Base):
|
||||||
|
|
||||||
audit_template_uuid = wtypes.wsattr(types.uuid, mandatory=False)
|
audit_template_uuid = wtypes.wsattr(types.uuid, mandatory=False)
|
||||||
@@ -69,6 +84,8 @@ class AuditPostType(wtypes.Base):
|
|||||||
|
|
||||||
scope = wtypes.wsattr(types.jsontype, readonly=True)
|
scope = wtypes.wsattr(types.jsontype, readonly=True)
|
||||||
|
|
||||||
|
auto_trigger = wtypes.wsattr(bool, mandatory=False)
|
||||||
|
|
||||||
def as_audit(self, context):
|
def as_audit(self, context):
|
||||||
audit_type_values = [val.value for val in objects.audit.AuditType]
|
audit_type_values = [val.value for val in objects.audit.AuditType]
|
||||||
if self.audit_type not in audit_type_values:
|
if self.audit_type not in audit_type_values:
|
||||||
@@ -115,7 +132,8 @@ class AuditPostType(wtypes.Base):
|
|||||||
goal_id=self.goal,
|
goal_id=self.goal,
|
||||||
strategy_id=self.strategy,
|
strategy_id=self.strategy,
|
||||||
interval=self.interval,
|
interval=self.interval,
|
||||||
scope=self.scope,)
|
scope=self.scope,
|
||||||
|
auto_trigger=self.auto_trigger)
|
||||||
|
|
||||||
|
|
||||||
class AuditPatchType(types.JsonPatchType):
|
class AuditPatchType(types.JsonPatchType):
|
||||||
@@ -257,6 +275,9 @@ class Audit(base.APIBase):
|
|||||||
scope = wsme.wsattr(types.jsontype, mandatory=False)
|
scope = wsme.wsattr(types.jsontype, mandatory=False)
|
||||||
"""Audit Scope"""
|
"""Audit Scope"""
|
||||||
|
|
||||||
|
auto_trigger = wsme.wsattr(bool, mandatory=False, default=False)
|
||||||
|
"""Autoexecute action plan once audit is succeeded"""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.fields = []
|
self.fields = []
|
||||||
fields = list(objects.Audit.fields)
|
fields = list(objects.Audit.fields)
|
||||||
@@ -313,7 +334,8 @@ class Audit(base.APIBase):
|
|||||||
deleted_at=None,
|
deleted_at=None,
|
||||||
updated_at=datetime.datetime.utcnow(),
|
updated_at=datetime.datetime.utcnow(),
|
||||||
interval=7200,
|
interval=7200,
|
||||||
scope=[])
|
scope=[],
|
||||||
|
auto_trigger=False)
|
||||||
|
|
||||||
sample.goal_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
|
sample.goal_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
|
||||||
sample.strategy_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ff'
|
sample.strategy_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ff'
|
||||||
@@ -525,7 +547,6 @@ class AuditsController(rest.RestController):
|
|||||||
pecan.response.location = link.build_url('audits', new_audit.uuid)
|
pecan.response.location = link.build_url('audits', new_audit.uuid)
|
||||||
|
|
||||||
# trigger decision-engine to run the audit
|
# trigger decision-engine to run the audit
|
||||||
|
|
||||||
if new_audit.audit_type == objects.audit.AuditType.ONESHOT.value:
|
if new_audit.audit_type == objects.audit.AuditType.ONESHOT.value:
|
||||||
dc_client = rpcapi.DecisionEngineAPI()
|
dc_client = rpcapi.DecisionEngineAPI()
|
||||||
dc_client.trigger_audit(context, new_audit.uuid)
|
dc_client.trigger_audit(context, new_audit.uuid)
|
||||||
@@ -555,6 +576,17 @@ class AuditsController(rest.RestController):
|
|||||||
except api_utils.JSONPATCH_EXCEPTIONS as e:
|
except api_utils.JSONPATCH_EXCEPTIONS as e:
|
||||||
raise exception.PatchError(patch=patch, reason=e)
|
raise exception.PatchError(patch=patch, reason=e)
|
||||||
|
|
||||||
|
initial_state = audit_dict['state']
|
||||||
|
new_state = api_utils.get_patch_value(patch, 'state')
|
||||||
|
allowed_states = ALLOWED_AUDIT_TRANSITIONS.get(initial_state, [])
|
||||||
|
if new_state is not None and new_state not in allowed_states:
|
||||||
|
error_message = _("State transition not allowed: "
|
||||||
|
"(%(initial_state)s -> %(new_state)s)")
|
||||||
|
raise exception.PatchError(
|
||||||
|
patch=patch,
|
||||||
|
reason=error_message % dict(
|
||||||
|
initial_state=initial_state, new_state=new_state))
|
||||||
|
|
||||||
# Update only the fields that have changed
|
# Update only the fields that have changed
|
||||||
for field in objects.Audit.fields:
|
for field in objects.Audit.fields:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -333,6 +333,7 @@ class AuditTemplate(base.APIBase):
|
|||||||
|
|
||||||
self.fields.append('goal_id')
|
self.fields.append('goal_id')
|
||||||
self.fields.append('strategy_id')
|
self.fields.append('strategy_id')
|
||||||
|
setattr(self, 'strategy_id', kwargs.get('strategy_id', wtypes.Unset))
|
||||||
|
|
||||||
# goal_uuid & strategy_uuid are not part of
|
# goal_uuid & strategy_uuid are not part of
|
||||||
# objects.AuditTemplate.fields because they're API-only attributes.
|
# objects.AuditTemplate.fields because they're API-only attributes.
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# coding: utf-8
|
|
||||||
#
|
|
||||||
# Copyright 2013 Red Hat, Inc.
|
# Copyright 2013 Red Hat, Inc.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -73,6 +73,12 @@ def apply_jsonpatch(doc, patch):
|
|||||||
return jsonpatch.apply_patch(doc, jsonpatch.JsonPatch(patch))
|
return jsonpatch.apply_patch(doc, jsonpatch.JsonPatch(patch))
|
||||||
|
|
||||||
|
|
||||||
|
def get_patch_value(patch, key):
|
||||||
|
for p in patch:
|
||||||
|
if p['op'] == 'replace' and p['path'] == '/%s' % key:
|
||||||
|
return p['value']
|
||||||
|
|
||||||
|
|
||||||
def as_filters_dict(**filters):
|
def as_filters_dict(**filters):
|
||||||
filters_dict = {}
|
filters_dict = {}
|
||||||
for filter_name, filter_value in filters.items():
|
for filter_name, filter_value in filters.items():
|
||||||
|
|||||||
@@ -20,32 +20,47 @@ from oslo_log import log
|
|||||||
|
|
||||||
from watcher.applier.action_plan import base
|
from watcher.applier.action_plan import base
|
||||||
from watcher.applier import default
|
from watcher.applier import default
|
||||||
|
from watcher import notifications
|
||||||
from watcher import objects
|
from watcher import objects
|
||||||
|
from watcher.objects import fields
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DefaultActionPlanHandler(base.BaseActionPlanHandler):
|
class DefaultActionPlanHandler(base.BaseActionPlanHandler):
|
||||||
|
|
||||||
def __init__(self, context, service, action_plan_uuid):
|
def __init__(self, context, service, action_plan_uuid):
|
||||||
super(DefaultActionPlanHandler, self).__init__()
|
super(DefaultActionPlanHandler, self).__init__()
|
||||||
self.ctx = context
|
self.ctx = context
|
||||||
self.service = service
|
self.service = service
|
||||||
self.action_plan_uuid = action_plan_uuid
|
self.action_plan_uuid = action_plan_uuid
|
||||||
|
|
||||||
def update_action_plan(self, uuid, state):
|
|
||||||
action_plan = objects.ActionPlan.get_by_uuid(self.ctx, uuid)
|
|
||||||
action_plan.state = state
|
|
||||||
action_plan.save()
|
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
try:
|
try:
|
||||||
self.update_action_plan(self.action_plan_uuid,
|
action_plan = objects.ActionPlan.get_by_uuid(
|
||||||
objects.action_plan.State.ONGOING)
|
self.ctx, self.action_plan_uuid, eager=True)
|
||||||
|
action_plan.state = objects.action_plan.State.ONGOING
|
||||||
|
action_plan.save()
|
||||||
|
notifications.action_plan.send_action_notification(
|
||||||
|
self.ctx, action_plan,
|
||||||
|
action=fields.NotificationAction.EXECUTION,
|
||||||
|
phase=fields.NotificationPhase.START)
|
||||||
|
|
||||||
applier = default.DefaultApplier(self.ctx, self.service)
|
applier = default.DefaultApplier(self.ctx, self.service)
|
||||||
applier.execute(self.action_plan_uuid)
|
applier.execute(self.action_plan_uuid)
|
||||||
state = objects.action_plan.State.SUCCEEDED
|
|
||||||
|
action_plan.state = objects.action_plan.State.SUCCEEDED
|
||||||
|
notifications.action_plan.send_action_notification(
|
||||||
|
self.ctx, action_plan,
|
||||||
|
action=fields.NotificationAction.EXECUTION,
|
||||||
|
phase=fields.NotificationPhase.END)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(e)
|
LOG.exception(e)
|
||||||
state = objects.action_plan.State.FAILED
|
action_plan.state = objects.action_plan.State.FAILED
|
||||||
|
notifications.action_plan.send_action_notification(
|
||||||
|
self.ctx, action_plan,
|
||||||
|
action=fields.NotificationAction.EXECUTION,
|
||||||
|
priority=fields.NotificationPriority.ERROR,
|
||||||
|
phase=fields.NotificationPhase.ERROR)
|
||||||
finally:
|
finally:
|
||||||
self.update_action_plan(self.action_plan_uuid, state)
|
action_plan.save()
|
||||||
|
|||||||
106
watcher/applier/actions/resize.py
Normal file
106
watcher/applier/actions/resize.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2017 Servionica
|
||||||
|
#
|
||||||
|
# Authors: Alexander Chadin <a.chadin@servionica.ru>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
from oslo_log import log
|
||||||
|
import six
|
||||||
|
import voluptuous
|
||||||
|
|
||||||
|
from watcher._i18n import _, _LC
|
||||||
|
from watcher.applier.actions import base
|
||||||
|
from watcher.common import nova_helper
|
||||||
|
from watcher.common import utils
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Resize(base.BaseAction):
|
||||||
|
"""Resizes a server with specified flavor.
|
||||||
|
|
||||||
|
This action will allow you to resize a server to another flavor.
|
||||||
|
|
||||||
|
The action schema is::
|
||||||
|
|
||||||
|
schema = Schema({
|
||||||
|
'resource_id': str, # should be a UUID
|
||||||
|
'flavor': str, # should be either ID or Name of Flavor
|
||||||
|
})
|
||||||
|
|
||||||
|
The `resource_id` is the UUID of the server to resize.
|
||||||
|
The `flavor` is the ID or Name of Flavor (Nova accepts either ID or Name
|
||||||
|
of Flavor to resize() function).
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 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)),
|
||||||
|
})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def instance_uuid(self):
|
||||||
|
return self.resource_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flavor(self):
|
||||||
|
return self.input_parameters.get(self.FLAVOR)
|
||||||
|
|
||||||
|
def resize(self):
|
||||||
|
nova = nova_helper.NovaHelper(osc=self.osc)
|
||||||
|
LOG.debug("Resize instance %s to %s flavor", self.instance_uuid,
|
||||||
|
self.flavor)
|
||||||
|
instance = nova.find_instance(self.instance_uuid)
|
||||||
|
result = None
|
||||||
|
if instance:
|
||||||
|
try:
|
||||||
|
result = nova.resize_instance(
|
||||||
|
instance_id=self.instance_uuid, flavor=self.flavor)
|
||||||
|
except Exception as exc:
|
||||||
|
LOG.exception(exc)
|
||||||
|
LOG.critical(
|
||||||
|
_LC("Unexpected error occurred. Resizing failed for "
|
||||||
|
"instance %s."), self.instance_uuid)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
return self.resize()
|
||||||
|
|
||||||
|
def revert(self):
|
||||||
|
return self.migrate(destination=self.source_node)
|
||||||
|
|
||||||
|
def pre_condition(self):
|
||||||
|
# TODO(jed): check if the instance exists / check if the instance is on
|
||||||
|
# the source_node
|
||||||
|
pass
|
||||||
|
|
||||||
|
def post_condition(self):
|
||||||
|
# TODO(jed): check extra parameters (network response, etc.)
|
||||||
|
pass
|
||||||
@@ -56,7 +56,7 @@ class DefaultApplier(base.BaseApplier):
|
|||||||
|
|
||||||
def execute(self, action_plan_uuid):
|
def execute(self, action_plan_uuid):
|
||||||
LOG.debug("Executing action plan %s ", action_plan_uuid)
|
LOG.debug("Executing action plan %s ", action_plan_uuid)
|
||||||
|
|
||||||
filters = {'action_plan_uuid': action_plan_uuid}
|
filters = {'action_plan_uuid': action_plan_uuid}
|
||||||
actions = objects.Action.list(self.context,
|
actions = objects.Action.list(self.context, filters=filters)
|
||||||
filters=filters)
|
|
||||||
return self.engine.execute(actions)
|
return self.engine.execute(actions)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# Copyright (c) 2015 b<>com
|
# Copyright (c) 2015 b<>com
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
#
|
#
|
||||||
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
|
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
|
||||||
#
|
#
|
||||||
@@ -17,41 +18,12 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from watcher.applier.messaging import trigger
|
from watcher.applier.messaging import trigger
|
||||||
from watcher.common import service_manager
|
from watcher.common import service_manager
|
||||||
|
|
||||||
CONF = cfg.CONF
|
from watcher import conf
|
||||||
|
|
||||||
|
CONF = conf.CONF
|
||||||
# Register options
|
|
||||||
APPLIER_MANAGER_OPTS = [
|
|
||||||
cfg.IntOpt('workers',
|
|
||||||
default='1',
|
|
||||||
min=1,
|
|
||||||
required=True,
|
|
||||||
help='Number of workers for applier, default value is 1.'),
|
|
||||||
cfg.StrOpt('conductor_topic',
|
|
||||||
default='watcher.applier.control',
|
|
||||||
help='The topic name used for'
|
|
||||||
'control events, this topic '
|
|
||||||
'used for rpc call '),
|
|
||||||
cfg.StrOpt('publisher_id',
|
|
||||||
default='watcher.applier.api',
|
|
||||||
help='The identifier used by watcher '
|
|
||||||
'module on the message broker'),
|
|
||||||
cfg.StrOpt('workflow_engine',
|
|
||||||
default='taskflow',
|
|
||||||
required=True,
|
|
||||||
help='Select the engine to use to execute the workflow')
|
|
||||||
]
|
|
||||||
|
|
||||||
opt_group = cfg.OptGroup(name='watcher_applier',
|
|
||||||
title='Options for the Applier messaging'
|
|
||||||
'core')
|
|
||||||
CONF.register_group(opt_group)
|
|
||||||
CONF.register_opts(APPLIER_MANAGER_OPTS, opt_group)
|
|
||||||
|
|
||||||
|
|
||||||
class ApplierManager(service_manager.ServiceManager):
|
class ApplierManager(service_manager.ServiceManager):
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# Copyright (c) 2015 b<>com
|
# Copyright (c) 2015 b<>com
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
#
|
#
|
||||||
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
|
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
|
||||||
#
|
#
|
||||||
@@ -16,18 +17,14 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from watcher.applier import manager
|
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher.common import service
|
from watcher.common import service
|
||||||
from watcher.common import service_manager
|
from watcher.common import service_manager
|
||||||
from watcher.common import utils
|
from watcher.common import utils
|
||||||
|
|
||||||
|
from watcher import conf
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = conf.CONF
|
||||||
CONF.register_group(manager.opt_group)
|
|
||||||
CONF.register_opts(manager.APPLIER_MANAGER_OPTS, manager.opt_group)
|
|
||||||
|
|
||||||
|
|
||||||
class ApplierAPI(service.Service):
|
class ApplierAPI(service.Service):
|
||||||
|
|||||||
@@ -15,10 +15,12 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from oslo_concurrency import processutils
|
||||||
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from taskflow import engines
|
from taskflow import engines
|
||||||
from taskflow.patterns import graph_flow as gf
|
from taskflow.patterns import graph_flow as gf
|
||||||
from taskflow import task
|
from taskflow import task as flow_task
|
||||||
|
|
||||||
from watcher._i18n import _LE, _LW, _LC
|
from watcher._i18n import _LE, _LW, _LC
|
||||||
from watcher.applier.workflow_engine import base
|
from watcher.applier.workflow_engine import base
|
||||||
@@ -48,6 +50,18 @@ class DefaultWorkFlowEngine(base.BaseWorkFlowEngine):
|
|||||||
# (True to allow v execution or False to not).
|
# (True to allow v execution or False to not).
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_opts(cls):
|
||||||
|
return [
|
||||||
|
cfg.IntOpt(
|
||||||
|
'max_workers',
|
||||||
|
default=processutils.get_worker_count(),
|
||||||
|
min=1,
|
||||||
|
required=True,
|
||||||
|
help='Number of workers for taskflow engine '
|
||||||
|
'to execute actions.')
|
||||||
|
]
|
||||||
|
|
||||||
def execute(self, actions):
|
def execute(self, actions):
|
||||||
try:
|
try:
|
||||||
# NOTE(jed) We want to have a strong separation of concern
|
# NOTE(jed) We want to have a strong separation of concern
|
||||||
@@ -56,34 +70,32 @@ class DefaultWorkFlowEngine(base.BaseWorkFlowEngine):
|
|||||||
# We want to provide the 'taskflow' engine by
|
# We want to provide the 'taskflow' engine by
|
||||||
# default although we still want to leave the possibility for
|
# default although we still want to leave the possibility for
|
||||||
# the users to change it.
|
# the users to change it.
|
||||||
# todo(jed) we need to change the way the actions are stored.
|
# The current implementation uses graph with linked actions.
|
||||||
# The current implementation only use a linked list of actions.
|
|
||||||
# todo(jed) add olso conf for retry and name
|
# todo(jed) add olso conf for retry and name
|
||||||
flow = gf.Flow("watcher_flow")
|
flow = gf.Flow("watcher_flow")
|
||||||
previous = None
|
actions_uuid = {}
|
||||||
for a in actions:
|
for a in actions:
|
||||||
task = TaskFlowActionContainer(a, self)
|
task = TaskFlowActionContainer(a, self)
|
||||||
flow.add(task)
|
flow.add(task)
|
||||||
if previous is None:
|
actions_uuid[a.uuid] = task
|
||||||
previous = task
|
|
||||||
# we have only one Action in the Action Plan
|
|
||||||
if len(actions) == 1:
|
|
||||||
nop = TaskFlowNop()
|
|
||||||
flow.add(nop)
|
|
||||||
flow.link(previous, nop)
|
|
||||||
else:
|
|
||||||
# decider == guard (UML)
|
|
||||||
flow.link(previous, task, decider=self.decider)
|
|
||||||
previous = task
|
|
||||||
|
|
||||||
e = engines.load(flow)
|
for a in actions:
|
||||||
|
for parent_id in a.parents:
|
||||||
|
flow.link(actions_uuid[parent_id], actions_uuid[a.uuid],
|
||||||
|
decider=self.decider)
|
||||||
|
|
||||||
|
e = engines.load(
|
||||||
|
flow, engine='parallel',
|
||||||
|
max_workers=self.config.max_workers)
|
||||||
e.run()
|
e.run()
|
||||||
|
|
||||||
|
return flow
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise exception.WorkflowExecutionException(error=e)
|
raise exception.WorkflowExecutionException(error=e)
|
||||||
|
|
||||||
|
|
||||||
class TaskFlowActionContainer(task.Task):
|
class TaskFlowActionContainer(flow_task.Task):
|
||||||
def __init__(self, db_action, engine):
|
def __init__(self, db_action, engine):
|
||||||
name = "action_type:{0} uuid:{1}".format(db_action.action_type,
|
name = "action_type:{0} uuid:{1}".format(db_action.action_type,
|
||||||
db_action.uuid)
|
db_action.uuid)
|
||||||
@@ -148,7 +160,7 @@ class TaskFlowActionContainer(task.Task):
|
|||||||
LOG.critical(_LC("Oops! We need a disaster recover plan."))
|
LOG.critical(_LC("Oops! We need a disaster recover plan."))
|
||||||
|
|
||||||
|
|
||||||
class TaskFlowNop(task.Task):
|
class TaskFlowNop(flow_task.Task):
|
||||||
"""This class is used in case of the workflow have only one Action.
|
"""This class is used in case of the workflow have only one Action.
|
||||||
|
|
||||||
We need at least two atoms to create a link.
|
We need at least two atoms to create a link.
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ def add_command_parsers(subparsers):
|
|||||||
"Optionally, use --revision to specify an alembic revision "
|
"Optionally, use --revision to specify an alembic revision "
|
||||||
"string to upgrade to.")
|
"string to upgrade to.")
|
||||||
parser.set_defaults(func=DBCommand.upgrade)
|
parser.set_defaults(func=DBCommand.upgrade)
|
||||||
parser.add_argument('--revision', nargs='?')
|
parser.add_argument('revision', nargs='?')
|
||||||
|
|
||||||
parser = subparsers.add_parser(
|
parser = subparsers.add_parser(
|
||||||
'downgrade',
|
'downgrade',
|
||||||
@@ -79,10 +79,10 @@ def add_command_parsers(subparsers):
|
|||||||
"While optional, one should generally use --revision to "
|
"While optional, one should generally use --revision to "
|
||||||
"specify the alembic revision string to downgrade to.")
|
"specify the alembic revision string to downgrade to.")
|
||||||
parser.set_defaults(func=DBCommand.downgrade)
|
parser.set_defaults(func=DBCommand.downgrade)
|
||||||
parser.add_argument('--revision', nargs='?')
|
parser.add_argument('revision', nargs='?')
|
||||||
|
|
||||||
parser = subparsers.add_parser('stamp')
|
parser = subparsers.add_parser('stamp')
|
||||||
parser.add_argument('--revision', nargs='?')
|
parser.add_argument('revision', nargs='?')
|
||||||
parser.set_defaults(func=DBCommand.stamp)
|
parser.set_defaults(func=DBCommand.stamp)
|
||||||
|
|
||||||
parser = subparsers.add_parser(
|
parser = subparsers.add_parser(
|
||||||
|
|||||||
@@ -15,51 +15,18 @@ from cinderclient import client as ciclient
|
|||||||
from glanceclient import client as glclient
|
from glanceclient import client as glclient
|
||||||
from keystoneauth1 import loading as ka_loading
|
from keystoneauth1 import loading as ka_loading
|
||||||
from keystoneclient import client as keyclient
|
from keystoneclient import client as keyclient
|
||||||
|
from monascaclient import client as monclient
|
||||||
from neutronclient.neutron import client as netclient
|
from neutronclient.neutron import client as netclient
|
||||||
from novaclient import client as nvclient
|
from novaclient import client as nvclient
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from watcher._i18n import _
|
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
|
|
||||||
|
from watcher import conf
|
||||||
|
|
||||||
NOVA_CLIENT_OPTS = [
|
CONF = conf.CONF
|
||||||
cfg.StrOpt('api_version',
|
|
||||||
default='2',
|
|
||||||
help=_('Version of Nova API to use in novaclient.'))]
|
|
||||||
|
|
||||||
GLANCE_CLIENT_OPTS = [
|
|
||||||
cfg.StrOpt('api_version',
|
|
||||||
default='2',
|
|
||||||
help=_('Version of Glance API to use in glanceclient.'))]
|
|
||||||
|
|
||||||
CINDER_CLIENT_OPTS = [
|
|
||||||
cfg.StrOpt('api_version',
|
|
||||||
default='2',
|
|
||||||
help=_('Version of Cinder API to use in cinderclient.'))]
|
|
||||||
|
|
||||||
CEILOMETER_CLIENT_OPTS = [
|
|
||||||
cfg.StrOpt('api_version',
|
|
||||||
default='2',
|
|
||||||
help=_('Version of Ceilometer API to use in '
|
|
||||||
'ceilometerclient.'))]
|
|
||||||
|
|
||||||
NEUTRON_CLIENT_OPTS = [
|
|
||||||
cfg.StrOpt('api_version',
|
|
||||||
default='2.0',
|
|
||||||
help=_('Version of Neutron API to use in neutronclient.'))]
|
|
||||||
|
|
||||||
cfg.CONF.register_opts(NOVA_CLIENT_OPTS, group='nova_client')
|
|
||||||
cfg.CONF.register_opts(GLANCE_CLIENT_OPTS, group='glance_client')
|
|
||||||
cfg.CONF.register_opts(CINDER_CLIENT_OPTS, group='cinder_client')
|
|
||||||
cfg.CONF.register_opts(CEILOMETER_CLIENT_OPTS, group='ceilometer_client')
|
|
||||||
cfg.CONF.register_opts(NEUTRON_CLIENT_OPTS, group='neutron_client')
|
|
||||||
|
|
||||||
_CLIENTS_AUTH_GROUP = 'watcher_clients_auth'
|
_CLIENTS_AUTH_GROUP = 'watcher_clients_auth'
|
||||||
|
|
||||||
ka_loading.register_auth_conf_options(cfg.CONF, _CLIENTS_AUTH_GROUP)
|
|
||||||
ka_loading.register_session_conf_options(cfg.CONF, _CLIENTS_AUTH_GROUP)
|
|
||||||
|
|
||||||
|
|
||||||
class OpenStackClients(object):
|
class OpenStackClients(object):
|
||||||
"""Convenience class to create and cache client instances."""
|
"""Convenience class to create and cache client instances."""
|
||||||
@@ -74,12 +41,13 @@ class OpenStackClients(object):
|
|||||||
self._glance = None
|
self._glance = None
|
||||||
self._cinder = None
|
self._cinder = None
|
||||||
self._ceilometer = None
|
self._ceilometer = None
|
||||||
|
self._monasca = None
|
||||||
self._neutron = None
|
self._neutron = None
|
||||||
|
|
||||||
def _get_keystone_session(self):
|
def _get_keystone_session(self):
|
||||||
auth = ka_loading.load_auth_from_conf_options(cfg.CONF,
|
auth = ka_loading.load_auth_from_conf_options(CONF,
|
||||||
_CLIENTS_AUTH_GROUP)
|
_CLIENTS_AUTH_GROUP)
|
||||||
sess = ka_loading.load_session_from_conf_options(cfg.CONF,
|
sess = ka_loading.load_session_from_conf_options(CONF,
|
||||||
_CLIENTS_AUTH_GROUP,
|
_CLIENTS_AUTH_GROUP,
|
||||||
auth=auth)
|
auth=auth)
|
||||||
return sess
|
return sess
|
||||||
@@ -95,7 +63,7 @@ class OpenStackClients(object):
|
|||||||
return self._session
|
return self._session
|
||||||
|
|
||||||
def _get_client_option(self, client, option):
|
def _get_client_option(self, client, option):
|
||||||
return getattr(getattr(cfg.CONF, '%s_client' % client), option)
|
return getattr(getattr(CONF, '%s_client' % client), option)
|
||||||
|
|
||||||
@exception.wrap_keystone_exception
|
@exception.wrap_keystone_exception
|
||||||
def keystone(self):
|
def keystone(self):
|
||||||
@@ -145,6 +113,35 @@ class OpenStackClients(object):
|
|||||||
session=self.session)
|
session=self.session)
|
||||||
return self._ceilometer
|
return self._ceilometer
|
||||||
|
|
||||||
|
@exception.wrap_keystone_exception
|
||||||
|
def monasca(self):
|
||||||
|
if self._monasca:
|
||||||
|
return self._monasca
|
||||||
|
|
||||||
|
monascaclient_version = self._get_client_option(
|
||||||
|
'monasca', 'api_version')
|
||||||
|
token = self.session.get_token()
|
||||||
|
watcher_clients_auth_config = CONF.get(_CLIENTS_AUTH_GROUP)
|
||||||
|
service_type = 'monitoring'
|
||||||
|
monasca_kwargs = {
|
||||||
|
'auth_url': watcher_clients_auth_config.auth_url,
|
||||||
|
'cert_file': watcher_clients_auth_config.certfile,
|
||||||
|
'insecure': watcher_clients_auth_config.insecure,
|
||||||
|
'key_file': watcher_clients_auth_config.keyfile,
|
||||||
|
'keystone_timeout': watcher_clients_auth_config.timeout,
|
||||||
|
'os_cacert': watcher_clients_auth_config.cafile,
|
||||||
|
'service_type': service_type,
|
||||||
|
'token': token,
|
||||||
|
'username': watcher_clients_auth_config.username,
|
||||||
|
'password': watcher_clients_auth_config.password,
|
||||||
|
}
|
||||||
|
endpoint = self.session.get_endpoint(service_type=service_type)
|
||||||
|
|
||||||
|
self._monasca = monclient.Client(
|
||||||
|
monascaclient_version, endpoint, **monasca_kwargs)
|
||||||
|
|
||||||
|
return self._monasca
|
||||||
|
|
||||||
@exception.wrap_keystone_exception
|
@exception.wrap_keystone_exception
|
||||||
def neutron(self):
|
def neutron(self):
|
||||||
if self._neutron:
|
if self._neutron:
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class RequestContext(context.RequestContext):
|
|||||||
self.domain_name = domain_name
|
self.domain_name = domain_name
|
||||||
self.domain_id = domain_id
|
self.domain_id = domain_id
|
||||||
self.auth_token_info = auth_token_info
|
self.auth_token_info = auth_token_info
|
||||||
self.user_id = user_id
|
self.user_id = user_id or user
|
||||||
self.project_id = project_id
|
self.project_id = project_id
|
||||||
if not timestamp:
|
if not timestamp:
|
||||||
timestamp = timeutils.utcnow()
|
timestamp = timeutils.utcnow()
|
||||||
|
|||||||
@@ -26,22 +26,16 @@ import functools
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from keystoneclient import exceptions as keystone_exceptions
|
from keystoneclient import exceptions as keystone_exceptions
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from watcher._i18n import _, _LE
|
from watcher._i18n import _, _LE
|
||||||
|
|
||||||
|
from watcher import conf
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
EXC_LOG_OPTS = [
|
CONF = conf.CONF
|
||||||
cfg.BoolOpt('fatal_exception_format_errors',
|
|
||||||
default=False,
|
|
||||||
help='Make exception message format errors fatal.'),
|
|
||||||
]
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
CONF.register_opts(EXC_LOG_OPTS)
|
|
||||||
|
|
||||||
|
|
||||||
def wrap_keystone_exception(func):
|
def wrap_keystone_exception(func):
|
||||||
@@ -180,6 +174,14 @@ class EagerlyLoadedAuditRequired(InvalidAudit):
|
|||||||
msg_fmt = _("Audit %(audit)s was not eagerly loaded")
|
msg_fmt = _("Audit %(audit)s was not eagerly loaded")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidActionPlan(Invalid):
|
||||||
|
msg_fmt = _("Action plan %(action_plan)s is invalid")
|
||||||
|
|
||||||
|
|
||||||
|
class EagerlyLoadedActionPlanRequired(InvalidActionPlan):
|
||||||
|
msg_fmt = _("Action plan %(action_plan)s was not eagerly loaded")
|
||||||
|
|
||||||
|
|
||||||
class InvalidUUID(Invalid):
|
class InvalidUUID(Invalid):
|
||||||
msg_fmt = _("Expected a uuid but received %(uuid)s")
|
msg_fmt = _("Expected a uuid but received %(uuid)s")
|
||||||
|
|
||||||
@@ -264,6 +266,12 @@ class ActionPlanReferenced(Invalid):
|
|||||||
"multiple actions")
|
"multiple actions")
|
||||||
|
|
||||||
|
|
||||||
|
class ActionPlanIsOngoing(Conflict):
|
||||||
|
msg_fmt = _("Action Plan %(action_plan)s is currently running. "
|
||||||
|
"New Action Plan %(new_action_plan)s will be set as "
|
||||||
|
"SUPERSEDED")
|
||||||
|
|
||||||
|
|
||||||
class ActionNotFound(ResourceNotFound):
|
class ActionNotFound(ResourceNotFound):
|
||||||
msg_fmt = _("Action %(action)s could not be found")
|
msg_fmt = _("Action %(action)s could not be found")
|
||||||
|
|
||||||
@@ -282,6 +290,10 @@ class ActionFilterCombinationProhibited(Invalid):
|
|||||||
"prohibited")
|
"prohibited")
|
||||||
|
|
||||||
|
|
||||||
|
class UnsupportedActionType(UnsupportedError):
|
||||||
|
msg_fmt = _("Provided %(action_type) is not supported yet")
|
||||||
|
|
||||||
|
|
||||||
class EfficacyIndicatorNotFound(ResourceNotFound):
|
class EfficacyIndicatorNotFound(ResourceNotFound):
|
||||||
msg_fmt = _("Efficacy indicator %(efficacy_indicator)s could not be found")
|
msg_fmt = _("Efficacy indicator %(efficacy_indicator)s could not be found")
|
||||||
|
|
||||||
@@ -340,6 +352,10 @@ class MetricCollectorNotDefined(WatcherException):
|
|||||||
msg_fmt = _("The metrics resource collector is not defined")
|
msg_fmt = _("The metrics resource collector is not defined")
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterStateStale(WatcherException):
|
||||||
|
msg_fmt = _("The cluster state is stale")
|
||||||
|
|
||||||
|
|
||||||
class ClusterDataModelCollectionError(WatcherException):
|
class ClusterDataModelCollectionError(WatcherException):
|
||||||
msg_fmt = _("The cluster data model '%(cdm)s' could not be built")
|
msg_fmt = _("The cluster data model '%(cdm)s' could not be built")
|
||||||
|
|
||||||
@@ -370,6 +386,11 @@ class NoMetricValuesForInstance(WatcherException):
|
|||||||
msg_fmt = _("No values returned by %(resource_id)s for %(metric_name)s.")
|
msg_fmt = _("No values returned by %(resource_id)s for %(metric_name)s.")
|
||||||
|
|
||||||
|
|
||||||
|
class UnsupportedDataSource(UnsupportedError):
|
||||||
|
msg_fmt = _("Datasource %(datasource)s is not supported "
|
||||||
|
"by strategy %(strategy)s")
|
||||||
|
|
||||||
|
|
||||||
class NoSuchMetricForHost(WatcherException):
|
class NoSuchMetricForHost(WatcherException):
|
||||||
msg_fmt = _("No %(metric)s metric for %(host)s found.")
|
msg_fmt = _("No %(metric)s metric for %(host)s found.")
|
||||||
|
|
||||||
@@ -389,11 +410,15 @@ class WildcardCharacterIsUsed(WatcherException):
|
|||||||
|
|
||||||
# Model
|
# Model
|
||||||
|
|
||||||
class InstanceNotFound(WatcherException):
|
class ComputeResourceNotFound(WatcherException):
|
||||||
|
msg_fmt = _("The compute resource '%(name)s' could not be found")
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceNotFound(ComputeResourceNotFound):
|
||||||
msg_fmt = _("The instance '%(name)s' could not be found")
|
msg_fmt = _("The instance '%(name)s' could not be found")
|
||||||
|
|
||||||
|
|
||||||
class ComputeNodeNotFound(WatcherException):
|
class ComputeNodeNotFound(ComputeResourceNotFound):
|
||||||
msg_fmt = _("The compute node %(name)s could not be found")
|
msg_fmt = _("The compute node %(name)s could not be found")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import time
|
|||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
import cinderclient.exceptions as ciexceptions
|
import cinderclient.exceptions as ciexceptions
|
||||||
|
import glanceclient.exc as glexceptions
|
||||||
import novaclient.exceptions as nvexceptions
|
import novaclient.exceptions as nvexceptions
|
||||||
|
|
||||||
from watcher.common import clients
|
from watcher.common import clients
|
||||||
@@ -63,6 +64,15 @@ class NovaHelper(object):
|
|||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
raise exception.ComputeNodeNotFound(name=node_hostname)
|
raise exception.ComputeNodeNotFound(name=node_hostname)
|
||||||
|
|
||||||
|
def get_instance_list(self):
|
||||||
|
return self.nova.servers.list(search_opts={'all_tenants': True})
|
||||||
|
|
||||||
|
def get_service(self, service_id):
|
||||||
|
return self.nova.services.find(id=service_id)
|
||||||
|
|
||||||
|
def get_flavor(self, flavor_id):
|
||||||
|
return self.nova.flavors.get(flavor_id)
|
||||||
|
|
||||||
def get_aggregate_list(self):
|
def get_aggregate_list(self):
|
||||||
return self.nova.aggregates.list()
|
return self.nova.aggregates.list()
|
||||||
|
|
||||||
@@ -142,7 +152,7 @@ class NovaHelper(object):
|
|||||||
# We'll use the same name for the new instance.
|
# We'll use the same name for the new instance.
|
||||||
imagedict = getattr(instance, "image")
|
imagedict = getattr(instance, "image")
|
||||||
image_id = imagedict["id"]
|
image_id = imagedict["id"]
|
||||||
image = self.nova.images.get(image_id)
|
image = self.glance.images.get(image_id)
|
||||||
new_image_name = getattr(image, "name")
|
new_image_name = getattr(image, "name")
|
||||||
|
|
||||||
instance_name = getattr(instance, "name")
|
instance_name = getattr(instance, "name")
|
||||||
@@ -304,6 +314,70 @@ class NovaHelper(object):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def resize_instance(self, instance_id, flavor, retry=120):
|
||||||
|
"""This method resizes given instance with specified flavor.
|
||||||
|
|
||||||
|
This method uses the Nova built-in resize()
|
||||||
|
action to do a resize of a given instance.
|
||||||
|
|
||||||
|
It returns True if the resize was successful,
|
||||||
|
False otherwise.
|
||||||
|
|
||||||
|
:param instance_id: the unique id of the instance to resize.
|
||||||
|
:param flavor: the name or ID of the flavor to resize to.
|
||||||
|
"""
|
||||||
|
LOG.debug("Trying a resize of instance %s to flavor '%s'" % (
|
||||||
|
instance_id, flavor))
|
||||||
|
|
||||||
|
# Looking for the instance to resize
|
||||||
|
instance = self.find_instance(instance_id)
|
||||||
|
|
||||||
|
flavor_id = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
flavor_id = self.nova.flavors.get(flavor)
|
||||||
|
except nvexceptions.NotFound:
|
||||||
|
flavor_id = [f.id for f in self.nova.flavors.list() if
|
||||||
|
f.name == flavor][0]
|
||||||
|
except nvexceptions.ClientException as e:
|
||||||
|
LOG.debug("Nova client exception occurred while resizing "
|
||||||
|
"instance %s. Exception: %s", instance_id, e)
|
||||||
|
|
||||||
|
if not flavor_id:
|
||||||
|
LOG.debug("Flavor not found: %s" % flavor)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not instance:
|
||||||
|
LOG.debug("Instance not found: %s" % instance_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
instance_status = getattr(instance, 'OS-EXT-STS:vm_state')
|
||||||
|
LOG.debug(
|
||||||
|
"Instance %s is in '%s' status." % (instance_id,
|
||||||
|
instance_status))
|
||||||
|
|
||||||
|
instance.resize(flavor=flavor_id)
|
||||||
|
while getattr(instance,
|
||||||
|
'OS-EXT-STS:vm_state') != 'resized' \
|
||||||
|
and retry:
|
||||||
|
instance = self.nova.servers.get(instance.id)
|
||||||
|
LOG.debug(
|
||||||
|
'Waiting the resize of {0} to {1}'.format(
|
||||||
|
instance, flavor_id))
|
||||||
|
time.sleep(1)
|
||||||
|
retry -= 1
|
||||||
|
|
||||||
|
instance_status = getattr(instance, 'status')
|
||||||
|
if instance_status != 'VERIFY_RESIZE':
|
||||||
|
return False
|
||||||
|
|
||||||
|
instance.confirm_resize()
|
||||||
|
|
||||||
|
LOG.debug("Resizing succeeded : instance %s is now on flavor "
|
||||||
|
"'%s'.", instance_id, flavor_id)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def live_migrate_instance(self, instance_id, dest_hostname,
|
def live_migrate_instance(self, instance_id, dest_hostname,
|
||||||
block_migration=False, retry=120):
|
block_migration=False, retry=120):
|
||||||
"""This method does a live migration of a given instance
|
"""This method does a live migration of a given instance
|
||||||
@@ -575,8 +649,8 @@ class NovaHelper(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image = self.nova.images.get(image_id)
|
image = self.glance.images.get(image_id)
|
||||||
except nvexceptions.NotFound:
|
except glexceptions.NotFound:
|
||||||
LOG.debug("Image '%s' not found " % image_id)
|
LOG.debug("Image '%s' not found " % image_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -644,6 +718,16 @@ class NovaHelper(object):
|
|||||||
|
|
||||||
return network_id
|
return network_id
|
||||||
|
|
||||||
|
def get_instance_by_uuid(self, instance_uuid):
|
||||||
|
return [instance for instance in
|
||||||
|
self.nova.servers.list(search_opts={"all_tenants": True,
|
||||||
|
"uuid": instance_uuid})]
|
||||||
|
|
||||||
|
def get_instance_by_name(self, instance_name):
|
||||||
|
return [instance for instance in
|
||||||
|
self.nova.servers.list(search_opts={"all_tenants": True,
|
||||||
|
"name": instance_name})]
|
||||||
|
|
||||||
def get_instances_by_node(self, host):
|
def get_instances_by_node(self, host):
|
||||||
return [instance for instance in
|
return [instance for instance in
|
||||||
self.nova.servers.list(search_opts={"all_tenants": True})
|
self.nova.servers.list(search_opts={"all_tenants": True})
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from watcher.common.messaging.utils import synchronization
|
from watcher.common import synchronization
|
||||||
|
|
||||||
|
|
||||||
class Observable(synchronization.Synchronization):
|
class Observable(synchronization.Synchronization):
|
||||||
|
|||||||
@@ -17,38 +17,9 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from oslo_config import cfg
|
from watcher import conf
|
||||||
|
|
||||||
PATH_OPTS = [
|
CONF = conf.CONF
|
||||||
cfg.StrOpt('pybasedir',
|
|
||||||
default=os.path.abspath(os.path.join(os.path.dirname(__file__),
|
|
||||||
'../')),
|
|
||||||
help='Directory where the watcher python module is installed.'),
|
|
||||||
cfg.StrOpt('bindir',
|
|
||||||
default='$pybasedir/bin',
|
|
||||||
help='Directory where watcher binaries are installed.'),
|
|
||||||
cfg.StrOpt('state_path',
|
|
||||||
default='$pybasedir',
|
|
||||||
help="Top-level directory for maintaining watcher's state."),
|
|
||||||
]
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
CONF.register_opts(PATH_OPTS)
|
|
||||||
|
|
||||||
|
|
||||||
def basedir_def(*args):
|
|
||||||
"""Return an uninterpolated path relative to $pybasedir."""
|
|
||||||
return os.path.join('$pybasedir', *args)
|
|
||||||
|
|
||||||
|
|
||||||
def bindir_def(*args):
|
|
||||||
"""Return an uninterpolated path relative to $bindir."""
|
|
||||||
return os.path.join('$bindir', *args)
|
|
||||||
|
|
||||||
|
|
||||||
def state_path_def(*args):
|
|
||||||
"""Return an uninterpolated path relative to $state_path."""
|
|
||||||
return os.path.join('$state_path', *args)
|
|
||||||
|
|
||||||
|
|
||||||
def basedir_rel(*args):
|
def basedir_rel(*args):
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from jsonschema import validators
|
from jsonschema import validators
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
@@ -29,18 +28,9 @@ import six
|
|||||||
from watcher._i18n import _LW
|
from watcher._i18n import _LW
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
|
|
||||||
|
from watcher import conf
|
||||||
|
|
||||||
UTILS_OPTS = [
|
CONF = conf.CONF
|
||||||
cfg.StrOpt('rootwrap_config',
|
|
||||||
default="/etc/watcher/rootwrap.conf",
|
|
||||||
help='Path to the rootwrap configuration file to use for '
|
|
||||||
'running commands as root.'),
|
|
||||||
cfg.StrOpt('tempdir',
|
|
||||||
help='Explicitly specify the temporary working directory.'),
|
|
||||||
]
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
CONF.register_opts(UTILS_OPTS)
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# Copyright (c) 2016 b<>com
|
# Copyright (c) 2016 b<>com
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
#
|
#
|
||||||
# Authors: Vincent FRANCOISE <vincent.francoise@b-com.com>
|
# Authors: Vincent FRANCOISE <vincent.francoise@b-com.com>
|
||||||
#
|
#
|
||||||
@@ -18,8 +19,38 @@
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from watcher.conf import api
|
||||||
|
from watcher.conf import applier
|
||||||
|
from watcher.conf import ceilometer_client
|
||||||
|
from watcher.conf import cinder_client
|
||||||
|
from watcher.conf import clients_auth
|
||||||
|
from watcher.conf import db
|
||||||
|
from watcher.conf import decision_engine
|
||||||
|
from watcher.conf import exception
|
||||||
|
from watcher.conf import glance_client
|
||||||
|
from watcher.conf import monasca_client
|
||||||
|
from watcher.conf import neutron_client
|
||||||
|
from watcher.conf import nova_client
|
||||||
|
from watcher.conf import paths
|
||||||
|
from watcher.conf import planner
|
||||||
from watcher.conf import service
|
from watcher.conf import service
|
||||||
|
from watcher.conf import utils
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
service.register_opts(CONF)
|
service.register_opts(CONF)
|
||||||
|
api.register_opts(CONF)
|
||||||
|
utils.register_opts(CONF)
|
||||||
|
paths.register_opts(CONF)
|
||||||
|
exception.register_opts(CONF)
|
||||||
|
db.register_opts(CONF)
|
||||||
|
planner.register_opts(CONF)
|
||||||
|
applier.register_opts(CONF)
|
||||||
|
decision_engine.register_opts(CONF)
|
||||||
|
monasca_client.register_opts(CONF)
|
||||||
|
nova_client.register_opts(CONF)
|
||||||
|
glance_client.register_opts(CONF)
|
||||||
|
cinder_client.register_opts(CONF)
|
||||||
|
ceilometer_client.register_opts(CONF)
|
||||||
|
neutron_client.register_opts(CONF)
|
||||||
|
clients_auth.register_opts(CONF)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# Copyright 2014
|
# Copyright 2014
|
||||||
# The Cloudscaling Group, Inc.
|
# The Cloudscaling Group, Inc.
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -17,38 +18,41 @@
|
|||||||
|
|
||||||
from keystoneauth1 import loading as ka_loading
|
from keystoneauth1 import loading as ka_loading
|
||||||
|
|
||||||
from watcher.api import acl as api_acl
|
from watcher.conf import api as conf_api
|
||||||
from watcher.api import app as api_app
|
from watcher.conf import applier as conf_applier
|
||||||
from watcher.applier import manager as applier_manager
|
from watcher.conf import ceilometer_client as conf_ceilometer_client
|
||||||
from watcher.common import clients
|
from watcher.conf import cinder_client as conf_cinder_client
|
||||||
from watcher.common import exception
|
from watcher.conf import db
|
||||||
from watcher.common import paths
|
from watcher.conf import decision_engine as conf_de
|
||||||
from watcher.db.sqlalchemy import models
|
from watcher.conf import exception
|
||||||
from watcher.decision_engine.audit import continuous
|
from watcher.conf import glance_client as conf_glance_client
|
||||||
from watcher.decision_engine import manager as decision_engine_manager
|
from watcher.conf import neutron_client as conf_neutron_client
|
||||||
from watcher.decision_engine.planner import manager as planner_manager
|
from watcher.conf import nova_client as conf_nova_client
|
||||||
|
from watcher.conf import paths
|
||||||
|
from watcher.conf import planner as conf_planner
|
||||||
|
from watcher.conf import utils
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
def list_opts():
|
||||||
"""Legacy aggregation of all the watcher config options"""
|
"""Legacy aggregation of all the watcher config options"""
|
||||||
return [
|
return [
|
||||||
('DEFAULT',
|
('DEFAULT',
|
||||||
(api_app.API_SERVICE_OPTS +
|
(conf_api.AUTH_OPTS +
|
||||||
api_acl.AUTH_OPTS +
|
|
||||||
exception.EXC_LOG_OPTS +
|
exception.EXC_LOG_OPTS +
|
||||||
paths.PATH_OPTS)),
|
paths.PATH_OPTS +
|
||||||
('api', api_app.API_SERVICE_OPTS),
|
utils.UTILS_OPTS)),
|
||||||
('database', models.SQL_OPTS),
|
('api', conf_api.API_SERVICE_OPTS),
|
||||||
|
('database', db.SQL_OPTS),
|
||||||
|
('watcher_planner', conf_planner.WATCHER_PLANNER_OPTS),
|
||||||
|
('watcher_applier', conf_applier.APPLIER_MANAGER_OPTS),
|
||||||
('watcher_decision_engine',
|
('watcher_decision_engine',
|
||||||
(decision_engine_manager.WATCHER_DECISION_ENGINE_OPTS +
|
(conf_de.WATCHER_DECISION_ENGINE_OPTS +
|
||||||
continuous.WATCHER_CONTINUOUS_OPTS)),
|
conf_de.WATCHER_CONTINUOUS_OPTS)),
|
||||||
('watcher_applier', applier_manager.APPLIER_MANAGER_OPTS),
|
('nova_client', conf_nova_client.NOVA_CLIENT_OPTS),
|
||||||
('watcher_planner', planner_manager.WATCHER_PLANNER_OPTS),
|
('glance_client', conf_glance_client.GLANCE_CLIENT_OPTS),
|
||||||
('nova_client', clients.NOVA_CLIENT_OPTS),
|
('cinder_client', conf_cinder_client.CINDER_CLIENT_OPTS),
|
||||||
('glance_client', clients.GLANCE_CLIENT_OPTS),
|
('ceilometer_client', conf_ceilometer_client.CEILOMETER_CLIENT_OPTS),
|
||||||
('cinder_client', clients.CINDER_CLIENT_OPTS),
|
('neutron_client', conf_neutron_client.NEUTRON_CLIENT_OPTS),
|
||||||
('ceilometer_client', clients.CEILOMETER_CLIENT_OPTS),
|
|
||||||
('neutron_client', clients.NEUTRON_CLIENT_OPTS),
|
|
||||||
('watcher_clients_auth',
|
('watcher_clients_auth',
|
||||||
(ka_loading.get_auth_common_conf_options() +
|
(ka_loading.get_auth_common_conf_options() +
|
||||||
ka_loading.get_auth_plugin_conf_options('password') +
|
ka_loading.get_auth_plugin_conf_options('password') +
|
||||||
|
|||||||
67
watcher/conf/api.py
Normal file
67
watcher/conf/api.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
|
#
|
||||||
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
api = cfg.OptGroup(name='api',
|
||||||
|
title='Options for the Watcher API service')
|
||||||
|
|
||||||
|
AUTH_OPTS = [
|
||||||
|
cfg.BoolOpt('enable_authentication',
|
||||||
|
default=True,
|
||||||
|
help='This option enables or disables user authentication '
|
||||||
|
'via keystone. Default value is True.'),
|
||||||
|
]
|
||||||
|
|
||||||
|
API_SERVICE_OPTS = [
|
||||||
|
cfg.PortOpt('port',
|
||||||
|
default=9322,
|
||||||
|
help='The port for the watcher API server'),
|
||||||
|
cfg.StrOpt('host',
|
||||||
|
default='127.0.0.1',
|
||||||
|
help='The listen IP address for the watcher API server'),
|
||||||
|
cfg.IntOpt('max_limit',
|
||||||
|
default=1000,
|
||||||
|
help='The maximum number of items returned in a single '
|
||||||
|
'response from a collection resource'),
|
||||||
|
cfg.IntOpt('workers',
|
||||||
|
min=1,
|
||||||
|
help='Number of workers for Watcher API service. '
|
||||||
|
'The default is equal to the number of CPUs available '
|
||||||
|
'if that can be determined, else a default worker '
|
||||||
|
'count of 1 is returned.'),
|
||||||
|
|
||||||
|
cfg.BoolOpt('enable_ssl_api',
|
||||||
|
default=False,
|
||||||
|
help="Enable the integrated stand-alone API to service "
|
||||||
|
"requests via HTTPS instead of HTTP. If there is a "
|
||||||
|
"front-end service performing HTTPS offloading from "
|
||||||
|
"the service, this option should be False; note, you "
|
||||||
|
"will want to change public API endpoint to represent "
|
||||||
|
"SSL termination URL with 'public_endpoint' option."),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(api)
|
||||||
|
conf.register_opts(API_SERVICE_OPTS, group=api)
|
||||||
|
conf.register_opts(AUTH_OPTS)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return [('api', API_SERVICE_OPTS), ('DEFAULT', AUTH_OPTS)]
|
||||||
53
watcher/conf/applier.py
Normal file
53
watcher/conf/applier.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
|
#
|
||||||
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
watcher_applier = cfg.OptGroup(name='watcher_applier',
|
||||||
|
title='Options for the Applier messaging'
|
||||||
|
'core')
|
||||||
|
|
||||||
|
APPLIER_MANAGER_OPTS = [
|
||||||
|
cfg.IntOpt('workers',
|
||||||
|
default='1',
|
||||||
|
min=1,
|
||||||
|
required=True,
|
||||||
|
help='Number of workers for applier, default value is 1.'),
|
||||||
|
cfg.StrOpt('conductor_topic',
|
||||||
|
default='watcher.applier.control',
|
||||||
|
help='The topic name used for'
|
||||||
|
'control events, this topic '
|
||||||
|
'used for rpc call '),
|
||||||
|
cfg.StrOpt('publisher_id',
|
||||||
|
default='watcher.applier.api',
|
||||||
|
help='The identifier used by watcher '
|
||||||
|
'module on the message broker'),
|
||||||
|
cfg.StrOpt('workflow_engine',
|
||||||
|
default='taskflow',
|
||||||
|
required=True,
|
||||||
|
help='Select the engine to use to execute the workflow'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(watcher_applier)
|
||||||
|
conf.register_opts(APPLIER_MANAGER_OPTS, group=watcher_applier)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return [('watcher_applier', APPLIER_MANAGER_OPTS)]
|
||||||
37
watcher/conf/ceilometer_client.py
Normal file
37
watcher/conf/ceilometer_client.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
|
#
|
||||||
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
ceilometer_client = cfg.OptGroup(name='ceilometer_client',
|
||||||
|
title='Configuration Options for Ceilometer')
|
||||||
|
|
||||||
|
CEILOMETER_CLIENT_OPTS = [
|
||||||
|
cfg.StrOpt('api_version',
|
||||||
|
default='2',
|
||||||
|
help='Version of Ceilometer API to use in '
|
||||||
|
'ceilometerclient.')]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(ceilometer_client)
|
||||||
|
conf.register_opts(CEILOMETER_CLIENT_OPTS, group=ceilometer_client)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return [('ceilometer_client', CEILOMETER_CLIENT_OPTS)]
|
||||||
36
watcher/conf/cinder_client.py
Normal file
36
watcher/conf/cinder_client.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
|
#
|
||||||
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
cinder_client = cfg.OptGroup(name='cinder_client',
|
||||||
|
title='Configuration Options for Cinder')
|
||||||
|
|
||||||
|
CINDER_CLIENT_OPTS = [
|
||||||
|
cfg.StrOpt('api_version',
|
||||||
|
default='2',
|
||||||
|
help='Version of Cinder API to use in cinderclient.')]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(cinder_client)
|
||||||
|
conf.register_opts(CINDER_CLIENT_OPTS, group=cinder_client)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return [('cinder_client', CINDER_CLIENT_OPTS)]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# Copyright (c) 2015 b<>com
|
# Copyright (c) 2016 Intel Corp
|
||||||
#
|
#
|
||||||
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -15,19 +15,17 @@
|
|||||||
# implied.
|
# implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
|
||||||
|
|
||||||
from watcher.decision_engine.model import element
|
from keystoneauth1 import loading as ka_loading
|
||||||
from watcher.tests import base
|
|
||||||
|
WATCHER_CLIENTS_AUTH = 'watcher_clients_auth'
|
||||||
|
|
||||||
|
|
||||||
class TestDiskInfo(base.TestCase):
|
def register_opts(conf):
|
||||||
def test_all(self):
|
ka_loading.register_session_conf_options(conf, WATCHER_CLIENTS_AUTH)
|
||||||
disk_information = element.DiskInfo()
|
ka_loading.register_auth_conf_options(conf, WATCHER_CLIENTS_AUTH)
|
||||||
disk_information.set_size(1024)
|
|
||||||
self.assertEqual(1024, disk_information.get_size())
|
|
||||||
|
|
||||||
disk_information.set_scheduler = "scheduler_qcq"
|
|
||||||
|
|
||||||
disk_information.set_device_name("nom_qcq")
|
def list_opts():
|
||||||
self.assertEqual("nom_qcq", disk_information.get_device_name())
|
return [('watcher_clients_auth', ka_loading.get_session_conf_options() +
|
||||||
|
ka_loading.get_auth_common_conf_options())]
|
||||||
44
watcher/conf/db.py
Normal file
44
watcher/conf/db.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
|
#
|
||||||
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_db import options as oslo_db_options
|
||||||
|
|
||||||
|
from watcher.conf import paths
|
||||||
|
|
||||||
|
_DEFAULT_SQL_CONNECTION = 'sqlite:///{0}'.format(
|
||||||
|
paths.state_path_def('watcher.sqlite'))
|
||||||
|
|
||||||
|
database = cfg.OptGroup(name='database',
|
||||||
|
title='Configuration Options for database')
|
||||||
|
|
||||||
|
SQL_OPTS = [
|
||||||
|
cfg.StrOpt('mysql_engine',
|
||||||
|
default='InnoDB',
|
||||||
|
help='MySQL engine to use.')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
oslo_db_options.set_defaults(conf, connection=_DEFAULT_SQL_CONNECTION)
|
||||||
|
conf.register_group(database)
|
||||||
|
conf.register_opts(SQL_OPTS, group=database)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return [('database', SQL_OPTS)]
|
||||||
64
watcher/conf/decision_engine.py
Normal file
64
watcher/conf/decision_engine.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2015 Intel Corp
|
||||||
|
#
|
||||||
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
|
||||||
|
watcher_decision_engine = cfg.OptGroup(name='watcher_decision_engine',
|
||||||
|
title='Defines the parameters of '
|
||||||
|
'the module decision engine')
|
||||||
|
|
||||||
|
WATCHER_DECISION_ENGINE_OPTS = [
|
||||||
|
cfg.StrOpt('conductor_topic',
|
||||||
|
default='watcher.decision.control',
|
||||||
|
help='The topic name used for '
|
||||||
|
'control events, this topic '
|
||||||
|
'used for RPC calls'),
|
||||||
|
cfg.ListOpt('notification_topics',
|
||||||
|
default=['versioned_notifications', 'watcher_notifications'],
|
||||||
|
help='The topic names from which notification events '
|
||||||
|
'will be listened to'),
|
||||||
|
cfg.StrOpt('publisher_id',
|
||||||
|
default='watcher.decision.api',
|
||||||
|
help='The identifier used by the Watcher '
|
||||||
|
'module on the message broker'),
|
||||||
|
cfg.IntOpt('max_workers',
|
||||||
|
default=2,
|
||||||
|
required=True,
|
||||||
|
help='The maximum number of threads that can be used to '
|
||||||
|
'execute strategies'),
|
||||||
|
]
|
||||||
|
|
||||||
|
WATCHER_CONTINUOUS_OPTS = [
|
||||||
|
cfg.IntOpt('continuous_audit_interval',
|
||||||
|
default=10,
|
||||||
|
help='Interval (in seconds) for checking newly created '
|
||||||
|
'continuous audits.')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(watcher_decision_engine)
|
||||||
|
conf.register_opts(WATCHER_DECISION_ENGINE_OPTS,
|
||||||
|
group=watcher_decision_engine)
|
||||||
|
conf.register_opts(WATCHER_CONTINUOUS_OPTS, group=watcher_decision_engine)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return [('watcher_decision_engine', WATCHER_DECISION_ENGINE_OPTS),
|
||||||
|
('watcher_decision_engine', WATCHER_CONTINUOUS_OPTS)]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# Copyright (c) 2015 b<>com
|
# Copyright (c) 2016 Intel Corp
|
||||||
#
|
#
|
||||||
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -15,16 +15,19 @@
|
|||||||
# implied.
|
# implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
|
||||||
from watcher.decision_engine.model import element
|
from oslo_config import cfg
|
||||||
from watcher.tests import base
|
|
||||||
|
EXC_LOG_OPTS = [
|
||||||
|
cfg.BoolOpt('fatal_exception_format_errors',
|
||||||
|
default=False,
|
||||||
|
help='Make exception message format errors fatal.'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestInstance(base.TestCase):
|
def register_opts(conf):
|
||||||
|
conf.register_opts(EXC_LOG_OPTS)
|
||||||
|
|
||||||
def test_namedelement(self):
|
|
||||||
instance = element.Instance()
|
def list_opts():
|
||||||
instance.state = element.InstanceState.ACTIVE
|
return [('DEFAULT', EXC_LOG_OPTS)]
|
||||||
self.assertEqual(element.InstanceState.ACTIVE, instance.state)
|
|
||||||
instance.human_id = "human_05"
|
|
||||||
self.assertEqual("human_05", instance.human_id)
|
|
||||||
36
watcher/conf/glance_client.py
Normal file
36
watcher/conf/glance_client.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
|
#
|
||||||
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
glance_client = cfg.OptGroup(name='glance_client',
|
||||||
|
title='Configuration Options for Glance')
|
||||||
|
|
||||||
|
GLANCE_CLIENT_OPTS = [
|
||||||
|
cfg.StrOpt('api_version',
|
||||||
|
default='2',
|
||||||
|
help='Version of Glance API to use in glanceclient.')]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(glance_client)
|
||||||
|
conf.register_opts(GLANCE_CLIENT_OPTS, group=glance_client)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return [('glance_client', GLANCE_CLIENT_OPTS)]
|
||||||
36
watcher/conf/monasca_client.py
Normal file
36
watcher/conf/monasca_client.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# Copyright (c) 2016 Intel Corp
|
||||||
|
#
|
||||||
|
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
monasca_client = cfg.OptGroup(name='monasca_client',
|
||||||
|
title='Configuration Options for Monasca')
|
||||||
|
|
||||||
|
MONASCA_CLIENT_OPTS = [
|
||||||
|
cfg.StrOpt('api_version',
|
||||||
|
default='2_0',
|
||||||
|
help='Version of Monasca API to use in monascaclient.')]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(monasca_client)
|
||||||
|
conf.register_opts(MONASCA_CLIENT_OPTS, group=monasca_client)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return [('monasca_client', MONASCA_CLIENT_OPTS)]
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user