Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1caf89686c | ||
|
|
ed3224835a | ||
|
|
8a7e316f73 | ||
|
|
112ac3bbdf | ||
|
|
d1ab697612 | ||
|
|
095ca0ffb2 | ||
|
|
e1131a65d8 | ||
|
|
5e507e56f4 |
@@ -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,13 +1,13 @@
|
|||||||
If you would like to contribute to the development of OpenStack,
|
If you would like to contribute to the development of OpenStack,
|
||||||
you must follow the steps in this page:
|
you must follow the steps in this page:
|
||||||
|
|
||||||
https://docs.openstack.org/infra/manual/developers.html
|
http://docs.openstack.org/infra/manual/developers.html
|
||||||
|
|
||||||
Once those steps have been completed, changes to OpenStack
|
Once those steps have been completed, changes to OpenStack
|
||||||
should be submitted for review via the Gerrit tool, following
|
should be submitted for review via the Gerrit tool, following
|
||||||
the workflow documented at:
|
the workflow documented at:
|
||||||
|
|
||||||
https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||||
|
|
||||||
Pull requests submitted through GitHub will be ignored.
|
Pull requests submitted through GitHub will be ignored.
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,4 @@
|
|||||||
watcher Style Commandments
|
watcher Style Commandments
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
Read the OpenStack Style Commandments https://docs.openstack.org/developer/hacking/
|
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
||||||
|
|||||||
10
README.rst
10
README.rst
@@ -2,8 +2,8 @@
|
|||||||
Team and repository tags
|
Team and repository tags
|
||||||
========================
|
========================
|
||||||
|
|
||||||
.. image:: https://governance.openstack.org/badges/watcher.svg
|
.. image:: http://governance.openstack.org/badges/watcher.svg
|
||||||
:target: https://governance.openstack.org/reference/tags/index.html
|
:target: http://governance.openstack.org/reference/tags/index.html
|
||||||
|
|
||||||
.. Change things from this point on
|
.. Change things from this point on
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ operating costs, increased system performance via intelligent virtual machine
|
|||||||
migration, increased energy efficiency-and more!
|
migration, increased energy efficiency-and more!
|
||||||
|
|
||||||
* Free software: Apache license
|
* Free software: Apache license
|
||||||
* Wiki: https://wiki.openstack.org/wiki/Watcher
|
* Wiki: http://wiki.openstack.org/wiki/Watcher
|
||||||
* Source: https://github.com/openstack/watcher
|
* Source: https://github.com/openstack/watcher
|
||||||
* Bugs: https://bugs.launchpad.net/watcher
|
* Bugs: http://bugs.launchpad.net/watcher
|
||||||
* Documentation: https://docs.openstack.org/developer/watcher/
|
* Documentation: http://docs.openstack.org/developer/watcher/
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
# This is an example Apache2 configuration file for using the
|
|
||||||
# Watcher API through mod_wsgi. This version assumes you are
|
|
||||||
# running devstack to configure the software.
|
|
||||||
|
|
||||||
Listen %WATCHER_SERVICE_PORT%
|
|
||||||
|
|
||||||
<VirtualHost *:%WATCHER_SERVICE_PORT%>
|
|
||||||
WSGIDaemonProcess watcher-api user=%USER% processes=%APIWORKERS% threads=1 display-name=%{GROUP}
|
|
||||||
WSGIScriptAlias / %WATCHER_WSGI_DIR%/app.wsgi
|
|
||||||
WSGIApplicationGroup %{GLOBAL}
|
|
||||||
WSGIProcessGroup watcher-api
|
|
||||||
WSGIPassAuthorization On
|
|
||||||
|
|
||||||
ErrorLogFormat "%M"
|
|
||||||
ErrorLog /var/log/%APACHE_NAME%/watcher-api.log
|
|
||||||
CustomLog /var/log/%APACHE_NAME%/watcher-api-access.log combined
|
|
||||||
|
|
||||||
|
|
||||||
<Directory %WATCHER_WSGI_DIR%>
|
|
||||||
WSGIProcessGroup watcher-api
|
|
||||||
WSGIApplicationGroup %{GLOBAL}
|
|
||||||
<IfVersion >= 2.4>
|
|
||||||
Require all granted
|
|
||||||
</IfVersion>
|
|
||||||
<IfVersion < 2.4>
|
|
||||||
Order allow,deny
|
|
||||||
Allow from all
|
|
||||||
</IfVersion>
|
|
||||||
</Directory>
|
|
||||||
</VirtualHost>
|
|
||||||
@@ -44,9 +44,6 @@ WATCHER_CONF_DIR=/etc/watcher
|
|||||||
WATCHER_CONF=$WATCHER_CONF_DIR/watcher.conf
|
WATCHER_CONF=$WATCHER_CONF_DIR/watcher.conf
|
||||||
WATCHER_POLICY_JSON=$WATCHER_CONF_DIR/policy.json
|
WATCHER_POLICY_JSON=$WATCHER_CONF_DIR/policy.json
|
||||||
|
|
||||||
WATCHER_DEVSTACK_DIR=$WATCHER_DIR/devstack
|
|
||||||
WATCHER_DEVSTACK_FILES_DIR=$WATCHER_DEVSTACK_DIR/files
|
|
||||||
|
|
||||||
NOVA_CONF_DIR=/etc/nova
|
NOVA_CONF_DIR=/etc/nova
|
||||||
NOVA_CONF=$NOVA_CONF_DIR/nova.conf
|
NOVA_CONF=$NOVA_CONF_DIR/nova.conf
|
||||||
|
|
||||||
@@ -54,13 +51,6 @@ if is_ssl_enabled_service "watcher" || is_service_enabled tls-proxy; then
|
|||||||
WATCHER_SERVICE_PROTOCOL="https"
|
WATCHER_SERVICE_PROTOCOL="https"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
WATCHER_USE_MOD_WSGI=$(trueorfalse True WATCHER_USE_MOD_WSGI)
|
|
||||||
|
|
||||||
if is_suse; then
|
|
||||||
WATCHER_WSGI_DIR=${WATCHER_WSGI_DIR:-/srv/www/htdocs/watcher}
|
|
||||||
else
|
|
||||||
WATCHER_WSGI_DIR=${WATCHER_WSGI_DIR:-/var/www/watcher}
|
|
||||||
fi
|
|
||||||
# Public facing bits
|
# Public facing bits
|
||||||
WATCHER_SERVICE_HOST=${WATCHER_SERVICE_HOST:-$HOST_IP}
|
WATCHER_SERVICE_HOST=${WATCHER_SERVICE_HOST:-$HOST_IP}
|
||||||
WATCHER_SERVICE_PORT=${WATCHER_SERVICE_PORT:-9322}
|
WATCHER_SERVICE_PORT=${WATCHER_SERVICE_PORT:-9322}
|
||||||
@@ -84,21 +74,10 @@ function is_watcher_enabled {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#_cleanup_watcher_apache_wsgi - Remove wsgi files,
|
|
||||||
#disable and remove apache vhost file
|
|
||||||
function _cleanup_watcher_apache_wsgi {
|
|
||||||
sudo rm -rf $WATCHER_WSGI_DIR
|
|
||||||
sudo rm -f $(apache_site_config_for watcher-api)
|
|
||||||
restart_apache_server
|
|
||||||
}
|
|
||||||
|
|
||||||
# cleanup_watcher() - Remove residual data files, anything left over from previous
|
# cleanup_watcher() - Remove residual data files, anything left over from previous
|
||||||
# runs that a clean run would need to clean up
|
# runs that a clean run would need to clean up
|
||||||
function cleanup_watcher {
|
function cleanup_watcher {
|
||||||
sudo rm -rf $WATCHER_STATE_PATH $WATCHER_AUTH_CACHE_DIR
|
sudo rm -rf $WATCHER_STATE_PATH $WATCHER_AUTH_CACHE_DIR
|
||||||
if [[ "$WATCHER_USE_MOD_WSGI" == "True" ]]; then
|
|
||||||
_cleanup_watcher_apache_wsgi
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# configure_watcher() - Set config files, create data dirs, etc
|
# configure_watcher() - Set config files, create data dirs, etc
|
||||||
@@ -129,28 +108,6 @@ function create_watcher_accounts {
|
|||||||
"$WATCHER_SERVICE_PROTOCOL://$WATCHER_SERVICE_HOST:$WATCHER_SERVICE_PORT"
|
"$WATCHER_SERVICE_PROTOCOL://$WATCHER_SERVICE_HOST:$WATCHER_SERVICE_PORT"
|
||||||
}
|
}
|
||||||
|
|
||||||
# _config_watcher_apache_wsgi() - Set WSGI config files of watcher
|
|
||||||
function _config_watcher_apache_wsgi {
|
|
||||||
local watcher_apache_conf
|
|
||||||
if [[ "$WATCHER_USE_MOD_WSGI" == "True" ]]; then
|
|
||||||
sudo mkdir -p $WATCHER_WSGI_DIR
|
|
||||||
sudo cp $WATCHER_DIR/watcher/api/app.wsgi $WATCHER_WSGI_DIR/app.wsgi
|
|
||||||
watcher_apache_conf=$(apache_site_config_for watcher-api)
|
|
||||||
sudo cp $WATCHER_DEVSTACK_FILES_DIR/apache-watcher-api.template $watcher_apache_conf
|
|
||||||
sudo sed -e "
|
|
||||||
s|%WATCHER_SERVICE_PORT%|$WATCHER_SERVICE_PORT|g;
|
|
||||||
s|%WATCHER_WSGI_DIR%|$WATCHER_WSGI_DIR|g;
|
|
||||||
s|%USER%|$STACK_USER|g;
|
|
||||||
s|%APIWORKERS%|$API_WORKERS|g;
|
|
||||||
s|%APACHE_NAME%|$APACHE_NAME|g;
|
|
||||||
" -i $watcher_apache_conf
|
|
||||||
enable_apache_site watcher-api
|
|
||||||
tail_log watcher-access /var/log/$APACHE_NAME/watcher-api-access.log
|
|
||||||
tail_log watcher-api /var/log/$APACHE_NAME/watcher-api.log
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# create_watcher_conf() - Create a new watcher.conf file
|
# create_watcher_conf() - Create a new watcher.conf file
|
||||||
function create_watcher_conf {
|
function create_watcher_conf {
|
||||||
# (Re)create ``watcher.conf``
|
# (Re)create ``watcher.conf``
|
||||||
@@ -169,7 +126,7 @@ function create_watcher_conf {
|
|||||||
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD
|
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD
|
||||||
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_host $RABBIT_HOST
|
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_host $RABBIT_HOST
|
||||||
|
|
||||||
iniset $WATCHER_CONF oslo_messaging_notifications driver "messagingv2"
|
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"
|
iniset $NOVA_CONF notifications notify_on_state_change "vm_and_task_state"
|
||||||
@@ -197,13 +154,9 @@ function create_watcher_conf {
|
|||||||
setup_colorized_logging $WATCHER_CONF DEFAULT
|
setup_colorized_logging $WATCHER_CONF DEFAULT
|
||||||
else
|
else
|
||||||
# Show user_name and project_name instead of user_id and project_id
|
# Show user_name and project_name instead of user_id and project_id
|
||||||
iniset $WATCHER_CONF DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(levelname)s %(name)s [%(request_id)s %(project_domain)s %(user_name)s %(project_name)s] %(instance)s%(message)s"
|
iniset $WATCHER_CONF DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(levelname)s %(name)s [%(request_id)s %(user_name)s %(project_name)s] %(instance)s%(message)s"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#config apache files
|
|
||||||
if [[ "$WATCHER_USE_MOD_WSGI" == "True" ]]; then
|
|
||||||
_config_watcher_apache_wsgi
|
|
||||||
fi
|
|
||||||
# Register SSL certificates if provided
|
# Register SSL certificates if provided
|
||||||
if is_ssl_enabled_service watcher; then
|
if is_ssl_enabled_service watcher; then
|
||||||
ensure_certificates WATCHER
|
ensure_certificates WATCHER
|
||||||
@@ -252,26 +205,19 @@ function install_watcherclient {
|
|||||||
function install_watcher {
|
function install_watcher {
|
||||||
git_clone $WATCHER_REPO $WATCHER_DIR $WATCHER_BRANCH
|
git_clone $WATCHER_REPO $WATCHER_DIR $WATCHER_BRANCH
|
||||||
setup_develop $WATCHER_DIR
|
setup_develop $WATCHER_DIR
|
||||||
if [[ "$WATCHER_USE_MOD_WSGI" == "True" ]]; then
|
|
||||||
install_apache_wsgi
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# start_watcher_api() - Start the API process ahead of other things
|
# start_watcher_api() - Start the API process ahead of other things
|
||||||
function start_watcher_api {
|
function start_watcher_api {
|
||||||
# Get right service port for testing
|
# Get right service port for testing
|
||||||
|
|
||||||
local service_port=$WATCHER_SERVICE_PORT
|
local service_port=$WATCHER_SERVICE_PORT
|
||||||
local service_protocol=$WATCHER_SERVICE_PROTOCOL
|
local service_protocol=$WATCHER_SERVICE_PROTOCOL
|
||||||
if is_service_enabled tls-proxy; then
|
if is_service_enabled tls-proxy; then
|
||||||
service_port=$WATCHER_SERVICE_PORT_INT
|
service_port=$WATCHER_SERVICE_PORT_INT
|
||||||
service_protocol="http"
|
service_protocol="http"
|
||||||
fi
|
fi
|
||||||
if [[ "$WATCHER_USE_MOD_WSGI" == "True" ]]; then
|
|
||||||
restart_apache_server
|
|
||||||
else
|
|
||||||
run_process watcher-api "$WATCHER_BIN_DIR/watcher-api --config-file $WATCHER_CONF"
|
run_process watcher-api "$WATCHER_BIN_DIR/watcher-api --config-file $WATCHER_CONF"
|
||||||
fi
|
|
||||||
echo "Waiting for watcher-api to start..."
|
echo "Waiting for watcher-api to start..."
|
||||||
if ! wait_for_service $SERVICE_TIMEOUT $service_protocol://$WATCHER_SERVICE_HOST:$service_port; then
|
if ! wait_for_service $SERVICE_TIMEOUT $service_protocol://$WATCHER_SERVICE_HOST:$service_port; then
|
||||||
die $LINENO "watcher-api did not start"
|
die $LINENO "watcher-api did not start"
|
||||||
@@ -294,12 +240,7 @@ function start_watcher {
|
|||||||
|
|
||||||
# stop_watcher() - Stop running processes (non-screen)
|
# stop_watcher() - Stop running processes (non-screen)
|
||||||
function stop_watcher {
|
function stop_watcher {
|
||||||
if [[ "$WATCHER_USE_MOD_WSGI" == "True" ]]; then
|
for serv in watcher-api watcher-decision-engine watcher-applier; do
|
||||||
disable_apache_site watcher-api
|
|
||||||
else
|
|
||||||
stop_process watcher-api
|
|
||||||
fi
|
|
||||||
for serv in watcher-decision-engine watcher-applier; do
|
|
||||||
stop_process $serv
|
stop_process $serv
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ GLANCE_HOSTPORT=${SERVICE_HOST}:9292
|
|||||||
DATABASE_TYPE=mysql
|
DATABASE_TYPE=mysql
|
||||||
|
|
||||||
# Enable services (including neutron)
|
# Enable services (including neutron)
|
||||||
ENABLED_SERVICES=n-cpu,n-api-meta,c-vol,q-agt,placement-client
|
ENABLED_SERVICES=n-cpu,n-api-meta,c-vol,q-agt
|
||||||
|
|
||||||
NOVA_VNC_ENABLED=True
|
NOVA_VNC_ENABLED=True
|
||||||
NOVNCPROXY_URL="http://$SERVICE_HOST:6080/vnc_auto.html"
|
NOVNCPROXY_URL="http://$SERVICE_HOST:6080/vnc_auto.html"
|
||||||
|
|||||||
@@ -17,10 +17,6 @@ NETWORK_GATEWAY=10.254.1.1 # Change this for your network
|
|||||||
|
|
||||||
MULTI_HOST=1
|
MULTI_HOST=1
|
||||||
|
|
||||||
|
|
||||||
#Set this to FALSE if do not want to run watcher-api behind mod-wsgi
|
|
||||||
#WATCHER_USE_MOD_WSGI=TRUE
|
|
||||||
|
|
||||||
# This is the controller node, so disable nova-compute
|
# This is the controller node, so disable nova-compute
|
||||||
disable_service n-cpu
|
disable_service n-cpu
|
||||||
|
|
||||||
@@ -32,7 +28,7 @@ ENABLED_SERVICES+=,q-svc,q-dhcp,q-meta,q-agt,q-l3,neutron
|
|||||||
enable_service n-cauth
|
enable_service n-cauth
|
||||||
|
|
||||||
# Enable the Watcher Dashboard plugin
|
# Enable the Watcher Dashboard plugin
|
||||||
enable_plugin watcher-dashboard git://git.openstack.org/openstack/watcher-dashboard
|
# enable_plugin watcher-dashboard git://git.openstack.org/openstack/watcher-dashboard
|
||||||
|
|
||||||
# Enable the Watcher plugin
|
# Enable the Watcher plugin
|
||||||
enable_plugin watcher git://git.openstack.org/openstack/watcher
|
enable_plugin watcher git://git.openstack.org/openstack/watcher
|
||||||
@@ -42,11 +38,6 @@ enable_plugin ceilometer git://git.openstack.org/openstack/ceilometer
|
|||||||
|
|
||||||
# This is the controller node, so disable the ceilometer compute agent
|
# This is the controller node, so disable the ceilometer compute agent
|
||||||
disable_service ceilometer-acompute
|
disable_service ceilometer-acompute
|
||||||
# Enable the ceilometer api explicitly(bug:1667678)
|
|
||||||
enable_service ceilometer-api
|
|
||||||
|
|
||||||
# Enable the Gnocchi plugin
|
|
||||||
enable_plugin gnocchi https://git.openstack.org/openstack/gnocchi
|
|
||||||
|
|
||||||
LOGFILE=$DEST/logs/stack.sh.log
|
LOGFILE=$DEST/logs/stack.sh.log
|
||||||
LOGDAYS=2
|
LOGDAYS=2
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"priority": "INFO",
|
|
||||||
"payload": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "ActionCreatePayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"input_parameters": {
|
|
||||||
"param2": 2,
|
|
||||||
"param1": 1
|
|
||||||
},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "PENDING",
|
|
||||||
"action_plan": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "TerseActionPlanPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
|
||||||
"global_efficacy": {},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "ONGOING",
|
|
||||||
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parents": [],
|
|
||||||
"action_type": "nop",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"publisher_id": "infra-optim:node0",
|
|
||||||
"timestamp": "2017-01-01 00:00:00.000000",
|
|
||||||
"event_type": "action.create",
|
|
||||||
"message_id": "530b409c-9b6b-459b-8f08-f93dbfeb4d41"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"priority": "INFO",
|
|
||||||
"payload": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "ActionDeletePayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"input_parameters": {
|
|
||||||
"param2": 2,
|
|
||||||
"param1": 1
|
|
||||||
},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "DELETED",
|
|
||||||
"action_plan": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "TerseActionPlanPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
|
||||||
"global_efficacy": {},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "ONGOING",
|
|
||||||
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parents": [],
|
|
||||||
"action_type": "nop",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"publisher_id": "infra-optim:node0",
|
|
||||||
"timestamp": "2017-01-01 00:00:00.000000",
|
|
||||||
"event_type": "action.delete",
|
|
||||||
"message_id": "530b409c-9b6b-459b-8f08-f93dbfeb4d41"
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"priority": "INFO",
|
|
||||||
"payload": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "ActionExecutionPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"input_parameters": {
|
|
||||||
"param2": 2,
|
|
||||||
"param1": 1
|
|
||||||
},
|
|
||||||
"fault": null,
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "SUCCEEDED",
|
|
||||||
"action_plan": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "TerseActionPlanPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
|
||||||
"global_efficacy": {},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "ONGOING",
|
|
||||||
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parents": [],
|
|
||||||
"action_type": "nop",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"event_type": "action.execution.end",
|
|
||||||
"publisher_id": "infra-optim:node0",
|
|
||||||
"timestamp": "2017-01-01 00:00:00.000000",
|
|
||||||
"message_id": "530b409c-9b6b-459b-8f08-f93dbfeb4d41"
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
{
|
|
||||||
"priority": "ERROR",
|
|
||||||
"payload": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "ActionExecutionPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"input_parameters": {
|
|
||||||
"param2": 2,
|
|
||||||
"param1": 1
|
|
||||||
},
|
|
||||||
"fault": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "ExceptionPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"module_name": "watcher.tests.notifications.test_action_notification",
|
|
||||||
"exception": "WatcherException",
|
|
||||||
"exception_message": "TEST",
|
|
||||||
"function_name": "test_send_action_execution_with_error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "FAILED",
|
|
||||||
"action_plan": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "TerseActionPlanPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
|
||||||
"global_efficacy": {},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "ONGOING",
|
|
||||||
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parents": [],
|
|
||||||
"action_type": "nop",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"event_type": "action.execution.error",
|
|
||||||
"publisher_id": "infra-optim:node0",
|
|
||||||
"timestamp": "2017-01-01 00:00:00.000000",
|
|
||||||
"message_id": "530b409c-9b6b-459b-8f08-f93dbfeb4d41"
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"priority": "INFO",
|
|
||||||
"payload": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "ActionExecutionPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"input_parameters": {
|
|
||||||
"param2": 2,
|
|
||||||
"param1": 1
|
|
||||||
},
|
|
||||||
"fault": null,
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "ONGOING",
|
|
||||||
"action_plan": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "TerseActionPlanPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
|
||||||
"global_efficacy": {},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "ONGOING",
|
|
||||||
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parents": [],
|
|
||||||
"action_type": "nop",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"event_type": "action.execution.start",
|
|
||||||
"publisher_id": "infra-optim:node0",
|
|
||||||
"timestamp": "2017-01-01 00:00:00.000000",
|
|
||||||
"message_id": "530b409c-9b6b-459b-8f08-f93dbfeb4d41"
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"priority": "INFO",
|
|
||||||
"payload": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "ActionUpdatePayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"input_parameters": {
|
|
||||||
"param2": 2,
|
|
||||||
"param1": 1
|
|
||||||
},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state_update": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "ActionStateUpdatePayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"old_state": "PENDING",
|
|
||||||
"state": "ONGOING"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": "ONGOING",
|
|
||||||
"action_plan": {
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.version": "1.0",
|
|
||||||
"watcher_object.name": "TerseActionPlanPayload",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"uuid": "76be87bd-3422-43f9-93a0-e85a577e3061",
|
|
||||||
"global_efficacy": {},
|
|
||||||
"created_at": "2016-10-18T09:52:05Z",
|
|
||||||
"updated_at": null,
|
|
||||||
"state": "ONGOING",
|
|
||||||
"audit_uuid": "10a47dd1-4874-4298-91cf-eff046dbdb8d",
|
|
||||||
"strategy_uuid": "cb3d0b58-4415-4d90-b75b-1e96878730e3",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parents": [],
|
|
||||||
"action_type": "nop",
|
|
||||||
"deleted_at": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"event_type": "action.update",
|
|
||||||
"publisher_id": "infra-optim:node0",
|
|
||||||
"timestamp": "2017-01-01 00:00:00.000000",
|
|
||||||
"message_id": "530b409c-9b6b-459b-8f08-f93dbfeb4d41"
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"payload": {
|
|
||||||
"watcher_object.name": "ServiceUpdatePayload",
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"status_update": {
|
|
||||||
"watcher_object.name": "ServiceStatusUpdatePayload",
|
|
||||||
"watcher_object.namespace": "watcher",
|
|
||||||
"watcher_object.data": {
|
|
||||||
"old_state": "ACTIVE",
|
|
||||||
"state": "FAILED"
|
|
||||||
},
|
|
||||||
"watcher_object.version": "1.0"
|
|
||||||
},
|
|
||||||
"last_seen_up": "2016-09-22T08:32:06Z",
|
|
||||||
"name": "watcher-service",
|
|
||||||
"sevice_host": "controller"
|
|
||||||
},
|
|
||||||
"watcher_object.version": "1.0"
|
|
||||||
},
|
|
||||||
"event_type": "service.update",
|
|
||||||
"priority": "INFO",
|
|
||||||
"message_id": "3984dc2b-8aef-462b-a220-8ae04237a56e",
|
|
||||||
"timestamp": "2016-10-18 09:52:05.219414",
|
|
||||||
"publisher_id": "infra-optim:node0"
|
|
||||||
}
|
|
||||||
@@ -407,9 +407,6 @@ be one of the following:
|
|||||||
- **CANCELLED** : the :ref:`Audit <audit_definition>` was in **PENDING** or
|
- **CANCELLED** : the :ref:`Audit <audit_definition>` was in **PENDING** or
|
||||||
**ONGOING** state and was cancelled by the
|
**ONGOING** state and was cancelled by the
|
||||||
:ref:`Administrator <administrator_definition>`
|
:ref:`Administrator <administrator_definition>`
|
||||||
- **SUSPENDED** : the :ref:`Audit <audit_definition>` was in **ONGOING**
|
|
||||||
state and was suspended by the
|
|
||||||
:ref:`Administrator <administrator_definition>`
|
|
||||||
|
|
||||||
The following diagram shows the different possible states of an
|
The following diagram shows the different possible states of an
|
||||||
:ref:`Audit <audit_definition>` and what event makes the state change to a new
|
:ref:`Audit <audit_definition>` and what event makes the state change to a new
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
..
|
|
||||||
Except where otherwise noted, this document is licensed under Creative
|
|
||||||
Commons Attribution 3.0 License. You can view the license at:
|
|
||||||
|
|
||||||
https://creativecommons.org/licenses/by/3.0/
|
|
||||||
|
|
||||||
|
|
||||||
Installing API behind mod_wsgi
|
|
||||||
==============================
|
|
||||||
|
|
||||||
#. Install the Apache Service::
|
|
||||||
|
|
||||||
Fedora 21/RHEL7/CentOS7:
|
|
||||||
sudo yum install httpd
|
|
||||||
|
|
||||||
Fedora 22 (or higher):
|
|
||||||
sudo dnf install httpd
|
|
||||||
|
|
||||||
Debian/Ubuntu:
|
|
||||||
apt-get install apache2
|
|
||||||
|
|
||||||
#. Copy ``etc/apache2/watcher.conf`` under the apache sites::
|
|
||||||
|
|
||||||
Fedora/RHEL7/CentOS7:
|
|
||||||
sudo cp etc/apache2/watcher /etc/httpd/conf.d/watcher.conf
|
|
||||||
|
|
||||||
Debian/Ubuntu:
|
|
||||||
sudo cp etc/apache2/watcher /etc/apache2/sites-available/watcher.conf
|
|
||||||
|
|
||||||
#. Edit ``<apache-configuration-dir>/watcher.conf`` according to installation
|
|
||||||
and environment.
|
|
||||||
|
|
||||||
* Modify the ``WSGIDaemonProcess`` directive to set the ``user`` and
|
|
||||||
``group`` values to appropriate user on your server.
|
|
||||||
* Modify the ``WSGIScriptAlias`` directive to point to the
|
|
||||||
watcher/api/app.wsgi script.
|
|
||||||
* Modify the ``Directory`` directive to set the path to the Watcher API
|
|
||||||
code.
|
|
||||||
* Modify the ``ErrorLog and CustomLog`` to redirect the logs to the right
|
|
||||||
directory.
|
|
||||||
|
|
||||||
#. Enable the apache watcher site and reload::
|
|
||||||
|
|
||||||
Fedora/RHEL7/CentOS7:
|
|
||||||
sudo systemctl reload httpd
|
|
||||||
|
|
||||||
Debian/Ubuntu:
|
|
||||||
sudo a2ensite watcher
|
|
||||||
sudo service apache2 reload
|
|
||||||
@@ -424,7 +424,7 @@ to Watcher receives Nova notifications in ``watcher_notifications`` as well.
|
|||||||
into which Nova services will publish events ::
|
into which Nova services will publish events ::
|
||||||
|
|
||||||
[oslo_messaging_notifications]
|
[oslo_messaging_notifications]
|
||||||
driver = messagingv2
|
driver = messaging
|
||||||
topics = notifications,watcher_notifications
|
topics = notifications,watcher_notifications
|
||||||
|
|
||||||
* Restart the Nova services.
|
* Restart the Nova services.
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ The source install instructions specifically avoid using platform specific
|
|||||||
packages, instead using the source for the code and the Python Package Index
|
packages, instead using the source for the code and the Python Package Index
|
||||||
(PyPi_).
|
(PyPi_).
|
||||||
|
|
||||||
.. _PyPi: https://pypi.python.org/pypi
|
.. _PyPi: http://pypi.python.org/pypi
|
||||||
|
|
||||||
It's expected that your system already has python2.7_, latest version of pip_,
|
It's expected that your system already has python2.7_, latest version of pip_,
|
||||||
and git_ available.
|
and git_ available.
|
||||||
|
|
||||||
.. _python2.7: https://www.python.org
|
.. _python2.7: http://www.python.org
|
||||||
.. _pip: https://pip.pypa.io/en/latest/installing/
|
.. _pip: http://www.pip-installer.org/en/latest/installing.html
|
||||||
.. _git: https://git-scm.com/
|
.. _git: http://git-scm.com/
|
||||||
|
|
||||||
Your system shall also have some additional system libraries:
|
Your system shall also have some additional system libraries:
|
||||||
|
|
||||||
|
|||||||
@@ -92,12 +92,6 @@ Detailed DevStack Instructions
|
|||||||
Note: if you want to use a specific branch, specify WATCHER_BRANCH in the
|
Note: if you want to use a specific branch, specify WATCHER_BRANCH in the
|
||||||
local.conf file. By default it will use the master branch.
|
local.conf file. By default it will use the master branch.
|
||||||
|
|
||||||
Note: watcher-api will default run under apache/httpd, set the variable
|
|
||||||
WATCHER_USE_MOD_WSGI=FALSE if you do not wish to run under apache/httpd.
|
|
||||||
For development environment it is suggested to set WATHCER_USE_MOD_WSGI
|
|
||||||
to FALSE. For Production environment it is suggested to keep it at the
|
|
||||||
default TRUE value.
|
|
||||||
|
|
||||||
#. Start stacking from the controller node::
|
#. Start stacking from the controller node::
|
||||||
|
|
||||||
./devstack/stack.sh
|
./devstack/stack.sh
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ for development purposes.
|
|||||||
To install Watcher from packaging, refer instead to Watcher `User
|
To install Watcher from packaging, refer instead to Watcher `User
|
||||||
Documentation`_.
|
Documentation`_.
|
||||||
|
|
||||||
.. _`Git Repository`: https://git.openstack.org/cgit/openstack/watcher
|
.. _`Git Repository`: http://git.openstack.org/cgit/openstack/watcher
|
||||||
.. _`User Documentation`: https://docs.openstack.org/developer/watcher/
|
.. _`User Documentation`: http://docs.openstack.org/developer/watcher/
|
||||||
|
|
||||||
Prerequisites
|
Prerequisites
|
||||||
=============
|
=============
|
||||||
@@ -35,10 +35,10 @@ following tools available on your system:
|
|||||||
**Reminder**: If you're successfully using a different platform, or a
|
**Reminder**: If you're successfully using a different platform, or a
|
||||||
different version of the above, please document your configuration here!
|
different version of the above, please document your configuration here!
|
||||||
|
|
||||||
.. _Python: https://www.python.org/
|
.. _Python: http://www.python.org/
|
||||||
.. _git: https://git-scm.com/
|
.. _git: http://git-scm.com/
|
||||||
.. _setuptools: https://pypi.python.org/pypi/setuptools
|
.. _setuptools: http://pypi.python.org/pypi/setuptools
|
||||||
.. _virtualenvwrapper: https://virtualenvwrapper.readthedocs.io/en/latest/install.html
|
.. _virtualenvwrapper: https://virtualenvwrapper.readthedocs.org/en/latest/install.html
|
||||||
|
|
||||||
Getting the latest code
|
Getting the latest code
|
||||||
=======================
|
=======================
|
||||||
@@ -175,12 +175,11 @@ The HTML files are available into ``doc/build`` directory.
|
|||||||
Configure the Watcher services
|
Configure the Watcher services
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Watcher services require a configuration file. Use tox to generate
|
Watcher services require a configuration file. There is a sample configuration
|
||||||
a sample configuration file that can be used to get started:
|
file that can be used to get started:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ tox -e genconfig
|
|
||||||
$ cp etc/watcher.conf.sample etc/watcher.conf
|
$ cp etc/watcher.conf.sample etc/watcher.conf
|
||||||
|
|
||||||
Most of the default configuration should be enough to get you going, but you
|
Most of the default configuration should be enough to get you going, but you
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Unit tests
|
|||||||
==========
|
==========
|
||||||
|
|
||||||
All unit tests should be run using `tox`_. To run the same unit tests that are
|
All unit tests should be run using `tox`_. To run the same unit tests that are
|
||||||
executing onto `Gerrit`_ which includes ``py35``, ``py27`` and ``pep8``, you
|
executing onto `Gerrit`_ which includes ``py34``, ``py27`` and ``pep8``, you
|
||||||
can issue the following command::
|
can issue the following command::
|
||||||
|
|
||||||
$ workon watcher
|
$ workon watcher
|
||||||
@@ -26,7 +26,7 @@ If you want to only run one of the aforementioned, you can then issue one of
|
|||||||
the following::
|
the following::
|
||||||
|
|
||||||
$ workon watcher
|
$ workon watcher
|
||||||
(watcher) $ tox -e py35
|
(watcher) $ tox -e py34
|
||||||
(watcher) $ tox -e py27
|
(watcher) $ tox -e py27
|
||||||
(watcher) $ tox -e pep8
|
(watcher) $ tox -e pep8
|
||||||
|
|
||||||
|
|||||||
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 |
@@ -4,14 +4,11 @@
|
|||||||
PENDING --> ONGOING: Audit request is received\nby the Watcher Decision Engine
|
PENDING --> ONGOING: Audit request is received\nby the Watcher Decision Engine
|
||||||
ONGOING --> FAILED: Audit fails\n(no solution found, technical error, ...)
|
ONGOING --> FAILED: Audit fails\n(no solution found, technical error, ...)
|
||||||
ONGOING --> SUCCEEDED: The Watcher Decision Engine\ncould find at least one Solution
|
ONGOING --> SUCCEEDED: The Watcher Decision Engine\ncould find at least one Solution
|
||||||
ONGOING --> SUSPENDED: Administrator wants to\nsuspend the Audit
|
|
||||||
SUSPENDED --> ONGOING: Administrator wants to\nresume the Audit
|
|
||||||
FAILED --> DELETED : Administrator wants to\narchive/delete the Audit
|
FAILED --> DELETED : Administrator wants to\narchive/delete the Audit
|
||||||
SUCCEEDED --> DELETED : Administrator wants to\narchive/delete the Audit
|
SUCCEEDED --> DELETED : Administrator wants to\narchive/delete the Audit
|
||||||
PENDING --> CANCELLED : Administrator cancels\nthe Audit
|
PENDING --> CANCELLED : Administrator cancels\nthe Audit
|
||||||
ONGOING --> CANCELLED : Administrator cancels\nthe Audit
|
ONGOING --> CANCELLED : Administrator cancels\nthe Audit
|
||||||
CANCELLED --> DELETED : Administrator wants to\narchive/delete the Audit
|
CANCELLED --> DELETED : Administrator wants to\narchive/delete the Audit
|
||||||
SUSPENDED --> DELETED: Administrator wants to\narchive/delete the Audit
|
|
||||||
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 |
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 36 KiB |
@@ -56,7 +56,6 @@ Getting Started
|
|||||||
dev/devstack
|
dev/devstack
|
||||||
deploy/configuration
|
deploy/configuration
|
||||||
deploy/conf-files
|
deploy/conf-files
|
||||||
deploy/apache-mod-wsgi
|
|
||||||
dev/notifications
|
dev/notifications
|
||||||
dev/testing
|
dev/testing
|
||||||
dev/rally_link
|
dev/rally_link
|
||||||
|
|||||||
@@ -72,9 +72,6 @@ Strategy parameter is:
|
|||||||
parameter type default Value description
|
parameter type default Value description
|
||||||
============== ====== ============= ====================================
|
============== ====== ============= ====================================
|
||||||
``threshold`` Number 35.0 Temperature threshold for migration
|
``threshold`` Number 35.0 Temperature threshold for migration
|
||||||
``period`` Number 30 The time interval in seconds for
|
|
||||||
getting statistic aggregation from
|
|
||||||
metric data source
|
|
||||||
============== ====== ============= ====================================
|
============== ====== ============= ====================================
|
||||||
|
|
||||||
Efficacy Indicator
|
Efficacy Indicator
|
||||||
|
|||||||
@@ -70,20 +70,6 @@ Default Watcher's planner:
|
|||||||
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
|
.. watcher-term:: watcher.decision_engine.planner.default.DefaultPlanner
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Strategy parameter is:
|
|
||||||
|
|
||||||
====================== ====== ============= ===================================
|
|
||||||
parameter type default Value description
|
|
||||||
====================== ====== ============= ===================================
|
|
||||||
``period`` Number 3600 The time interval in seconds
|
|
||||||
for getting statistic aggregation
|
|
||||||
from metric data source
|
|
||||||
====================== ====== ============= ===================================
|
|
||||||
|
|
||||||
|
|
||||||
Efficacy Indicator
|
Efficacy Indicator
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
# This is an example Apache2 configuration file for using
|
|
||||||
# Watcher API through mod_wsgi
|
|
||||||
Listen 9322
|
|
||||||
|
|
||||||
<VirtualHost *:9322>
|
|
||||||
WSGIDaemonProcess watcher-api user=stack group=stack processes=2 threads=2 display-name=%{GROUP}
|
|
||||||
WSGIScriptAlias / /opt/stack/watcher/watcher/api/app.wsgi
|
|
||||||
WSGIProcessGroup watcher-api
|
|
||||||
|
|
||||||
ErrorLog /var/log/httpd/watcher_error.log
|
|
||||||
LogLevel info
|
|
||||||
CustomLog /var/log/httpd/watcher_access.log combined
|
|
||||||
|
|
||||||
<Directory /opt/stack/watcher/watcher/api>
|
|
||||||
WSGIProcessGroup watcher-api
|
|
||||||
WSGIApplicationGroup %{GLOBAL}
|
|
||||||
AllowOverride All
|
|
||||||
Require all granted
|
|
||||||
</Directory>
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
2. Edit the ``/etc/watcher/watcher.conf`` file and complete the following
|
|
||||||
actions:
|
|
||||||
|
|
||||||
* In the ``[database]`` section, configure database access:
|
|
||||||
|
|
||||||
.. code-block:: ini
|
|
||||||
|
|
||||||
[database]
|
|
||||||
...
|
|
||||||
connection = mysql+pymysql://watcher:WATCHER_DBPASS@controller/watcher?charset=utf8
|
|
||||||
|
|
||||||
* In the `[DEFAULT]` section, configure the transport url for RabbitMQ message broker.
|
|
||||||
|
|
||||||
.. code-block:: ini
|
|
||||||
|
|
||||||
[DEFAULT]
|
|
||||||
...
|
|
||||||
control_exchange = watcher
|
|
||||||
transport_url = rabbit://openstack:RABBIT_PASS@controller
|
|
||||||
|
|
||||||
Replace the RABBIT_PASS with the password you chose for OpenStack user in RabbitMQ.
|
|
||||||
|
|
||||||
* In the `[keystone_authtoken]` section, configure Identity service access.
|
|
||||||
|
|
||||||
.. code-block:: ini
|
|
||||||
|
|
||||||
[keystone_authtoken]
|
|
||||||
...
|
|
||||||
auth_uri = http://controller:5000
|
|
||||||
auth_url = http://controller:35357
|
|
||||||
memcached_servers = controller:11211
|
|
||||||
auth_type = password
|
|
||||||
project_domain_name = default
|
|
||||||
user_domain_name = default
|
|
||||||
project_name = service
|
|
||||||
username = watcher
|
|
||||||
password = WATCHER_PASS
|
|
||||||
|
|
||||||
Replace WATCHER_PASS with the password you chose for the watcher user in the Identity service.
|
|
||||||
|
|
||||||
* Watcher interacts with other OpenStack projects via project clients, in order to instantiate these
|
|
||||||
clients, Watcher requests new session from Identity service. In the `[watcher_client_auth]` section,
|
|
||||||
configure the identity service access to interact with other OpenStack project clients.
|
|
||||||
|
|
||||||
.. code-block:: ini
|
|
||||||
|
|
||||||
[watcher_client_auth]
|
|
||||||
...
|
|
||||||
auth_type = password
|
|
||||||
auth_url = http://controller:35357
|
|
||||||
username = watcher
|
|
||||||
password = WATCHER_PASS
|
|
||||||
project_domain_name = default
|
|
||||||
user_domain_name = default
|
|
||||||
project_name = service
|
|
||||||
|
|
||||||
Replace WATCHER_PASS with the password you chose for the watcher user in the Identity service.
|
|
||||||
|
|
||||||
* In the `[oslo_messaging_notifications]` section, configure the messaging driver.
|
|
||||||
|
|
||||||
.. code-block:: ini
|
|
||||||
|
|
||||||
[oslo_messaging_notifications]
|
|
||||||
...
|
|
||||||
driver = messagingv2
|
|
||||||
|
|
||||||
3. Populate watcher database:
|
|
||||||
|
|
||||||
.. code-block:: ini
|
|
||||||
|
|
||||||
su -s /bin/sh -c "watcher-db-manage" watcher
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
Prerequisites
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Before you install and configure the Infrastructure Optimization service,
|
|
||||||
you must create a database, service credentials, and API endpoints.
|
|
||||||
|
|
||||||
1. Create the database, complete these steps:
|
|
||||||
|
|
||||||
* Use the database access client to connect to the database
|
|
||||||
server as the ``root`` user:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ mysql -u root -p
|
|
||||||
|
|
||||||
* Create the ``watcher`` database:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
CREATE DATABASE watcher CHARACTER SET utf8;
|
|
||||||
|
|
||||||
* Grant proper access to the ``watcher`` database:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
GRANT ALL PRIVILEGES ON watcher.* TO 'watcher'@'localhost' \
|
|
||||||
IDENTIFIED BY 'WATCHER_DBPASS';
|
|
||||||
GRANT ALL PRIVILEGES ON watcher.* TO 'watcher'@'%' \
|
|
||||||
IDENTIFIED BY 'WATCHER_DBPASS';
|
|
||||||
|
|
||||||
Replace ``WATCHER_DBPASS`` with a suitable password.
|
|
||||||
|
|
||||||
* Exit the database access client.
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
exit;
|
|
||||||
|
|
||||||
2. Source the ``admin`` credentials to gain access to
|
|
||||||
admin-only CLI commands:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ . admin-openrc
|
|
||||||
|
|
||||||
3. To create the service credentials, complete these steps:
|
|
||||||
|
|
||||||
* Create the ``watcher`` user:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack user create --domain default --password-prompt watcher
|
|
||||||
User Password:
|
|
||||||
Repeat User Password:
|
|
||||||
+---------------------+----------------------------------+
|
|
||||||
| Field | Value |
|
|
||||||
+---------------------+----------------------------------+
|
|
||||||
| domain_id | default |
|
|
||||||
| enabled | True |
|
|
||||||
| id | b18ee38e06034b748141beda8fc8bfad |
|
|
||||||
| name | watcher |
|
|
||||||
| options | {} |
|
|
||||||
| password_expires_at | None |
|
|
||||||
+---------------------+----------------------------------+
|
|
||||||
|
|
||||||
|
|
||||||
* Add the ``admin`` role to the ``watcher`` user:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack role add --project service --user watcher admin
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
This command produces no output.
|
|
||||||
|
|
||||||
* Create the watcher service entities:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack service create --name watcher --description "Infrastructure Optimization" infra-optim
|
|
||||||
+-------------+----------------------------------+
|
|
||||||
| Field | Value |
|
|
||||||
+-------------+----------------------------------+
|
|
||||||
| description | Infrastructure Optimization |
|
|
||||||
| enabled | True |
|
|
||||||
| id | d854f6fff0a64f77bda8003c8dedfada |
|
|
||||||
| name | watcher |
|
|
||||||
| type | infra-optim |
|
|
||||||
+-------------+----------------------------------+
|
|
||||||
|
|
||||||
|
|
||||||
4. Create the Infrastructure Optimization service API endpoints:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack endpoint create --region RegionOne \
|
|
||||||
infra-optim public http://controller:9322
|
|
||||||
+-------------+----------------------------------+
|
|
||||||
| Field | Value |
|
|
||||||
+-------------+----------------------------------+
|
|
||||||
| description | Infrastructure Optimization |
|
|
||||||
| enabled | True |
|
|
||||||
| id | d854f6fff0a64f77bda8003c8dedfada |
|
|
||||||
| name | watcher |
|
|
||||||
| type | infra-optim |
|
|
||||||
+-------------+----------------------------------+
|
|
||||||
|
|
||||||
$ openstack endpoint create --region RegionOne \
|
|
||||||
infra-optim internal http://controller:9322
|
|
||||||
+--------------+----------------------------------+
|
|
||||||
| Field | Value |
|
|
||||||
+--------------+----------------------------------+
|
|
||||||
| enabled | True |
|
|
||||||
| id | 225aef8465ef4df48a341aaaf2b0a390 |
|
|
||||||
| interface | internal |
|
|
||||||
| region | RegionOne |
|
|
||||||
| region_id | RegionOne |
|
|
||||||
| service_id | d854f6fff0a64f77bda8003c8dedfada |
|
|
||||||
| service_name | watcher |
|
|
||||||
| service_type | infra-optim |
|
|
||||||
| url | http://controller:9322 |
|
|
||||||
+--------------+----------------------------------+
|
|
||||||
|
|
||||||
$ openstack endpoint create --region RegionOne \
|
|
||||||
infra-optim admin http://controller:9322
|
|
||||||
+--------------+----------------------------------+
|
|
||||||
| Field | Value |
|
|
||||||
+--------------+----------------------------------+
|
|
||||||
| enabled | True |
|
|
||||||
| id | 375eb5057fb546edbdf3ee4866179672 |
|
|
||||||
| interface | admin |
|
|
||||||
| region | RegionOne |
|
|
||||||
| region_id | RegionOne |
|
|
||||||
| service_id | d854f6fff0a64f77bda8003c8dedfada |
|
|
||||||
| service_name | watcher |
|
|
||||||
| service_type | infra-optim |
|
|
||||||
| url | http://controller:9322 |
|
|
||||||
+--------------+----------------------------------+
|
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
# This file is execfile()d with the current directory set to its
|
|
||||||
# containing dir.
|
|
||||||
#
|
|
||||||
# Note that not all possible configuration values are present in this
|
|
||||||
# autogenerated file.
|
|
||||||
#
|
|
||||||
# All configuration values have a default; values that are commented out
|
|
||||||
# serve to show the default.
|
|
||||||
|
|
||||||
import os
|
|
||||||
# import sys
|
|
||||||
|
|
||||||
|
|
||||||
import openstackdocstheme
|
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
||||||
# sys.path.insert(0, os.path.abspath('.'))
|
|
||||||
|
|
||||||
# -- General configuration ------------------------------------------------
|
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
|
||||||
# needs_sphinx = '1.0'
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
|
||||||
# ones.
|
|
||||||
# TODO(ajaeger): enable PDF building, for example add 'rst2pdf.pdfbuilder'
|
|
||||||
# extensions =
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
|
||||||
# templates_path = ['_templates']
|
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = '.rst'
|
|
||||||
|
|
||||||
# The encoding of source files.
|
|
||||||
# source_encoding = 'utf-8-sig'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'index'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = u'Installation Guide for Infrastructure Optimization Service'
|
|
||||||
bug_tag = u'install-guide'
|
|
||||||
copyright = u'2016, OpenStack contributors'
|
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
|
||||||
# |version| and |release|, also used in various other places throughout the
|
|
||||||
# built documents.
|
|
||||||
#
|
|
||||||
# The short X.Y version.
|
|
||||||
version = '0.1'
|
|
||||||
# The full version, including alpha/beta/rc tags.
|
|
||||||
release = '0.1'
|
|
||||||
|
|
||||||
# A few variables have to be set for the log-a-bug feature.
|
|
||||||
# giturl: The location of conf.py on Git. Must be set manually.
|
|
||||||
# gitsha: The SHA checksum of the bug description. Automatically extracted
|
|
||||||
# from git log.
|
|
||||||
# bug_tag: Tag for categorizing the bug. Must be set manually.
|
|
||||||
# These variables are passed to the logabug code via html_context.
|
|
||||||
giturl = u'http://git.openstack.org/cgit/openstack/watcher/tree/install-guide/source' # noqa
|
|
||||||
git_cmd = "/usr/bin/git log | head -n1 | cut -f2 -d' '"
|
|
||||||
gitsha = os.popen(git_cmd).read().strip('\n')
|
|
||||||
html_context = {"gitsha": gitsha, "bug_tag": bug_tag,
|
|
||||||
"giturl": giturl,
|
|
||||||
"bug_project": "watcher"}
|
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
||||||
# for a list of supported languages.
|
|
||||||
# language = None
|
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
|
||||||
# non-false value, then it is used:
|
|
||||||
# today = ''
|
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
|
||||||
# today_fmt = '%B %d, %Y'
|
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
|
||||||
# directories to ignore when looking for source files.
|
|
||||||
exclude_patterns = ["common_prerequisites.rst", "common_configure.rst"]
|
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all
|
|
||||||
# documents.
|
|
||||||
# default_role = None
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
# add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
# add_module_names = True
|
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
|
||||||
# output. They are ignored by default.
|
|
||||||
# show_authors = False
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'sphinx'
|
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
|
||||||
# modindex_common_prefix = []
|
|
||||||
|
|
||||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
|
||||||
# keep_warnings = False
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ----------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
||||||
# a list of builtin themes.
|
|
||||||
html_theme = 'openstackdocs'
|
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
|
||||||
# further. For a list of options available for each theme, see the
|
|
||||||
# documentation.
|
|
||||||
# html_theme_options = {}
|
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
|
||||||
html_theme_path = [openstackdocstheme.get_html_theme_path()]
|
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
|
||||||
# "<project> v<release> documentation".
|
|
||||||
# html_title = None
|
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
|
||||||
# html_short_title = None
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
|
||||||
# of the sidebar.
|
|
||||||
# html_logo = None
|
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
|
||||||
# pixels large.
|
|
||||||
# html_favicon = None
|
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
||||||
# html_static_path = []
|
|
||||||
|
|
||||||
# Add any extra paths that contain custom files (such as robots.txt or
|
|
||||||
# .htaccess) here, relative to this directory. These files are copied
|
|
||||||
# directly to the root of the documentation.
|
|
||||||
# html_extra_path = []
|
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
|
||||||
# using the given strftime format.
|
|
||||||
# So that we can enable "log-a-bug" links from each output HTML page, this
|
|
||||||
# variable must be set to a format that includes year, month, day, hours and
|
|
||||||
# minutes.
|
|
||||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
|
||||||
|
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
|
||||||
# typographically correct entities.
|
|
||||||
# html_use_smartypants = True
|
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
|
||||||
# html_sidebars = {}
|
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
|
||||||
# template names.
|
|
||||||
# html_additional_pages = {}
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
# html_domain_indices = True
|
|
||||||
|
|
||||||
# If false, no index is generated.
|
|
||||||
html_use_index = False
|
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
|
||||||
# html_split_index = False
|
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
|
||||||
html_show_sourcelink = False
|
|
||||||
|
|
||||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
|
||||||
# html_show_sphinx = True
|
|
||||||
|
|
||||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
|
||||||
# html_show_copyright = True
|
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
|
||||||
# base URL from which the finished HTML is served.
|
|
||||||
# html_use_opensearch = ''
|
|
||||||
|
|
||||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
|
||||||
# html_file_suffix = None
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = 'install-guide'
|
|
||||||
|
|
||||||
# If true, publish source files
|
|
||||||
html_copy_source = False
|
|
||||||
|
|
||||||
# -- Options for LaTeX output ---------------------------------------------
|
|
||||||
|
|
||||||
latex_elements = {
|
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
|
||||||
# 'papersize': 'letterpaper',
|
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
|
||||||
# 'pointsize': '10pt',
|
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
# 'preamble': '',
|
|
||||||
}
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title,
|
|
||||||
# author, documentclass [howto, manual, or own class]).
|
|
||||||
latex_documents = [
|
|
||||||
('index', 'InstallGuide.tex', u'Install Guide',
|
|
||||||
u'OpenStack contributors', 'manual'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
|
||||||
# the title page.
|
|
||||||
# latex_logo = None
|
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
|
||||||
# not chapters.
|
|
||||||
# latex_use_parts = False
|
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
|
||||||
# latex_show_pagerefs = False
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
|
||||||
# latex_show_urls = False
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
# latex_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
# latex_domain_indices = True
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output ---------------------------------------
|
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
|
||||||
# (source start file, name, description, authors, manual section).
|
|
||||||
man_pages = [
|
|
||||||
('index', 'installguide', u'Install Guide',
|
|
||||||
[u'OpenStack contributors'], 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
|
||||||
# man_show_urls = False
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output -------------------------------------------
|
|
||||||
|
|
||||||
# Grouping the document tree into Texinfo files. List of tuples
|
|
||||||
# (source start file, target name, title, author,
|
|
||||||
# dir menu entry, description, category)
|
|
||||||
texinfo_documents = [
|
|
||||||
('index', 'InstallGuide', u'Install Guide',
|
|
||||||
u'OpenStack contributors', 'InstallGuide',
|
|
||||||
'This guide shows OpenStack end users how to install '
|
|
||||||
'an OpenStack cloud.', 'Miscellaneous'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
# texinfo_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
# texinfo_domain_indices = True
|
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
|
||||||
# texinfo_show_urls = 'footnote'
|
|
||||||
|
|
||||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
|
||||||
# texinfo_no_detailmenu = False
|
|
||||||
|
|
||||||
# -- Options for Internationalization output ------------------------------
|
|
||||||
locale_dirs = ['locale/']
|
|
||||||
|
|
||||||
# -- Options for PDF output --------------------------------------------------
|
|
||||||
|
|
||||||
pdf_documents = [
|
|
||||||
('index', u'InstallGuide', u'Install Guide',
|
|
||||||
u'OpenStack contributors')
|
|
||||||
]
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
============================================
|
|
||||||
Infrastructure Optimization service overview
|
|
||||||
============================================
|
|
||||||
The Infrastructure Optimization service provides flexible and scalable
|
|
||||||
optimization service for multi-tenant OpenStack based clouds.
|
|
||||||
|
|
||||||
The Infrastructure Optimization service consists of the following components:
|
|
||||||
|
|
||||||
``watcher`` command-line client
|
|
||||||
A CLI to communicate with ``watcher-api`` to optimize the cloud.
|
|
||||||
|
|
||||||
``watcher-api`` service
|
|
||||||
An OpenStack-native REST API that accepts and responds to end-user calls
|
|
||||||
by processing them and forwarding to appropriate underlying watcher
|
|
||||||
services via AMQP.
|
|
||||||
|
|
||||||
``watcher-decision-engine`` service
|
|
||||||
It runs audit and return an action plan to achieve optimization goal
|
|
||||||
specified by the end-user in audit.
|
|
||||||
|
|
||||||
``watcher-applier`` service
|
|
||||||
It executes action plan built by watcher-decision-engine. It interacts with
|
|
||||||
other OpenStack components like nova to execute the given action
|
|
||||||
plan.
|
|
||||||
|
|
||||||
``watcher-dashboard``
|
|
||||||
Watcher UI implemented as a plugin for the OpenStack Dashboard.
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
===================================
|
|
||||||
Infrastructure Optimization service
|
|
||||||
===================================
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
get_started.rst
|
|
||||||
install.rst
|
|
||||||
verify.rst
|
|
||||||
next-steps.rst
|
|
||||||
|
|
||||||
The Infrastructure Optimization service (watcher) provides
|
|
||||||
flexible and scalable resource optimization service for
|
|
||||||
multi-tenant OpenStack-based clouds.
|
|
||||||
|
|
||||||
Watcher provides a complete optimization loop including
|
|
||||||
everything from a metrics receiver, complex event processor
|
|
||||||
and profiler, optimization processor and an action plan
|
|
||||||
applier. This provides a robust framework to realize a wide
|
|
||||||
range of cloud optimization goals, including the reduction
|
|
||||||
of data center operating costs, increased system performance
|
|
||||||
via intelligent virtual machine migration, increased energy
|
|
||||||
efficiency—and more!
|
|
||||||
|
|
||||||
watcher also supports a pluggable architecture by which custom
|
|
||||||
optimization algorithms, data metrics and data profilers can be
|
|
||||||
developed and inserted into the Watcher framework.
|
|
||||||
|
|
||||||
check the documentation for watcher optimization strategies at
|
|
||||||
https://docs.openstack.org/developer/watcher/strategies
|
|
||||||
|
|
||||||
check watcher glossary at
|
|
||||||
https://docs.openstack.org/developer/watcher/glossary.html
|
|
||||||
|
|
||||||
|
|
||||||
This chapter assumes a working setup of OpenStack following the
|
|
||||||
`OpenStack Installation Tutorial
|
|
||||||
<https://docs.openstack.org/project-install-guide/ocata/>`_.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
.. _install-obs:
|
|
||||||
|
|
||||||
|
|
||||||
Install and configure for openSUSE and SUSE Linux Enterprise
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This section describes how to install and configure the Infrastructure Optimization service
|
|
||||||
for openSUSE Leap 42.1 and SUSE Linux Enterprise Server 12 SP1.
|
|
||||||
|
|
||||||
.. include:: common_prerequisites.rst
|
|
||||||
|
|
||||||
Install and configure components
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
#. Install the packages:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
# zypper --quiet --non-interactive install
|
|
||||||
|
|
||||||
.. include:: common_configure.rst
|
|
||||||
|
|
||||||
|
|
||||||
Finalize installation
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Start the Infrastructure Optimization services and configure them to start when
|
|
||||||
the system boots:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
# systemctl enable openstack-watcher-api.service
|
|
||||||
|
|
||||||
# systemctl start openstack-watcher-api.service
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
.. _install-rdo:
|
|
||||||
|
|
||||||
Install and configure for Red Hat Enterprise Linux and CentOS
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
|
|
||||||
This section describes how to install and configure the Infrastructure Optimization service
|
|
||||||
for Red Hat Enterprise Linux 7 and CentOS 7.
|
|
||||||
|
|
||||||
.. include:: common_prerequisites.rst
|
|
||||||
|
|
||||||
Install and configure components
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
1. Install the packages:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
# sudo yum install openstack-watcher-api openstack-watcher-applier \
|
|
||||||
openstack-watcher-decision-engine
|
|
||||||
|
|
||||||
.. include:: common_configure.rst
|
|
||||||
|
|
||||||
Finalize installation
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Start the Infrastructure Optimization services and configure them to start when
|
|
||||||
the system boots:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
# systemctl enable openstack-watcher-api.service \
|
|
||||||
openstack-watcher-decision-engine.service \
|
|
||||||
openstack-watcher-applier.service
|
|
||||||
|
|
||||||
# systemctl start openstack-watcher-api.service \
|
|
||||||
openstack-watcher-decision-engine.service \
|
|
||||||
openstack-watcher-applier.service
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
.. _install-ubuntu:
|
|
||||||
|
|
||||||
Install and configure for Ubuntu
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This section describes how to install and configure the Infrastructure Optimization
|
|
||||||
service for Ubuntu 14.04 (LTS).
|
|
||||||
|
|
||||||
.. include:: common_prerequisites.rst
|
|
||||||
|
|
||||||
Install and configure components
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
1. Install the packages:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
# apt install watcher-api watcher-decision-engine \
|
|
||||||
watcher-applier
|
|
||||||
|
|
||||||
# apt install python-watcherclient
|
|
||||||
|
|
||||||
.. include:: common_configure.rst
|
|
||||||
|
|
||||||
Finalize installation
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Restart the Infrastructure Optimization services:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
# service watcher-api restart
|
|
||||||
# service watcher-decision-engine restart
|
|
||||||
# service watcher-applier restart
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
.. _install:
|
|
||||||
|
|
||||||
Install and configure
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This section describes how to install and configure the
|
|
||||||
Infrastructure Optimization service, code-named watcher, on the controller node.
|
|
||||||
|
|
||||||
This section assumes that you already have a working OpenStack
|
|
||||||
environment with at least the following components installed:
|
|
||||||
Identity Service, Compute Service, Telemetry data collection service.
|
|
||||||
|
|
||||||
Note that installation and configuration vary by distribution.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
install-obs.rst
|
|
||||||
install-rdo.rst
|
|
||||||
install-ubuntu.rst
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
.. _next-steps:
|
|
||||||
|
|
||||||
Next steps
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
Your OpenStack environment now includes the watcher service.
|
|
||||||
|
|
||||||
To add additional services, see
|
|
||||||
https://docs.openstack.org/project-install-guide/ocata/.
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
.. _verify:
|
|
||||||
|
|
||||||
Verify operation
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Verify operation of the Infrastructure Optimization service.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Perform these commands on the controller node.
|
|
||||||
|
|
||||||
1. Source the ``admin`` project credentials to gain access to
|
|
||||||
admin-only CLI commands:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ . admin-openrc
|
|
||||||
|
|
||||||
2. List service components to verify successful launch and registration
|
|
||||||
of each process:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack optimize service list
|
|
||||||
+----+-------------------------+------------+--------+
|
|
||||||
| ID | Name | Host | Status |
|
|
||||||
+----+-------------------------+------------+--------+
|
|
||||||
| 1 | watcher-decision-engine | controller | ACTIVE |
|
|
||||||
| 2 | watcher-applier | controller | ACTIVE |
|
|
||||||
+----+-------------------------+------------+--------+
|
|
||||||
|
|
||||||
3. List goals and strategies:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack optimize goal list
|
|
||||||
+--------------------------------------+----------------------+----------------------+
|
|
||||||
| UUID | Name | Display name |
|
|
||||||
+--------------------------------------+----------------------+----------------------+
|
|
||||||
| a8cd6d1a-008b-4ff0-8dbc-b30493fcc5b9 | dummy | Dummy goal |
|
|
||||||
| 03953f2f-02d0-42b5-9a12-7ba500a54395 | workload_balancing | Workload Balancing |
|
|
||||||
| de0f8714-984b-4d6b-add1-9cad8120fbce | server_consolidation | Server Consolidation |
|
|
||||||
| f056bc80-c6d1-40dc-b002-938ccade9385 | thermal_optimization | Thermal Optimization |
|
|
||||||
| e7062856-892e-4f0f-b84d-b828464b3fd0 | airflow_optimization | Airflow Optimization |
|
|
||||||
| 1f038da9-b36c-449f-9f04-c225bf3eb478 | unclassified | Unclassified |
|
|
||||||
+--------------------------------------+----------------------+----------------------+
|
|
||||||
|
|
||||||
$ openstack optimize strategy list
|
|
||||||
+--------------------------------------+---------------------------+---------------------------------------------+----------------------+
|
|
||||||
| UUID | Name | Display name | Goal |
|
|
||||||
+--------------------------------------+---------------------------+---------------------------------------------+----------------------+
|
|
||||||
| 98ae84c8-7c9b-4cbd-8d9c-4bd7c6b106eb | dummy | Dummy strategy | dummy |
|
|
||||||
| 02a170b6-c72e-479d-95c0-8a4fdd4cc1ef | dummy_with_scorer | Dummy Strategy using sample Scoring Engines | dummy |
|
|
||||||
| 8bf591b8-57e5-4a9e-8c7d-c37bda735a45 | outlet_temperature | Outlet temperature based strategy | thermal_optimization |
|
|
||||||
| 8a0810fb-9d9a-47b9-ab25-e442878abc54 | vm_workload_consolidation | VM Workload Consolidation Strategy | server_consolidation |
|
|
||||||
| 1718859c-3eb5-45cb-9220-9cb79fe42fa5 | basic | Basic offline consolidation | server_consolidation |
|
|
||||||
| b5e7f5f1-4824-42c7-bb52-cf50724f67bf | workload_stabilization | Workload stabilization | workload_balancing |
|
|
||||||
| f853d71e-9286-4df3-9d3e-8eaf0f598e07 | workload_balance | Workload Balance Migration Strategy | workload_balancing |
|
|
||||||
| 58bdfa89-95b5-4630-adf6-fd3af5ff1f75 | uniform_airflow | Uniform airflow migration strategy | airflow_optimization |
|
|
||||||
| 66fde55d-a612-4be9-8cb0-ea63472b420b | dummy_with_resize | Dummy strategy with resize | dummy |
|
|
||||||
+--------------------------------------+---------------------------+---------------------------------------------+----------------------+
|
|
||||||
|
|
||||||
4. Run an action plan by creating an audit with dummy goal:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack optimize audit create --goal dummy
|
|
||||||
+--------------+--------------------------------------+
|
|
||||||
| Field | Value |
|
|
||||||
+--------------+--------------------------------------+
|
|
||||||
| UUID | e94d4826-ad4e-44df-ad93-dff489fde457 |
|
|
||||||
| Created At | 2017-05-23T11:46:58.763394+00:00 |
|
|
||||||
| Updated At | None |
|
|
||||||
| Deleted At | None |
|
|
||||||
| State | PENDING |
|
|
||||||
| Audit Type | ONESHOT |
|
|
||||||
| Parameters | {} |
|
|
||||||
| Interval | None |
|
|
||||||
| Goal | dummy |
|
|
||||||
| Strategy | auto |
|
|
||||||
| Audit Scope | [] |
|
|
||||||
| Auto Trigger | False |
|
|
||||||
+--------------+--------------------------------------+
|
|
||||||
|
|
||||||
$ openstack optimize audit list
|
|
||||||
+--------------------------------------+------------+-----------+-------+----------+--------------+
|
|
||||||
| UUID | Audit Type | State | Goal | Strategy | Auto Trigger |
|
|
||||||
+--------------------------------------+------------+-----------+-------+----------+--------------+
|
|
||||||
| e94d4826-ad4e-44df-ad93-dff489fde457 | ONESHOT | SUCCEEDED | dummy | auto | False |
|
|
||||||
+--------------------------------------+------------+-----------+-------+----------+--------------+
|
|
||||||
|
|
||||||
$ openstack optimize actionplan list
|
|
||||||
+--------------------------------------+--------------------------------------+-------------+------------+-----------------+
|
|
||||||
| UUID | Audit | State | Updated At | Global efficacy |
|
|
||||||
+--------------------------------------+--------------------------------------+-------------+------------+-----------------+
|
|
||||||
| ba9ce6b3-969c-4b8e-bb61-ae24e8630f81 | e94d4826-ad4e-44df-ad93-dff489fde457 | RECOMMENDED | None | None |
|
|
||||||
+--------------------------------------+--------------------------------------+-------------+------------+-----------------+
|
|
||||||
|
|
||||||
$ openstack optimize actionplan start ba9ce6b3-969c-4b8e-bb61-ae24e8630f81
|
|
||||||
+---------------------+--------------------------------------+
|
|
||||||
| Field | Value |
|
|
||||||
+---------------------+--------------------------------------+
|
|
||||||
| UUID | ba9ce6b3-969c-4b8e-bb61-ae24e8630f81 |
|
|
||||||
| Created At | 2017-05-23T11:46:58+00:00 |
|
|
||||||
| Updated At | 2017-05-23T11:53:12+00:00 |
|
|
||||||
| Deleted At | None |
|
|
||||||
| Audit | e94d4826-ad4e-44df-ad93-dff489fde457 |
|
|
||||||
| Strategy | dummy |
|
|
||||||
| State | ONGOING |
|
|
||||||
| Efficacy indicators | [] |
|
|
||||||
| Global efficacy | {} |
|
|
||||||
+---------------------+--------------------------------------+
|
|
||||||
|
|
||||||
$ openstack optimize actionplan list
|
|
||||||
+--------------------------------------+--------------------------------------+-----------+---------------------------+-----------------+
|
|
||||||
| UUID | Audit | State | Updated At | Global efficacy |
|
|
||||||
+--------------------------------------+--------------------------------------+-----------+---------------------------+-----------------+
|
|
||||||
| ba9ce6b3-969c-4b8e-bb61-ae24e8630f81 | e94d4826-ad4e-44df-ad93-dff489fde457 | SUCCEEDED | 2017-05-23T11:53:16+00:00 | None |
|
|
||||||
+--------------------------------------+--------------------------------------+-----------+---------------------------+-----------------+
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
features:
|
|
||||||
- |
|
|
||||||
Adds feature to cancel an action-plan.
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
---
|
---
|
||||||
features:
|
features:
|
||||||
- Check the creation time of the action plan,
|
- Add superseded state for an action plan if the cluster data model has
|
||||||
and set its state to SUPERSEDED if it has expired.
|
changed after it has been created.
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
features:
|
|
||||||
- |
|
|
||||||
Added SUSPENDED audit state
|
|
||||||
@@ -1,16 +1,3 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
# 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.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,17 +1,3 @@
|
|||||||
..
|
|
||||||
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.
|
|
||||||
|
|
||||||
=================================================
|
|
||||||
Welcome to watcher's Release Notes documentation!
|
Welcome to watcher's Release Notes documentation!
|
||||||
=================================================
|
=================================================
|
||||||
|
|
||||||
@@ -21,6 +7,5 @@ Contents:
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
unreleased
|
unreleased
|
||||||
ocata
|
|
||||||
newton
|
newton
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
# Gérald LONLAS <g.lonlas@gmail.com>, 2016. #zanata
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: watcher 1.0.1.dev51\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2017-03-21 11:57+0000\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"PO-Revision-Date: 2016-10-22 06:44+0000\n"
|
|
||||||
"Last-Translator: Gérald LONLAS <g.lonlas@gmail.com>\n"
|
|
||||||
"Language-Team: French\n"
|
|
||||||
"Language: fr\n"
|
|
||||||
"X-Generator: Zanata 3.9.6\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
|
||||||
|
|
||||||
msgid "0.29.0"
|
|
||||||
msgstr "0.29.0"
|
|
||||||
|
|
||||||
msgid "Contents:"
|
|
||||||
msgstr "Contenu :"
|
|
||||||
|
|
||||||
msgid "Current Series Release Notes"
|
|
||||||
msgstr "Note de la release actuelle"
|
|
||||||
|
|
||||||
msgid "New Features"
|
|
||||||
msgstr "Nouvelles fonctionnalités"
|
|
||||||
|
|
||||||
msgid "Newton Series Release Notes"
|
|
||||||
msgstr "Note de release pour Newton"
|
|
||||||
|
|
||||||
msgid "Welcome to watcher's Release Notes documentation!"
|
|
||||||
msgstr "Bienvenue dans la documentation de la note de Release de Watcher"
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
===================================
|
|
||||||
Ocata Series Release Notes
|
|
||||||
===================================
|
|
||||||
|
|
||||||
.. release-notes::
|
|
||||||
:branch: origin/stable/ocata
|
|
||||||
@@ -5,43 +5,41 @@
|
|||||||
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.20.0 # Apache-2.0
|
keystoneauth1>=2.18.0 # Apache-2.0
|
||||||
keystonemiddleware>=4.12.0 # Apache-2.0
|
keystonemiddleware>=4.12.0 # Apache-2.0
|
||||||
lxml!=3.7.0,>=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>=4.0.0 # Apache-2.0
|
oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
|
||||||
oslo.context>=2.14.0 # Apache-2.0
|
oslo.context>=2.9.0 # Apache-2.0
|
||||||
oslo.db>=4.21.1 # Apache-2.0
|
oslo.db>=4.15.0 # Apache-2.0
|
||||||
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
|
oslo.i18n>=2.1.0 # Apache-2.0
|
||||||
oslo.log>=3.22.0 # Apache-2.0
|
oslo.log>=3.11.0 # Apache-2.0
|
||||||
oslo.messaging!=5.25.0,>=5.24.2 # Apache-2.0
|
oslo.messaging>=5.14.0 # Apache-2.0
|
||||||
oslo.policy>=1.17.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.20.0 # Apache-2.0
|
oslo.utils>=3.18.0 # Apache-2.0
|
||||||
oslo.versionedobjects>=1.17.0 # Apache-2.0
|
oslo.versionedobjects>=1.17.0 # Apache-2.0
|
||||||
PasteDeploy>=1.5.0 # MIT
|
PasteDeploy>=1.5.0 # MIT
|
||||||
pbr!=2.1.0,>=2.0.0 # 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
|
||||||
PrettyTable<0.8,>=0.7.1 # BSD
|
PrettyTable<0.8,>=0.7.1 # BSD
|
||||||
voluptuous>=0.8.9 # BSD License
|
voluptuous>=0.8.9 # BSD License
|
||||||
gnocchiclient>=2.7.0 # Apache-2.0
|
|
||||||
python-ceilometerclient>=2.5.0 # Apache-2.0
|
python-ceilometerclient>=2.5.0 # Apache-2.0
|
||||||
python-cinderclient>=2.1.0 # Apache-2.0
|
python-cinderclient!=1.7.0,!=1.7.1,>=1.6.0 # Apache-2.0
|
||||||
python-glanceclient>=2.7.0 # Apache-2.0
|
python-glanceclient>=2.5.0 # Apache-2.0
|
||||||
python-keystoneclient>=3.8.0 # Apache-2.0
|
python-keystoneclient>=3.8.0 # Apache-2.0
|
||||||
python-monascaclient>=1.1.0 # Apache-2.0
|
python-monascaclient>=1.1.0 # Apache-2.0
|
||||||
python-neutronclient>=6.3.0 # Apache-2.0
|
python-neutronclient>=5.1.0 # Apache-2.0
|
||||||
python-novaclient>=7.1.0 # Apache-2.0
|
python-novaclient!=7.0.0,>=6.0.0 # Apache-2.0
|
||||||
python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0
|
python-openstackclient>=3.3.0 # Apache-2.0
|
||||||
python-ironicclient>=1.11.0 # Apache-2.0
|
|
||||||
six>=1.9.0 # MIT
|
six>=1.9.0 # MIT
|
||||||
SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT
|
SQLAlchemy<1.1.0,>=1.0.10 # MIT
|
||||||
stevedore>=1.20.0 # Apache-2.0
|
stevedore>=1.17.1 # Apache-2.0
|
||||||
taskflow>=2.7.0 # Apache-2.0
|
taskflow>=2.7.0 # Apache-2.0
|
||||||
WebOb>=1.7.1 # MIT
|
WebOb>=1.6.0 # MIT
|
||||||
WSME>=0.8 # MIT
|
WSME>=0.8 # MIT
|
||||||
networkx>=1.10 # BSD
|
networkx>=1.10 # BSD
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ classifier =
|
|||||||
Programming Language :: Python :: 2
|
Programming Language :: Python :: 2
|
||||||
Programming Language :: Python :: 2.7
|
Programming Language :: Python :: 2.7
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
|
Programming Language :: Python :: 3.4
|
||||||
Programming Language :: Python :: 3.5
|
Programming Language :: Python :: 3.5
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -25,5 +25,5 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
setup_requires=['pbr>=2.0.0'],
|
setup_requires=['pbr>=1.8'],
|
||||||
pbr=True)
|
pbr=True)
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# of appearance. Changing the order has an impact on the overall integration
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
coverage!=4.4,>=4.0 # Apache-2.0
|
coverage>=4.0 # Apache-2.0
|
||||||
doc8 # Apache-2.0
|
doc8 # Apache-2.0
|
||||||
freezegun>=0.3.6 # Apache-2.0
|
freezegun>=0.3.6 # Apache-2.0
|
||||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
hacking<0.11,>=0.10.2
|
||||||
mock>=2.0 # BSD
|
mock>=2.0 # BSD
|
||||||
oslotest>=1.10.0 # Apache-2.0
|
oslotest>=1.10.0 # Apache-2.0
|
||||||
os-testr>=0.8.0 # Apache-2.0
|
os-testr>=0.8.0 # Apache-2.0
|
||||||
@@ -15,14 +15,12 @@ testscenarios>=0.4 # Apache-2.0/BSD
|
|||||||
testtools>=1.4.0 # MIT
|
testtools>=1.4.0 # MIT
|
||||||
|
|
||||||
# Doc requirements
|
# Doc requirements
|
||||||
openstackdocstheme>=1.5.0 # Apache-2.0
|
|
||||||
oslosphinx>=4.7.0 # Apache-2.0
|
oslosphinx>=4.7.0 # Apache-2.0
|
||||||
sphinx!=1.6.1,>=1.5.1 # BSD
|
sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
|
||||||
sphinxcontrib-pecanwsme>=0.8 # Apache-2.0
|
sphinxcontrib-pecanwsme>=0.8 # Apache-2.0
|
||||||
|
|
||||||
|
|
||||||
# releasenotes
|
# releasenotes
|
||||||
reno!=2.3.1,>=1.8.0 # Apache-2.0
|
reno>=1.8.0 # Apache-2.0
|
||||||
|
|
||||||
# bandit
|
# bandit
|
||||||
bandit>=1.1.0 # Apache-2.0
|
bandit>=1.1.0 # Apache-2.0
|
||||||
|
|||||||
11
tox.ini
11
tox.ini
@@ -1,18 +1,16 @@
|
|||||||
[tox]
|
[tox]
|
||||||
minversion = 1.8
|
minversion = 1.8
|
||||||
envlist = py35,py27,pep8
|
envlist = py35,py34,py27,pep8
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
whitelist_externals = find
|
whitelist_externals = find
|
||||||
rm
|
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}
|
||||||
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
|
||||||
setenv =
|
setenv =
|
||||||
VIRTUAL_ENV={envdir}
|
VIRTUAL_ENV={envdir}
|
||||||
deps = -r{toxinidir}/test-requirements.txt
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
commands =
|
commands =
|
||||||
rm -f .testrepository/times.dbm
|
|
||||||
find . -type f -name "*.py[c|o]" -delete
|
find . -type f -name "*.py[c|o]" -delete
|
||||||
ostestr --concurrency=6 {posargs}
|
ostestr --concurrency=6 {posargs}
|
||||||
|
|
||||||
@@ -47,7 +45,7 @@ commands =
|
|||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
show-source=True
|
show-source=True
|
||||||
ignore= H105,E123,E226,N320
|
ignore=
|
||||||
builtins= _
|
builtins= _
|
||||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,*sqlalchemy/alembic/versions/*,demo/,releasenotes
|
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,*sqlalchemy/alembic/versions/*,demo/,releasenotes
|
||||||
|
|
||||||
@@ -69,6 +67,3 @@ commands = sphinx-build -a -W -E -d releasenotes/build/doctrees -b html releasen
|
|||||||
[testenv:bandit]
|
[testenv:bandit]
|
||||||
deps = -r{toxinidir}/test-requirements.txt
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
commands = bandit -r watcher -x tests -n5 -ll -s B320
|
commands = bandit -r watcher -x tests -n5 -ll -s B320
|
||||||
|
|
||||||
[testenv:install-guide]
|
|
||||||
commands = sphinx-build -a -E -W -d install-guide/build/doctrees -b html install-guide/source install-guide/build/html
|
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
# -*- mode: python -*-
|
|
||||||
# -*- encoding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
"""
|
|
||||||
Use this file for deploying the API service under Apache2 mod_wsgi.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
import oslo_i18n as i18n
|
|
||||||
from oslo_log import log
|
|
||||||
|
|
||||||
from watcher.api import app
|
|
||||||
from watcher.common import service
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
i18n.install('watcher')
|
|
||||||
|
|
||||||
service.prepare_service(sys.argv)
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
LOG.debug("Configuration:")
|
|
||||||
CONF.log_opt_values(LOG, log.DEBUG)
|
|
||||||
|
|
||||||
application = app.VersionSelectorApplication()
|
|
||||||
|
|
||||||
@@ -488,7 +488,6 @@ class ActionPlansController(rest.RestController):
|
|||||||
raise exception.PatchError(patch=patch, reason=e)
|
raise exception.PatchError(patch=patch, reason=e)
|
||||||
|
|
||||||
launch_action_plan = False
|
launch_action_plan = False
|
||||||
cancel_action_plan = False
|
|
||||||
|
|
||||||
# transitions that are allowed via PATCH
|
# transitions that are allowed via PATCH
|
||||||
allowed_patch_transitions = [
|
allowed_patch_transitions = [
|
||||||
@@ -497,7 +496,7 @@ class ActionPlansController(rest.RestController):
|
|||||||
(ap_objects.State.RECOMMENDED,
|
(ap_objects.State.RECOMMENDED,
|
||||||
ap_objects.State.CANCELLED),
|
ap_objects.State.CANCELLED),
|
||||||
(ap_objects.State.ONGOING,
|
(ap_objects.State.ONGOING,
|
||||||
ap_objects.State.CANCELLING),
|
ap_objects.State.CANCELLED),
|
||||||
(ap_objects.State.PENDING,
|
(ap_objects.State.PENDING,
|
||||||
ap_objects.State.CANCELLED),
|
ap_objects.State.CANCELLED),
|
||||||
]
|
]
|
||||||
@@ -516,8 +515,6 @@ class ActionPlansController(rest.RestController):
|
|||||||
|
|
||||||
if action_plan.state == ap_objects.State.PENDING:
|
if action_plan.state == ap_objects.State.PENDING:
|
||||||
launch_action_plan = True
|
launch_action_plan = True
|
||||||
if action_plan.state == ap_objects.State.CANCELLED:
|
|
||||||
cancel_action_plan = True
|
|
||||||
|
|
||||||
# Update only the fields that have changed
|
# Update only the fields that have changed
|
||||||
for field in objects.ActionPlan.fields:
|
for field in objects.ActionPlan.fields:
|
||||||
@@ -537,16 +534,6 @@ class ActionPlansController(rest.RestController):
|
|||||||
|
|
||||||
action_plan_to_update.save()
|
action_plan_to_update.save()
|
||||||
|
|
||||||
# NOTE: if action plan is cancelled from pending or recommended
|
|
||||||
# state update action state here only
|
|
||||||
if cancel_action_plan:
|
|
||||||
filters = {'action_plan_uuid': action_plan.uuid}
|
|
||||||
actions = objects.Action.list(pecan.request.context,
|
|
||||||
filters=filters, eager=True)
|
|
||||||
for a in actions:
|
|
||||||
a.state = objects.action.State.CANCELLED
|
|
||||||
a.save()
|
|
||||||
|
|
||||||
if launch_action_plan:
|
if launch_action_plan:
|
||||||
applier_client = rpcapi.ApplierAPI()
|
applier_client = rpcapi.ApplierAPI()
|
||||||
applier_client.launch_action_plan(pecan.request.context,
|
applier_client.launch_action_plan(pecan.request.context,
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -129,15 +144,8 @@ class AuditPatchType(types.JsonPatchType):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate(patch):
|
def validate(patch):
|
||||||
|
serialized_patch = {'path': patch.path, 'op': patch.op}
|
||||||
def is_new_state_none(p):
|
if patch.path in AuditPatchType.mandatory_attrs():
|
||||||
return p.path == '/state' and p.op == 'replace' and p.value is None
|
|
||||||
|
|
||||||
serialized_patch = {'path': patch.path,
|
|
||||||
'op': patch.op,
|
|
||||||
'value': patch.value}
|
|
||||||
if (patch.path in AuditPatchType.mandatory_attrs() or
|
|
||||||
is_new_state_none(patch)):
|
|
||||||
msg = _("%(field)s can't be updated.")
|
msg = _("%(field)s can't be updated.")
|
||||||
raise exception.PatchError(
|
raise exception.PatchError(
|
||||||
patch=serialized_patch,
|
patch=serialized_patch,
|
||||||
@@ -301,7 +309,7 @@ class Audit(base.APIBase):
|
|||||||
audit.unset_fields_except(['uuid', 'audit_type', 'state',
|
audit.unset_fields_except(['uuid', 'audit_type', 'state',
|
||||||
'goal_uuid', 'interval', 'scope',
|
'goal_uuid', 'interval', 'scope',
|
||||||
'strategy_uuid', 'goal_name',
|
'strategy_uuid', 'goal_name',
|
||||||
'strategy_name', 'auto_trigger'])
|
'strategy_name'])
|
||||||
|
|
||||||
audit.links = [link.Link.make_link('self', url,
|
audit.links = [link.Link.make_link('self', url,
|
||||||
'audits', audit.uuid),
|
'audits', audit.uuid),
|
||||||
@@ -564,11 +572,14 @@ class AuditsController(rest.RestController):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
audit_dict = audit_to_update.as_dict()
|
audit_dict = audit_to_update.as_dict()
|
||||||
|
audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch))
|
||||||
|
except api_utils.JSONPATCH_EXCEPTIONS as e:
|
||||||
|
raise exception.PatchError(patch=patch, reason=e)
|
||||||
|
|
||||||
initial_state = audit_dict['state']
|
initial_state = audit_dict['state']
|
||||||
new_state = api_utils.get_patch_value(patch, 'state')
|
new_state = api_utils.get_patch_value(patch, 'state')
|
||||||
if not api_utils.check_audit_state_transition(
|
allowed_states = ALLOWED_AUDIT_TRANSITIONS.get(initial_state, [])
|
||||||
patch, initial_state):
|
if new_state is not None and new_state not in allowed_states:
|
||||||
error_message = _("State transition not allowed: "
|
error_message = _("State transition not allowed: "
|
||||||
"(%(initial_state)s -> %(new_state)s)")
|
"(%(initial_state)s -> %(new_state)s)")
|
||||||
raise exception.PatchError(
|
raise exception.PatchError(
|
||||||
@@ -576,10 +587,6 @@ class AuditsController(rest.RestController):
|
|||||||
reason=error_message % dict(
|
reason=error_message % dict(
|
||||||
initial_state=initial_state, new_state=new_state))
|
initial_state=initial_state, new_state=new_state))
|
||||||
|
|
||||||
audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch))
|
|
||||||
except api_utils.JSONPATCH_EXCEPTIONS as e:
|
|
||||||
raise exception.PatchError(patch=patch, reason=e)
|
|
||||||
|
|
||||||
# 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:
|
||||||
|
|||||||
@@ -109,21 +109,6 @@ class AuditTemplatePostType(wtypes.Base):
|
|||||||
common_utils.Draft4Validator(
|
common_utils.Draft4Validator(
|
||||||
default.DefaultScope.DEFAULT_SCHEMA).validate(audit_template.scope)
|
default.DefaultScope.DEFAULT_SCHEMA).validate(audit_template.scope)
|
||||||
|
|
||||||
include_host_aggregates = False
|
|
||||||
exclude_host_aggregates = False
|
|
||||||
for rule in audit_template.scope:
|
|
||||||
if 'host_aggregates' in rule:
|
|
||||||
include_host_aggregates = True
|
|
||||||
elif 'exclude' in rule:
|
|
||||||
for resource in rule['exclude']:
|
|
||||||
if 'host_aggregates' in resource:
|
|
||||||
exclude_host_aggregates = True
|
|
||||||
if include_host_aggregates and exclude_host_aggregates:
|
|
||||||
raise exception.Invalid(
|
|
||||||
message=_(
|
|
||||||
"host_aggregates can't be "
|
|
||||||
"included and excluded together"))
|
|
||||||
|
|
||||||
if audit_template.strategy:
|
if audit_template.strategy:
|
||||||
available_strategies = objects.Strategy.list(
|
available_strategies = objects.Strategy.list(
|
||||||
AuditTemplatePostType._ctx)
|
AuditTemplatePostType._ctx)
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ import wsme
|
|||||||
from wsme import types as wtypes
|
from wsme import types as wtypes
|
||||||
import wsmeext.pecan as wsme_pecan
|
import wsmeext.pecan as wsme_pecan
|
||||||
|
|
||||||
|
from watcher._i18n import _LW
|
||||||
from watcher.api.controllers import base
|
from watcher.api.controllers import base
|
||||||
from watcher.api.controllers import link
|
from watcher.api.controllers import link
|
||||||
from watcher.api.controllers.v1 import collection
|
from watcher.api.controllers.v1 import collection
|
||||||
from watcher.api.controllers.v1 import utils as api_utils
|
from watcher.api.controllers.v1 import utils as api_utils
|
||||||
from watcher.common import context
|
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher.common import policy
|
from watcher.common import policy
|
||||||
from watcher import objects
|
from watcher import objects
|
||||||
@@ -52,13 +52,12 @@ class Service(base.APIBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_status = None
|
_status = None
|
||||||
_context = context.RequestContext(is_admin=True)
|
|
||||||
|
|
||||||
def _get_status(self):
|
def _get_status(self):
|
||||||
return self._status
|
return self._status
|
||||||
|
|
||||||
def _set_status(self, id):
|
def _set_status(self, name):
|
||||||
service = objects.Service.get(pecan.request.context, id)
|
service = objects.Service.get_by_name(pecan.request.context, name)
|
||||||
last_heartbeat = (service.last_seen_up or service.updated_at
|
last_heartbeat = (service.last_seen_up or service.updated_at
|
||||||
or service.created_at)
|
or service.created_at)
|
||||||
if isinstance(last_heartbeat, six.string_types):
|
if isinstance(last_heartbeat, six.string_types):
|
||||||
@@ -73,9 +72,9 @@ class Service(base.APIBase):
|
|||||||
elapsed = timeutils.delta_seconds(last_heartbeat, timeutils.utcnow())
|
elapsed = timeutils.delta_seconds(last_heartbeat, timeutils.utcnow())
|
||||||
is_up = abs(elapsed) <= CONF.service_down_time
|
is_up = abs(elapsed) <= CONF.service_down_time
|
||||||
if not is_up:
|
if not is_up:
|
||||||
LOG.warning('Seems service %(name)s on host %(host)s is down. '
|
LOG.warning(_LW('Seems service %(name)s on host %(host)s is down. '
|
||||||
'Last heartbeat was %(lhb)s.'
|
'Last heartbeat was %(lhb)s.'
|
||||||
'Elapsed time is %(el)s',
|
'Elapsed time is %(el)s'),
|
||||||
{'name': service.name,
|
{'name': service.name,
|
||||||
'host': service.host,
|
'host': service.host,
|
||||||
'lhb': str(last_heartbeat), 'el': str(elapsed)})
|
'lhb': str(last_heartbeat), 'el': str(elapsed)})
|
||||||
@@ -109,7 +108,7 @@ class Service(base.APIBase):
|
|||||||
for field in fields:
|
for field in fields:
|
||||||
self.fields.append(field)
|
self.fields.append(field)
|
||||||
setattr(self, field, kwargs.get(
|
setattr(self, field, kwargs.get(
|
||||||
field if field != 'status' else 'id', wtypes.Unset))
|
field if field != 'status' else 'name', wtypes.Unset))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _convert_with_links(service, url, expand=True):
|
def _convert_with_links(service, url, expand=True):
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ class JsonPatchType(wtypes.Base):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mandatory_attrs():
|
def mandatory_attrs():
|
||||||
"""Returns a list of mandatory attributes.
|
"""Retruns a list of mandatory attributes.
|
||||||
|
|
||||||
Mandatory attributes can't be removed from the document. This
|
Mandatory attributes can't be removed from the document. This
|
||||||
method should be overwritten by derived class.
|
method should be overwritten by derived class.
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ def validate_sort_dir(sort_dir):
|
|||||||
|
|
||||||
|
|
||||||
def validate_search_filters(filters, allowed_fields):
|
def validate_search_filters(filters, allowed_fields):
|
||||||
# Very lightweight validation for now
|
# Very leightweight validation for now
|
||||||
# todo: improve this (e.g. https://www.parse.com/docs/rest/guide/#queries)
|
# todo: improve this (e.g. https://www.parse.com/docs/rest/guide/#queries)
|
||||||
for filter_name in filters.keys():
|
for filter_name in filters.keys():
|
||||||
if filter_name not in allowed_fields:
|
if filter_name not in allowed_fields:
|
||||||
@@ -79,15 +79,6 @@ def get_patch_value(patch, key):
|
|||||||
return p['value']
|
return p['value']
|
||||||
|
|
||||||
|
|
||||||
def check_audit_state_transition(patch, initial):
|
|
||||||
is_transition_valid = True
|
|
||||||
state_value = get_patch_value(patch, "state")
|
|
||||||
if state_value is not None:
|
|
||||||
is_transition_valid = objects.audit.AuditStateTransitionManager(
|
|
||||||
).check_transition(initial, state_value)
|
|
||||||
return is_transition_valid
|
|
||||||
|
|
||||||
|
|
||||||
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():
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from oslo_serialization import jsonutils
|
|||||||
import six
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from watcher._i18n import _
|
from watcher._i18n import _, _LE
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ class ParsableErrorMiddleware(object):
|
|||||||
et.ElementTree.Element(
|
et.ElementTree.Element(
|
||||||
'error_message', text='\n'.join(app_iter)))]
|
'error_message', text='\n'.join(app_iter)))]
|
||||||
except et.ElementTree.ParseError as err:
|
except et.ElementTree.ParseError as err:
|
||||||
LOG.error('Error parsing HTTP response: %s', err)
|
LOG.error(_LE('Error parsing HTTP response: %s'), err)
|
||||||
body = ['<error_message>%s'
|
body = ['<error_message>%s'
|
||||||
'</error_message>' % state['status_code']]
|
'</error_message>' % state['status_code']]
|
||||||
state['headers'].append(('Content-Type', 'application/xml'))
|
state['headers'].append(('Content-Type', 'application/xml'))
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
# Copyright (c) 2017 Servionica
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_log import log
|
|
||||||
from oslo_utils import timeutils
|
|
||||||
import six
|
|
||||||
|
|
||||||
from watcher._i18n import _LW
|
|
||||||
from watcher.common import context as watcher_context
|
|
||||||
from watcher.common import scheduling
|
|
||||||
from watcher import notifications
|
|
||||||
|
|
||||||
from watcher import objects
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class APISchedulingService(scheduling.BackgroundSchedulerService):
|
|
||||||
|
|
||||||
def __init__(self, gconfig=None, **options):
|
|
||||||
self.services_status = {}
|
|
||||||
gconfig = None or {}
|
|
||||||
super(APISchedulingService, self).__init__(gconfig, **options)
|
|
||||||
|
|
||||||
def get_services_status(self, context):
|
|
||||||
services = objects.service.Service.list(context)
|
|
||||||
for service in services:
|
|
||||||
result = self.get_service_status(context, service.id)
|
|
||||||
if service.id not in self.services_status.keys():
|
|
||||||
self.services_status[service.id] = result
|
|
||||||
continue
|
|
||||||
if self.services_status[service.id] != result:
|
|
||||||
self.services_status[service.id] = result
|
|
||||||
notifications.service.send_service_update(context, service,
|
|
||||||
state=result)
|
|
||||||
|
|
||||||
def get_service_status(self, context, service_id):
|
|
||||||
service = objects.Service.get(context, service_id)
|
|
||||||
last_heartbeat = (service.last_seen_up or service.updated_at
|
|
||||||
or service.created_at)
|
|
||||||
if isinstance(last_heartbeat, six.string_types):
|
|
||||||
# NOTE(russellb) If this service came in over rpc via
|
|
||||||
# conductor, then the timestamp will be a string and needs to be
|
|
||||||
# converted back to a datetime.
|
|
||||||
last_heartbeat = timeutils.parse_strtime(last_heartbeat)
|
|
||||||
else:
|
|
||||||
# Objects have proper UTC timezones, but the timeutils comparison
|
|
||||||
# below does not (and will fail)
|
|
||||||
last_heartbeat = last_heartbeat.replace(tzinfo=None)
|
|
||||||
elapsed = timeutils.delta_seconds(last_heartbeat, timeutils.utcnow())
|
|
||||||
is_up = abs(elapsed) <= CONF.service_down_time
|
|
||||||
if not is_up:
|
|
||||||
LOG.warning(_LW('Seems service %(name)s on host %(host)s is down. '
|
|
||||||
'Last heartbeat was %(lhb)s.'
|
|
||||||
'Elapsed time is %(el)s'),
|
|
||||||
{'name': service.name,
|
|
||||||
'host': service.host,
|
|
||||||
'lhb': str(last_heartbeat), 'el': str(elapsed)})
|
|
||||||
return objects.service.ServiceStatus.FAILED
|
|
||||||
|
|
||||||
return objects.service.ServiceStatus.ACTIVE
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
"""Start service."""
|
|
||||||
context = watcher_context.make_context(is_admin=True)
|
|
||||||
self.add_job(self.get_services_status, name='service_status',
|
|
||||||
trigger='interval', jobstore='default', args=[context],
|
|
||||||
next_run_time=datetime.datetime.now(), seconds=60)
|
|
||||||
super(APISchedulingService, self).start()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
"""Stop service."""
|
|
||||||
self.shutdown()
|
|
||||||
|
|
||||||
def wait(self):
|
|
||||||
"""Wait for service to complete."""
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""Reset service.
|
|
||||||
|
|
||||||
Called in case service running in daemon mode receives SIGHUP.
|
|
||||||
"""
|
|
||||||
@@ -20,7 +20,6 @@ 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.common import exception
|
|
||||||
from watcher import notifications
|
from watcher import notifications
|
||||||
from watcher import objects
|
from watcher import objects
|
||||||
from watcher.objects import fields
|
from watcher.objects import fields
|
||||||
@@ -40,9 +39,6 @@ class DefaultActionPlanHandler(base.BaseActionPlanHandler):
|
|||||||
try:
|
try:
|
||||||
action_plan = objects.ActionPlan.get_by_uuid(
|
action_plan = objects.ActionPlan.get_by_uuid(
|
||||||
self.ctx, self.action_plan_uuid, eager=True)
|
self.ctx, self.action_plan_uuid, eager=True)
|
||||||
if action_plan.state == objects.action_plan.State.CANCELLED:
|
|
||||||
self._update_action_from_pending_to_cancelled()
|
|
||||||
return
|
|
||||||
action_plan.state = objects.action_plan.State.ONGOING
|
action_plan.state = objects.action_plan.State.ONGOING
|
||||||
action_plan.save()
|
action_plan.save()
|
||||||
notifications.action_plan.send_action_notification(
|
notifications.action_plan.send_action_notification(
|
||||||
@@ -58,12 +54,6 @@ class DefaultActionPlanHandler(base.BaseActionPlanHandler):
|
|||||||
self.ctx, action_plan,
|
self.ctx, action_plan,
|
||||||
action=fields.NotificationAction.EXECUTION,
|
action=fields.NotificationAction.EXECUTION,
|
||||||
phase=fields.NotificationPhase.END)
|
phase=fields.NotificationPhase.END)
|
||||||
|
|
||||||
except exception.ActionPlanCancelled as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
action_plan.state = objects.action_plan.State.CANCELLED
|
|
||||||
self._update_action_from_pending_to_cancelled()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(e)
|
LOG.exception(e)
|
||||||
action_plan.state = objects.action_plan.State.FAILED
|
action_plan.state = objects.action_plan.State.FAILED
|
||||||
@@ -74,12 +64,3 @@ class DefaultActionPlanHandler(base.BaseActionPlanHandler):
|
|||||||
phase=fields.NotificationPhase.ERROR)
|
phase=fields.NotificationPhase.ERROR)
|
||||||
finally:
|
finally:
|
||||||
action_plan.save()
|
action_plan.save()
|
||||||
|
|
||||||
def _update_action_from_pending_to_cancelled(self):
|
|
||||||
filters = {'action_plan_uuid': self.action_plan_uuid,
|
|
||||||
'state': objects.action.State.PENDING}
|
|
||||||
actions = objects.Action.list(self.ctx, filters=filters, eager=True)
|
|
||||||
if actions:
|
|
||||||
for a in actions:
|
|
||||||
a.state = objects.action.State.CANCELLED
|
|
||||||
a.save()
|
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ class BaseAction(loadable.Loadable):
|
|||||||
# watcher dashboard and will be nested in input_parameters
|
# watcher dashboard and will be nested in input_parameters
|
||||||
RESOURCE_ID = 'resource_id'
|
RESOURCE_ID = 'resource_id'
|
||||||
|
|
||||||
# Add action class name to the list, if implementing abort.
|
|
||||||
ABORT_TRUE = ['Sleep', 'Nop']
|
|
||||||
|
|
||||||
def __init__(self, config, osc=None):
|
def __init__(self, config, osc=None):
|
||||||
"""Constructor
|
"""Constructor
|
||||||
|
|
||||||
@@ -114,7 +111,7 @@ class BaseAction(loadable.Loadable):
|
|||||||
def post_condition(self):
|
def post_condition(self):
|
||||||
"""Hook: called after the execution of an action
|
"""Hook: called after the execution of an action
|
||||||
|
|
||||||
This function is called regardless of whether an action succeeded or
|
This function is called regardless of whether an action succeded or
|
||||||
not. So you can use it to perform cleanup operations.
|
not. So you can use it to perform cleanup operations.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@@ -132,11 +129,3 @@ class BaseAction(loadable.Loadable):
|
|||||||
def validate_parameters(self):
|
def validate_parameters(self):
|
||||||
self.schema(self.input_parameters)
|
self.schema(self.input_parameters)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_description(self):
|
|
||||||
"""Description of the action"""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def check_abort(self):
|
|
||||||
return bool(self.__class__.__name__ in self.ABORT_TRUE)
|
|
||||||
|
|||||||
@@ -101,9 +101,3 @@ class ChangeNovaServiceState(base.BaseAction):
|
|||||||
|
|
||||||
def post_condition(self):
|
def post_condition(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
"""Description of the action"""
|
|
||||||
return ("Disables or enables the nova-compute service."
|
|
||||||
"A disabled nova-compute service can not be selected "
|
|
||||||
"by the nova for future deployment of new server.")
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from oslo_log import log
|
|||||||
import six
|
import six
|
||||||
import voluptuous
|
import voluptuous
|
||||||
|
|
||||||
from watcher._i18n import _
|
from watcher._i18n import _, _LC
|
||||||
from watcher.applier.actions import base
|
from watcher.applier.actions import base
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher.common import nova_helper
|
from watcher.common import nova_helper
|
||||||
@@ -120,9 +120,9 @@ class Migrate(base.BaseAction):
|
|||||||
"migrating instance %s.Exception: %s" %
|
"migrating instance %s.Exception: %s" %
|
||||||
(self.instance_uuid, e))
|
(self.instance_uuid, e))
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.critical("Unexpected error occurred. Migration failed for "
|
LOG.critical(_LC("Unexpected error occurred. Migration failed for "
|
||||||
"instance %s. Leaving instance on previous "
|
"instance %s. Leaving instance on previous "
|
||||||
"host.", self.instance_uuid)
|
"host."), self.instance_uuid)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -134,9 +134,9 @@ class Migrate(base.BaseAction):
|
|||||||
dest_hostname=destination)
|
dest_hostname=destination)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
LOG.critical("Unexpected error occurred. Migration failed for "
|
LOG.critical(_LC("Unexpected error occurred. Migration failed for "
|
||||||
"instance %s. Leaving instance on previous "
|
"instance %s. Leaving instance on previous "
|
||||||
"host.", self.instance_uuid)
|
"host."), self.instance_uuid)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -164,10 +164,6 @@ class Migrate(base.BaseAction):
|
|||||||
def revert(self):
|
def revert(self):
|
||||||
return self.migrate(destination=self.source_node)
|
return self.migrate(destination=self.source_node)
|
||||||
|
|
||||||
def abort(self):
|
|
||||||
# TODO(adisky): implement abort for migration
|
|
||||||
LOG.warning("Abort for migration not implemented")
|
|
||||||
|
|
||||||
def pre_condition(self):
|
def pre_condition(self):
|
||||||
# TODO(jed): check if the instance exists / check if the instance is on
|
# TODO(jed): check if the instance exists / check if the instance is on
|
||||||
# the source_node
|
# the source_node
|
||||||
@@ -176,7 +172,3 @@ class Migrate(base.BaseAction):
|
|||||||
def post_condition(self):
|
def post_condition(self):
|
||||||
# TODO(jed): check extra parameters (network response, etc.)
|
# TODO(jed): check extra parameters (network response, etc.)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
"""Description of the action"""
|
|
||||||
return "Moving a VM instance from source_node to destination_node"
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import voluptuous
|
|||||||
|
|
||||||
from watcher.applier.actions import base
|
from watcher.applier.actions import base
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -64,10 +65,3 @@ class Nop(base.BaseAction):
|
|||||||
|
|
||||||
def post_condition(self):
|
def post_condition(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
"""Description of the action"""
|
|
||||||
return "Logging a NOP message"
|
|
||||||
|
|
||||||
def abort(self):
|
|
||||||
LOG.debug("Abort action NOP")
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from oslo_log import log
|
|||||||
import six
|
import six
|
||||||
import voluptuous
|
import voluptuous
|
||||||
|
|
||||||
from watcher._i18n import _
|
from watcher._i18n import _, _LC
|
||||||
from watcher.applier.actions import base
|
from watcher.applier.actions import base
|
||||||
from watcher.common import nova_helper
|
from watcher.common import nova_helper
|
||||||
from watcher.common import utils
|
from watcher.common import utils
|
||||||
@@ -86,8 +86,8 @@ class Resize(base.BaseAction):
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
LOG.critical(
|
LOG.critical(
|
||||||
"Unexpected error occurred. Resizing failed for "
|
_LC("Unexpected error occurred. Resizing failed for "
|
||||||
"instance %s.", self.instance_uuid)
|
"instance %s."), self.instance_uuid)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
@@ -104,7 +104,3 @@ class Resize(base.BaseAction):
|
|||||||
def post_condition(self):
|
def post_condition(self):
|
||||||
# TODO(jed): check extra parameters (network response, etc.)
|
# TODO(jed): check extra parameters (network response, etc.)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
"""Description of the action"""
|
|
||||||
return "Resize a server with specified flavor."
|
|
||||||
|
|||||||
@@ -66,10 +66,3 @@ class Sleep(base.BaseAction):
|
|||||||
|
|
||||||
def post_condition(self):
|
def post_condition(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
"""Description of the action"""
|
|
||||||
return "Wait for a given interval in seconds."
|
|
||||||
|
|
||||||
def abort(self):
|
|
||||||
LOG.debug("Abort action sleep")
|
|
||||||
|
|||||||
3
watcher/applier/default.py
Executable file → Normal file
3
watcher/applier/default.py
Executable file → Normal file
@@ -58,6 +58,5 @@ class DefaultApplier(base.BaseApplier):
|
|||||||
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, filters=filters,
|
actions = objects.Action.list(self.context, filters=filters)
|
||||||
eager=True)
|
|
||||||
return self.engine.execute(actions)
|
return self.engine.execute(actions)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class ApplierAPI(service.Service):
|
|||||||
if not utils.is_uuid_like(action_plan_uuid):
|
if not utils.is_uuid_like(action_plan_uuid):
|
||||||
raise exception.InvalidUuidOrName(name=action_plan_uuid)
|
raise exception.InvalidUuidOrName(name=action_plan_uuid)
|
||||||
|
|
||||||
self.conductor_client.cast(
|
return self.conductor_client.call(
|
||||||
context, 'launch_action_plan', action_plan_uuid=action_plan_uuid)
|
context, 'launch_action_plan', action_plan_uuid=action_plan_uuid)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,27 +17,13 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
|
||||||
import six
|
import six
|
||||||
import time
|
|
||||||
|
|
||||||
import eventlet
|
|
||||||
|
|
||||||
from oslo_log import log
|
|
||||||
from taskflow import task as flow_task
|
|
||||||
|
|
||||||
from watcher.applier.actions import factory
|
from watcher.applier.actions import factory
|
||||||
from watcher.common import clients
|
from watcher.common import clients
|
||||||
from watcher.common import exception
|
|
||||||
from watcher.common.loader import loadable
|
from watcher.common.loader import loadable
|
||||||
from watcher import notifications
|
|
||||||
from watcher import objects
|
from watcher import objects
|
||||||
from watcher.objects import fields
|
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
CANCEL_STATE = [objects.action_plan.State.CANCELLING,
|
|
||||||
objects.action_plan.State.CANCELLED]
|
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
@@ -86,175 +72,11 @@ class BaseWorkFlowEngine(loadable.Loadable):
|
|||||||
return self._action_factory
|
return self._action_factory
|
||||||
|
|
||||||
def notify(self, action, state):
|
def notify(self, action, state):
|
||||||
db_action = objects.Action.get_by_uuid(self.context, action.uuid,
|
db_action = objects.Action.get_by_uuid(self.context, action.uuid)
|
||||||
eager=True)
|
|
||||||
if (db_action.state in [objects.action.State.CANCELLING,
|
|
||||||
objects.action.State.CANCELLED] and
|
|
||||||
state == objects.action.State.SUCCEEDED):
|
|
||||||
return
|
|
||||||
db_action.state = state
|
db_action.state = state
|
||||||
db_action.save()
|
db_action.save()
|
||||||
|
# NOTE(v-francoise): Implement notifications for action
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def execute(self, actions):
|
def execute(self, actions):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class BaseTaskFlowActionContainer(flow_task.Task):
|
|
||||||
|
|
||||||
def __init__(self, name, db_action, engine, **kwargs):
|
|
||||||
super(BaseTaskFlowActionContainer, self).__init__(name=name)
|
|
||||||
self._db_action = db_action
|
|
||||||
self._engine = engine
|
|
||||||
self.loaded_action = None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def engine(self):
|
|
||||||
return self._engine
|
|
||||||
|
|
||||||
@property
|
|
||||||
def action(self):
|
|
||||||
if self.loaded_action is None:
|
|
||||||
action = self.engine.action_factory.make_action(
|
|
||||||
self._db_action,
|
|
||||||
osc=self._engine.osc)
|
|
||||||
self.loaded_action = action
|
|
||||||
return self.loaded_action
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def do_pre_execute(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def do_execute(self, *args, **kwargs):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def do_post_execute(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def do_revert(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def do_abort(self, *args, **kwargs):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
# NOTE(alexchadin): taskflow does 3 method calls (pre_execute, execute,
|
|
||||||
# post_execute) independently. We want to support notifications in base
|
|
||||||
# class, so child's methods should be named with `do_` prefix and wrapped.
|
|
||||||
def pre_execute(self):
|
|
||||||
try:
|
|
||||||
# NOTE(adisky): check the state of action plan before starting
|
|
||||||
# next action, if action plan is cancelled raise the exceptions
|
|
||||||
# so that taskflow does not schedule further actions.
|
|
||||||
action_plan = objects.ActionPlan.get_by_id(
|
|
||||||
self.engine.context, self._db_action.action_plan_id)
|
|
||||||
if action_plan.state in CANCEL_STATE:
|
|
||||||
raise exception.ActionPlanCancelled(uuid=action_plan.uuid)
|
|
||||||
self.do_pre_execute()
|
|
||||||
notifications.action.send_execution_notification(
|
|
||||||
self.engine.context, self._db_action,
|
|
||||||
fields.NotificationAction.EXECUTION,
|
|
||||||
fields.NotificationPhase.START)
|
|
||||||
except exception.ActionPlanCancelled as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
self.engine.notify(self._db_action, objects.action.State.FAILED)
|
|
||||||
notifications.action.send_execution_notification(
|
|
||||||
self.engine.context, self._db_action,
|
|
||||||
fields.NotificationAction.EXECUTION,
|
|
||||||
fields.NotificationPhase.ERROR,
|
|
||||||
priority=fields.NotificationPriority.ERROR)
|
|
||||||
|
|
||||||
def execute(self, *args, **kwargs):
|
|
||||||
def _do_execute_action(*args, **kwargs):
|
|
||||||
try:
|
|
||||||
self.do_execute(*args, **kwargs)
|
|
||||||
notifications.action.send_execution_notification(
|
|
||||||
self.engine.context, self._db_action,
|
|
||||||
fields.NotificationAction.EXECUTION,
|
|
||||||
fields.NotificationPhase.END)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
LOG.error('The workflow engine has failed'
|
|
||||||
'to execute the action: %s', self.name)
|
|
||||||
self.engine.notify(self._db_action,
|
|
||||||
objects.action.State.FAILED)
|
|
||||||
notifications.action.send_execution_notification(
|
|
||||||
self.engine.context, self._db_action,
|
|
||||||
fields.NotificationAction.EXECUTION,
|
|
||||||
fields.NotificationPhase.ERROR,
|
|
||||||
priority=fields.NotificationPriority.ERROR)
|
|
||||||
raise
|
|
||||||
# NOTE: spawn a new thread for action execution, so that if action plan
|
|
||||||
# is cancelled workflow engine will not wait to finish action execution
|
|
||||||
et = eventlet.spawn(_do_execute_action, *args, **kwargs)
|
|
||||||
# NOTE: check for the state of action plan periodically,so that if
|
|
||||||
# action is finished or action plan is cancelled we can exit from here.
|
|
||||||
while True:
|
|
||||||
action_object = objects.Action.get_by_uuid(
|
|
||||||
self.engine.context, self._db_action.uuid, eager=True)
|
|
||||||
action_plan_object = objects.ActionPlan.get_by_id(
|
|
||||||
self.engine.context, action_object.action_plan_id)
|
|
||||||
if (action_object.state in [objects.action.State.SUCCEEDED,
|
|
||||||
objects.action.State.FAILED] or
|
|
||||||
action_plan_object.state in CANCEL_STATE):
|
|
||||||
break
|
|
||||||
time.sleep(2)
|
|
||||||
try:
|
|
||||||
# NOTE: kill the action execution thread, if action plan is
|
|
||||||
# cancelled for all other cases wait for the result from action
|
|
||||||
# execution thread.
|
|
||||||
# Not all actions support abort operations, kill only those action
|
|
||||||
# which support abort operations
|
|
||||||
abort = self.action.check_abort()
|
|
||||||
if (action_plan_object.state in CANCEL_STATE and abort):
|
|
||||||
et.kill()
|
|
||||||
et.wait()
|
|
||||||
|
|
||||||
# NOTE: catch the greenlet exit exception due to thread kill,
|
|
||||||
# taskflow will call revert for the action,
|
|
||||||
# we will redirect it to abort.
|
|
||||||
except eventlet.greenlet.GreenletExit:
|
|
||||||
raise exception.ActionPlanCancelled(uuid=action_plan_object.uuid)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def post_execute(self):
|
|
||||||
try:
|
|
||||||
self.do_post_execute()
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
self.engine.notify(self._db_action, objects.action.State.FAILED)
|
|
||||||
notifications.action.send_execution_notification(
|
|
||||||
self.engine.context, self._db_action,
|
|
||||||
fields.NotificationAction.EXECUTION,
|
|
||||||
fields.NotificationPhase.ERROR,
|
|
||||||
priority=fields.NotificationPriority.ERROR)
|
|
||||||
|
|
||||||
def revert(self, *args, **kwargs):
|
|
||||||
action_plan = objects.ActionPlan.get_by_id(
|
|
||||||
self.engine.context, self._db_action.action_plan_id, eager=True)
|
|
||||||
# NOTE: check if revert cause by cancel action plan or
|
|
||||||
# some other exception occured during action plan execution
|
|
||||||
# if due to some other exception keep the flow intact.
|
|
||||||
if action_plan.state not in CANCEL_STATE:
|
|
||||||
self.do_revert()
|
|
||||||
action_object = objects.Action.get_by_uuid(
|
|
||||||
self.engine.context, self._db_action.uuid, eager=True)
|
|
||||||
if action_object.state == objects.action.State.ONGOING:
|
|
||||||
action_object.state = objects.action.State.CANCELLING
|
|
||||||
action_object.save()
|
|
||||||
self.abort()
|
|
||||||
if action_object.state == objects.action.State.PENDING:
|
|
||||||
action_object.state = objects.action.State.CANCELLED
|
|
||||||
action_object.save()
|
|
||||||
|
|
||||||
def abort(self, *args, **kwargs):
|
|
||||||
self.do_abort(*args, **kwargs)
|
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ from oslo_concurrency import processutils
|
|||||||
from oslo_config import cfg
|
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 import exceptions as tf_exception
|
|
||||||
from taskflow.patterns import graph_flow as gf
|
from taskflow.patterns import graph_flow as gf
|
||||||
from taskflow import task as flow_task
|
from taskflow import task as flow_task
|
||||||
|
|
||||||
|
from watcher._i18n import _LE, _LW, _LC
|
||||||
from watcher.applier.workflow_engine import base
|
from watcher.applier.workflow_engine import base
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher import objects
|
from watcher import objects
|
||||||
@@ -91,63 +91,73 @@ class DefaultWorkFlowEngine(base.BaseWorkFlowEngine):
|
|||||||
|
|
||||||
return flow
|
return flow
|
||||||
|
|
||||||
except exception.ActionPlanCancelled as e:
|
|
||||||
raise
|
|
||||||
|
|
||||||
except tf_exception.WrappedFailure as e:
|
|
||||||
if e.check("watcher.common.exception.ActionPlanCancelled"):
|
|
||||||
raise exception.ActionPlanCancelled
|
|
||||||
else:
|
|
||||||
raise exception.WorkflowExecutionException(error=e)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise exception.WorkflowExecutionException(error=e)
|
raise exception.WorkflowExecutionException(error=e)
|
||||||
|
|
||||||
|
|
||||||
class TaskFlowActionContainer(base.BaseTaskFlowActionContainer):
|
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)
|
||||||
super(TaskFlowActionContainer, self).__init__(name, db_action, engine)
|
super(TaskFlowActionContainer, self).__init__(name=name)
|
||||||
|
self._db_action = db_action
|
||||||
|
self._engine = engine
|
||||||
|
self.loaded_action = None
|
||||||
|
|
||||||
def do_pre_execute(self):
|
@property
|
||||||
|
def action(self):
|
||||||
|
if self.loaded_action is None:
|
||||||
|
action = self.engine.action_factory.make_action(
|
||||||
|
self._db_action,
|
||||||
|
osc=self._engine.osc)
|
||||||
|
self.loaded_action = action
|
||||||
|
return self.loaded_action
|
||||||
|
|
||||||
|
@property
|
||||||
|
def engine(self):
|
||||||
|
return self._engine
|
||||||
|
|
||||||
|
def pre_execute(self):
|
||||||
|
try:
|
||||||
self.engine.notify(self._db_action, objects.action.State.ONGOING)
|
self.engine.notify(self._db_action, objects.action.State.ONGOING)
|
||||||
LOG.debug("Pre-condition action: %s", self.name)
|
LOG.debug("Pre-condition action: %s", self.name)
|
||||||
self.action.pre_condition()
|
self.action.pre_condition()
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception(e)
|
||||||
|
self.engine.notify(self._db_action, objects.action.State.FAILED)
|
||||||
|
raise
|
||||||
|
|
||||||
def do_execute(self, *args, **kwargs):
|
def execute(self, *args, **kwargs):
|
||||||
|
try:
|
||||||
LOG.debug("Running action: %s", self.name)
|
LOG.debug("Running action: %s", self.name)
|
||||||
|
|
||||||
# NOTE: For result is False, set action state fail
|
self.action.execute()
|
||||||
result = self.action.execute()
|
self.engine.notify(self._db_action, objects.action.State.SUCCEEDED)
|
||||||
if result is False:
|
except Exception as e:
|
||||||
self.engine.notify(self._db_action,
|
LOG.exception(e)
|
||||||
objects.action.State.FAILED)
|
LOG.error(_LE('The workflow engine has failed '
|
||||||
else:
|
'to execute the action: %s'), self.name)
|
||||||
self.engine.notify(self._db_action,
|
|
||||||
objects.action.State.SUCCEEDED)
|
|
||||||
|
|
||||||
def do_post_execute(self):
|
self.engine.notify(self._db_action, objects.action.State.FAILED)
|
||||||
|
raise
|
||||||
|
|
||||||
|
def post_execute(self):
|
||||||
|
try:
|
||||||
LOG.debug("Post-condition action: %s", self.name)
|
LOG.debug("Post-condition action: %s", self.name)
|
||||||
self.action.post_condition()
|
self.action.post_condition()
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception(e)
|
||||||
|
self.engine.notify(self._db_action, objects.action.State.FAILED)
|
||||||
|
raise
|
||||||
|
|
||||||
def do_revert(self, *args, **kwargs):
|
def revert(self, *args, **kwargs):
|
||||||
LOG.warning("Revert action: %s", self.name)
|
LOG.warning(_LW("Revert action: %s"), self.name)
|
||||||
try:
|
try:
|
||||||
# TODO(jed): do we need to update the states in case of failure?
|
# TODO(jed): do we need to update the states in case of failure?
|
||||||
self.action.revert()
|
self.action.revert()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(e)
|
LOG.exception(e)
|
||||||
LOG.critical("Oops! We need a disaster recover plan.")
|
LOG.critical(_LC("Oops! We need a disaster recover plan."))
|
||||||
|
|
||||||
def do_abort(self, *args, **kwargs):
|
|
||||||
LOG.warning("Aborting action: %s", self.name)
|
|
||||||
try:
|
|
||||||
self.action.abort()
|
|
||||||
self.engine.notify(self._db_action, objects.action.State.CANCELLED)
|
|
||||||
except Exception as e:
|
|
||||||
self.engine.notify(self._db_action, objects.action.State.FAILED)
|
|
||||||
LOG.exception(e)
|
|
||||||
|
|
||||||
|
|
||||||
class TaskFlowNop(flow_task.Task):
|
class TaskFlowNop(flow_task.Task):
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import sys
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from watcher.api import scheduling
|
from watcher._i18n import _LI
|
||||||
from watcher.common import service
|
from watcher.common import service
|
||||||
from watcher import conf
|
from watcher import conf
|
||||||
|
|
||||||
@@ -39,15 +39,12 @@ def main():
|
|||||||
server = service.WSGIService('watcher-api', CONF.api.enable_ssl_api)
|
server = service.WSGIService('watcher-api', CONF.api.enable_ssl_api)
|
||||||
|
|
||||||
if host == '127.0.0.1':
|
if host == '127.0.0.1':
|
||||||
LOG.info('serving on 127.0.0.1:%(port)s, '
|
LOG.info(_LI('serving on 127.0.0.1:%(port)s, '
|
||||||
'view at %(protocol)s://127.0.0.1:%(port)s' %
|
'view at %(protocol)s://127.0.0.1:%(port)s') %
|
||||||
dict(protocol=protocol, port=port))
|
dict(protocol=protocol, port=port))
|
||||||
else:
|
else:
|
||||||
LOG.info('serving on %(protocol)s://%(host)s:%(port)s' %
|
LOG.info(_LI('serving on %(protocol)s://%(host)s:%(port)s') %
|
||||||
dict(protocol=protocol, host=host, port=port))
|
dict(protocol=protocol, host=host, port=port))
|
||||||
|
|
||||||
api_schedule = scheduling.APISchedulingService()
|
|
||||||
api_schedule.start()
|
|
||||||
|
|
||||||
launcher = service.launch(CONF, server, workers=server.workers)
|
launcher = service.launch(CONF, server, workers=server.workers)
|
||||||
launcher.wait()
|
launcher.wait()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import sys
|
|||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from watcher._i18n import _LI
|
||||||
from watcher.applier import manager
|
from watcher.applier import manager
|
||||||
from watcher.common import service as watcher_service
|
from watcher.common import service as watcher_service
|
||||||
from watcher import conf
|
from watcher import conf
|
||||||
@@ -33,7 +34,7 @@ CONF = conf.CONF
|
|||||||
def main():
|
def main():
|
||||||
watcher_service.prepare_service(sys.argv, CONF)
|
watcher_service.prepare_service(sys.argv, CONF)
|
||||||
|
|
||||||
LOG.info('Starting Watcher Applier service in PID %s', os.getpid())
|
LOG.info(_LI('Starting Watcher Applier service in PID %s'), os.getpid())
|
||||||
|
|
||||||
applier_service = watcher_service.Service(manager.ApplierManager)
|
applier_service = watcher_service.Service(manager.ApplierManager)
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import sys
|
|||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from watcher._i18n import _LI
|
||||||
from watcher.common import service as watcher_service
|
from watcher.common import service as watcher_service
|
||||||
from watcher import conf
|
from watcher import conf
|
||||||
from watcher.decision_engine import gmr
|
from watcher.decision_engine import gmr
|
||||||
@@ -37,17 +38,17 @@ def main():
|
|||||||
watcher_service.prepare_service(sys.argv, CONF)
|
watcher_service.prepare_service(sys.argv, CONF)
|
||||||
gmr.register_gmr_plugins()
|
gmr.register_gmr_plugins()
|
||||||
|
|
||||||
LOG.info('Starting Watcher Decision Engine service in PID %s',
|
LOG.info(_LI('Starting Watcher Decision Engine service in PID %s'),
|
||||||
os.getpid())
|
os.getpid())
|
||||||
|
|
||||||
syncer = sync.Syncer()
|
syncer = sync.Syncer()
|
||||||
syncer.sync()
|
syncer.sync()
|
||||||
|
|
||||||
de_service = watcher_service.Service(manager.DecisionEngineManager)
|
de_service = watcher_service.Service(manager.DecisionEngineManager)
|
||||||
bg_scheduler_service = scheduling.DecisionEngineSchedulingService()
|
bg_schedulder_service = scheduling.DecisionEngineSchedulingService()
|
||||||
|
|
||||||
# Only 1 process
|
# Only 1 process
|
||||||
launcher = watcher_service.launch(CONF, de_service)
|
launcher = watcher_service.launch(CONF, de_service)
|
||||||
launcher.launch_service(bg_scheduler_service)
|
launcher.launch_service(bg_schedulder_service)
|
||||||
|
|
||||||
launcher.wait()
|
launcher.wait()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import sys
|
|||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from watcher._i18n import _LI
|
||||||
from watcher.common import service as service
|
from watcher.common import service as service
|
||||||
from watcher import conf
|
from watcher import conf
|
||||||
from watcher.decision_engine import sync
|
from watcher.decision_engine import sync
|
||||||
@@ -31,10 +32,10 @@ CONF = conf.CONF
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
LOG.info('Watcher sync started.')
|
LOG.info(_LI('Watcher sync started.'))
|
||||||
|
|
||||||
service.prepare_service(sys.argv, CONF)
|
service.prepare_service(sys.argv, CONF)
|
||||||
syncer = sync.Syncer()
|
syncer = sync.Syncer()
|
||||||
syncer.sync()
|
syncer.sync()
|
||||||
|
|
||||||
LOG.info('Watcher sync finished.')
|
LOG.info(_LI('Watcher sync finished.'))
|
||||||
|
|||||||
53
watcher/common/clients.py
Executable file → Normal file
53
watcher/common/clients.py
Executable file → Normal file
@@ -13,8 +13,6 @@
|
|||||||
from ceilometerclient import client as ceclient
|
from ceilometerclient import client as ceclient
|
||||||
from cinderclient import client as ciclient
|
from cinderclient import client as ciclient
|
||||||
from glanceclient import client as glclient
|
from glanceclient import client as glclient
|
||||||
from gnocchiclient import client as gnclient
|
|
||||||
from ironicclient import client as irclient
|
|
||||||
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 monascaclient import client as monclient
|
||||||
@@ -41,12 +39,10 @@ class OpenStackClients(object):
|
|||||||
self._keystone = None
|
self._keystone = None
|
||||||
self._nova = None
|
self._nova = None
|
||||||
self._glance = None
|
self._glance = None
|
||||||
self._gnocchi = None
|
|
||||||
self._cinder = None
|
self._cinder = None
|
||||||
self._ceilometer = None
|
self._ceilometer = None
|
||||||
self._monasca = None
|
self._monasca = None
|
||||||
self._neutron = None
|
self._neutron = None
|
||||||
self._ironic = None
|
|
||||||
|
|
||||||
def _get_keystone_session(self):
|
def _get_keystone_session(self):
|
||||||
auth = ka_loading.load_auth_from_conf_options(CONF,
|
auth = ka_loading.load_auth_from_conf_options(CONF,
|
||||||
@@ -82,9 +78,7 @@ class OpenStackClients(object):
|
|||||||
return self._nova
|
return self._nova
|
||||||
|
|
||||||
novaclient_version = self._get_client_option('nova', 'api_version')
|
novaclient_version = self._get_client_option('nova', 'api_version')
|
||||||
nova_endpoint_type = self._get_client_option('nova', 'endpoint_type')
|
|
||||||
self._nova = nvclient.Client(novaclient_version,
|
self._nova = nvclient.Client(novaclient_version,
|
||||||
endpoint_type=nova_endpoint_type,
|
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._nova
|
return self._nova
|
||||||
|
|
||||||
@@ -94,37 +88,17 @@ class OpenStackClients(object):
|
|||||||
return self._glance
|
return self._glance
|
||||||
|
|
||||||
glanceclient_version = self._get_client_option('glance', 'api_version')
|
glanceclient_version = self._get_client_option('glance', 'api_version')
|
||||||
glance_endpoint_type = self._get_client_option('glance',
|
|
||||||
'endpoint_type')
|
|
||||||
self._glance = glclient.Client(glanceclient_version,
|
self._glance = glclient.Client(glanceclient_version,
|
||||||
interface=glance_endpoint_type,
|
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._glance
|
return self._glance
|
||||||
|
|
||||||
@exception.wrap_keystone_exception
|
|
||||||
def gnocchi(self):
|
|
||||||
if self._gnocchi:
|
|
||||||
return self._gnocchi
|
|
||||||
|
|
||||||
gnocchiclient_version = self._get_client_option('gnocchi',
|
|
||||||
'api_version')
|
|
||||||
gnocchiclient_interface = self._get_client_option('gnocchi',
|
|
||||||
'endpoint_type')
|
|
||||||
self._gnocchi = gnclient.Client(gnocchiclient_version,
|
|
||||||
interface=gnocchiclient_interface,
|
|
||||||
session=self.session)
|
|
||||||
return self._gnocchi
|
|
||||||
|
|
||||||
@exception.wrap_keystone_exception
|
@exception.wrap_keystone_exception
|
||||||
def cinder(self):
|
def cinder(self):
|
||||||
if self._cinder:
|
if self._cinder:
|
||||||
return self._cinder
|
return self._cinder
|
||||||
|
|
||||||
cinderclient_version = self._get_client_option('cinder', 'api_version')
|
cinderclient_version = self._get_client_option('cinder', 'api_version')
|
||||||
cinder_endpoint_type = self._get_client_option('cinder',
|
|
||||||
'endpoint_type')
|
|
||||||
self._cinder = ciclient.Client(cinderclient_version,
|
self._cinder = ciclient.Client(cinderclient_version,
|
||||||
endpoint_type=cinder_endpoint_type,
|
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._cinder
|
return self._cinder
|
||||||
|
|
||||||
@@ -135,11 +109,7 @@ class OpenStackClients(object):
|
|||||||
|
|
||||||
ceilometerclient_version = self._get_client_option('ceilometer',
|
ceilometerclient_version = self._get_client_option('ceilometer',
|
||||||
'api_version')
|
'api_version')
|
||||||
ceilometer_endpoint_type = self._get_client_option('ceilometer',
|
self._ceilometer = ceclient.get_client(ceilometerclient_version,
|
||||||
'endpoint_type')
|
|
||||||
self._ceilometer = ceclient.get_client(
|
|
||||||
ceilometerclient_version,
|
|
||||||
endpoint_type=ceilometer_endpoint_type,
|
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._ceilometer
|
return self._ceilometer
|
||||||
|
|
||||||
@@ -150,8 +120,6 @@ class OpenStackClients(object):
|
|||||||
|
|
||||||
monascaclient_version = self._get_client_option(
|
monascaclient_version = self._get_client_option(
|
||||||
'monasca', 'api_version')
|
'monasca', 'api_version')
|
||||||
monascaclient_interface = self._get_client_option(
|
|
||||||
'monasca', 'interface')
|
|
||||||
token = self.session.get_token()
|
token = self.session.get_token()
|
||||||
watcher_clients_auth_config = CONF.get(_CLIENTS_AUTH_GROUP)
|
watcher_clients_auth_config = CONF.get(_CLIENTS_AUTH_GROUP)
|
||||||
service_type = 'monitoring'
|
service_type = 'monitoring'
|
||||||
@@ -167,8 +135,7 @@ class OpenStackClients(object):
|
|||||||
'username': watcher_clients_auth_config.username,
|
'username': watcher_clients_auth_config.username,
|
||||||
'password': watcher_clients_auth_config.password,
|
'password': watcher_clients_auth_config.password,
|
||||||
}
|
}
|
||||||
endpoint = self.session.get_endpoint(service_type=service_type,
|
endpoint = self.session.get_endpoint(service_type=service_type)
|
||||||
interface=monascaclient_interface)
|
|
||||||
|
|
||||||
self._monasca = monclient.Client(
|
self._monasca = monclient.Client(
|
||||||
monascaclient_version, endpoint, **monasca_kwargs)
|
monascaclient_version, endpoint, **monasca_kwargs)
|
||||||
@@ -182,23 +149,7 @@ class OpenStackClients(object):
|
|||||||
|
|
||||||
neutronclient_version = self._get_client_option('neutron',
|
neutronclient_version = self._get_client_option('neutron',
|
||||||
'api_version')
|
'api_version')
|
||||||
neutron_endpoint_type = self._get_client_option('neutron',
|
|
||||||
'endpoint_type')
|
|
||||||
|
|
||||||
self._neutron = netclient.Client(neutronclient_version,
|
self._neutron = netclient.Client(neutronclient_version,
|
||||||
endpoint_type=neutron_endpoint_type,
|
|
||||||
session=self.session)
|
session=self.session)
|
||||||
self._neutron.format = 'json'
|
self._neutron.format = 'json'
|
||||||
return self._neutron
|
return self._neutron
|
||||||
|
|
||||||
@exception.wrap_keystone_exception
|
|
||||||
def ironic(self):
|
|
||||||
if self._ironic:
|
|
||||||
return self._ironic
|
|
||||||
|
|
||||||
ironicclient_version = self._get_client_option('ironic', 'api_version')
|
|
||||||
endpoint_type = self._get_client_option('ironic', 'endpoint_type')
|
|
||||||
self._ironic = irclient.get_client(ironicclient_version,
|
|
||||||
ironic_url=endpoint_type,
|
|
||||||
session=self.session)
|
|
||||||
return self._ironic
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from oslo_log import log as logging
|
|||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from watcher._i18n import _LW
|
||||||
from watcher.common import utils
|
from watcher.common import utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@@ -64,7 +65,7 @@ class RequestContext(context.RequestContext):
|
|||||||
# safely ignore this as we don't use it.
|
# safely ignore this as we don't use it.
|
||||||
kwargs.pop('user_identity', None)
|
kwargs.pop('user_identity', None)
|
||||||
if kwargs:
|
if kwargs:
|
||||||
LOG.warning('Arguments dropped when creating context: %s',
|
LOG.warning(_LW('Arguments dropped when creating context: %s'),
|
||||||
str(kwargs))
|
str(kwargs))
|
||||||
|
|
||||||
# FIXME(dims): user_id and project_id duplicate information that is
|
# FIXME(dims): user_id and project_id duplicate information that is
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ from keystoneclient import exceptions as keystone_exceptions
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from watcher._i18n import _
|
from watcher._i18n import _, _LE
|
||||||
|
|
||||||
from watcher import conf
|
from watcher import conf
|
||||||
|
|
||||||
@@ -83,9 +83,9 @@ class WatcherException(Exception):
|
|||||||
except Exception:
|
except Exception:
|
||||||
# kwargs doesn't match a variable in msg_fmt
|
# kwargs doesn't match a variable in msg_fmt
|
||||||
# log the issue and the kwargs
|
# log the issue and the kwargs
|
||||||
LOG.exception('Exception in string format operation')
|
LOG.exception(_LE('Exception in string format operation'))
|
||||||
for name, value in kwargs.items():
|
for name, value in kwargs.items():
|
||||||
LOG.error("%(name)s: %(value)s",
|
LOG.error(_LE("%(name)s: %(value)s"),
|
||||||
{'name': name, 'value': value})
|
{'name': name, 'value': value})
|
||||||
|
|
||||||
if CONF.fatal_exception_format_errors:
|
if CONF.fatal_exception_format_errors:
|
||||||
@@ -130,7 +130,7 @@ class OperationNotPermitted(NotAuthorized):
|
|||||||
msg_fmt = _("Operation not permitted")
|
msg_fmt = _("Operation not permitted")
|
||||||
|
|
||||||
|
|
||||||
class Invalid(WatcherException, ValueError):
|
class Invalid(WatcherException):
|
||||||
msg_fmt = _("Unacceptable parameters")
|
msg_fmt = _("Unacceptable parameters")
|
||||||
code = 400
|
code = 400
|
||||||
|
|
||||||
@@ -149,10 +149,6 @@ class ResourceNotFound(ObjectNotFound):
|
|||||||
code = 404
|
code = 404
|
||||||
|
|
||||||
|
|
||||||
class InvalidParameter(Invalid):
|
|
||||||
msg_fmt = _("%(parameter)s has to be of type %(parameter_type)s")
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidIdentity(Invalid):
|
class InvalidIdentity(Invalid):
|
||||||
msg_fmt = _("Expected a uuid or int but received %(identity)s")
|
msg_fmt = _("Expected a uuid or int but received %(identity)s")
|
||||||
|
|
||||||
@@ -186,10 +182,6 @@ class EagerlyLoadedActionPlanRequired(InvalidActionPlan):
|
|||||||
msg_fmt = _("Action plan %(action_plan)s was not eagerly loaded")
|
msg_fmt = _("Action plan %(action_plan)s was not eagerly loaded")
|
||||||
|
|
||||||
|
|
||||||
class EagerlyLoadedActionRequired(InvalidActionPlan):
|
|
||||||
msg_fmt = _("Action %(action)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")
|
||||||
|
|
||||||
@@ -274,12 +266,10 @@ class ActionPlanReferenced(Invalid):
|
|||||||
"multiple actions")
|
"multiple actions")
|
||||||
|
|
||||||
|
|
||||||
class ActionPlanCancelled(WatcherException):
|
|
||||||
msg_fmt = _("Action Plan with UUID %(uuid)s is cancelled by user")
|
|
||||||
|
|
||||||
|
|
||||||
class ActionPlanIsOngoing(Conflict):
|
class ActionPlanIsOngoing(Conflict):
|
||||||
msg_fmt = _("Action Plan %(action_plan)s is currently running.")
|
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):
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
import oslo_messaging as messaging
|
import oslo_messaging as messaging
|
||||||
|
|
||||||
from oslo_messaging.rpc import dispatcher
|
from watcher._i18n import _LE
|
||||||
|
|
||||||
from watcher.common import context as watcher_context
|
from watcher.common import context as watcher_context
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
|
|
||||||
@@ -33,6 +32,7 @@ __all__ = [
|
|||||||
'get_client',
|
'get_client',
|
||||||
'get_server',
|
'get_server',
|
||||||
'get_notifier',
|
'get_notifier',
|
||||||
|
'TRANSPORT_ALIASES',
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@@ -46,6 +46,16 @@ ALLOWED_EXMODS = [
|
|||||||
]
|
]
|
||||||
EXTRA_EXMODS = []
|
EXTRA_EXMODS = []
|
||||||
|
|
||||||
|
# NOTE(lucasagomes): The watcher.openstack.common.rpc entries are for
|
||||||
|
# backwards compat with IceHouse rpc_backend configuration values.
|
||||||
|
TRANSPORT_ALIASES = {
|
||||||
|
'watcher.openstack.common.rpc.impl_kombu': 'rabbit',
|
||||||
|
'watcher.openstack.common.rpc.impl_qpid': 'qpid',
|
||||||
|
'watcher.openstack.common.rpc.impl_zmq': 'zmq',
|
||||||
|
'watcher.rpc.impl_kombu': 'rabbit',
|
||||||
|
'watcher.rpc.impl_qpid': 'qpid',
|
||||||
|
'watcher.rpc.impl_zmq': 'zmq',
|
||||||
|
}
|
||||||
|
|
||||||
JsonPayloadSerializer = messaging.JsonPayloadSerializer
|
JsonPayloadSerializer = messaging.JsonPayloadSerializer
|
||||||
|
|
||||||
@@ -54,10 +64,12 @@ def init(conf):
|
|||||||
global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER
|
global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER
|
||||||
exmods = get_allowed_exmods()
|
exmods = get_allowed_exmods()
|
||||||
TRANSPORT = messaging.get_transport(conf,
|
TRANSPORT = messaging.get_transport(conf,
|
||||||
allowed_remote_exmods=exmods)
|
allowed_remote_exmods=exmods,
|
||||||
|
aliases=TRANSPORT_ALIASES)
|
||||||
NOTIFICATION_TRANSPORT = messaging.get_notification_transport(
|
NOTIFICATION_TRANSPORT = messaging.get_notification_transport(
|
||||||
conf,
|
conf,
|
||||||
allowed_remote_exmods=exmods)
|
allowed_remote_exmods=exmods,
|
||||||
|
aliases=TRANSPORT_ALIASES)
|
||||||
|
|
||||||
serializer = RequestContextSerializer(JsonPayloadSerializer())
|
serializer = RequestContextSerializer(JsonPayloadSerializer())
|
||||||
if not conf.notification_level:
|
if not conf.notification_level:
|
||||||
@@ -75,7 +87,7 @@ def initialized():
|
|||||||
def cleanup():
|
def cleanup():
|
||||||
global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER
|
global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER
|
||||||
if NOTIFIER is None:
|
if NOTIFIER is None:
|
||||||
LOG.exception("RPC cleanup: NOTIFIER is None")
|
LOG.exception(_LE("RPC cleanup: NOTIFIER is None"))
|
||||||
TRANSPORT.cleanup()
|
TRANSPORT.cleanup()
|
||||||
NOTIFICATION_TRANSPORT.cleanup()
|
NOTIFICATION_TRANSPORT.cleanup()
|
||||||
TRANSPORT = NOTIFICATION_TRANSPORT = NOTIFIER = None
|
TRANSPORT = NOTIFICATION_TRANSPORT = NOTIFIER = None
|
||||||
@@ -130,14 +142,12 @@ def get_client(target, version_cap=None, serializer=None):
|
|||||||
|
|
||||||
def get_server(target, endpoints, serializer=None):
|
def get_server(target, endpoints, serializer=None):
|
||||||
assert TRANSPORT is not None
|
assert TRANSPORT is not None
|
||||||
access_policy = dispatcher.DefaultRPCAccessPolicy
|
|
||||||
serializer = RequestContextSerializer(serializer)
|
serializer = RequestContextSerializer(serializer)
|
||||||
return messaging.get_rpc_server(TRANSPORT,
|
return messaging.get_rpc_server(TRANSPORT,
|
||||||
target,
|
target,
|
||||||
endpoints,
|
endpoints,
|
||||||
executor='eventlet',
|
executor='eventlet',
|
||||||
serializer=serializer,
|
serializer=serializer)
|
||||||
access_policy=access_policy)
|
|
||||||
|
|
||||||
|
|
||||||
def get_notifier(publisher_id):
|
def get_notifier(publisher_id):
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ from oslo_reports import opts as gmr_opts
|
|||||||
from oslo_service import service
|
from oslo_service import service
|
||||||
from oslo_service import wsgi
|
from oslo_service import wsgi
|
||||||
|
|
||||||
from oslo_messaging.rpc import dispatcher
|
|
||||||
|
|
||||||
from watcher._i18n import _
|
from watcher._i18n import _
|
||||||
from watcher.api import app
|
from watcher.api import app
|
||||||
from watcher.common import config
|
from watcher.common import config
|
||||||
@@ -112,19 +110,16 @@ class WSGIService(service.ServiceBase):
|
|||||||
|
|
||||||
class ServiceHeartbeat(scheduling.BackgroundSchedulerService):
|
class ServiceHeartbeat(scheduling.BackgroundSchedulerService):
|
||||||
|
|
||||||
service_name = None
|
|
||||||
|
|
||||||
def __init__(self, gconfig=None, service_name=None, **kwargs):
|
def __init__(self, gconfig=None, service_name=None, **kwargs):
|
||||||
gconfig = None or {}
|
gconfig = None or {}
|
||||||
super(ServiceHeartbeat, self).__init__(gconfig, **kwargs)
|
super(ServiceHeartbeat, self).__init__(gconfig, **kwargs)
|
||||||
ServiceHeartbeat.service_name = service_name
|
self.service_name = service_name
|
||||||
self.context = context.make_context()
|
self.context = context.make_context()
|
||||||
self.send_beat()
|
|
||||||
|
|
||||||
def send_beat(self):
|
def send_beat(self):
|
||||||
host = CONF.host
|
host = CONF.host
|
||||||
watcher_list = objects.Service.list(
|
watcher_list = objects.Service.list(
|
||||||
self.context, filters={'name': ServiceHeartbeat.service_name,
|
self.context, filters={'name': self.service_name,
|
||||||
'host': host})
|
'host': host})
|
||||||
if watcher_list:
|
if watcher_list:
|
||||||
watcher_service = watcher_list[0]
|
watcher_service = watcher_list[0]
|
||||||
@@ -132,7 +127,7 @@ class ServiceHeartbeat(scheduling.BackgroundSchedulerService):
|
|||||||
watcher_service.save()
|
watcher_service.save()
|
||||||
else:
|
else:
|
||||||
watcher_service = objects.Service(self.context)
|
watcher_service = objects.Service(self.context)
|
||||||
watcher_service.name = ServiceHeartbeat.service_name
|
watcher_service.name = self.service_name
|
||||||
watcher_service.host = host
|
watcher_service.host = host
|
||||||
watcher_service.create()
|
watcher_service.create()
|
||||||
|
|
||||||
@@ -140,10 +135,6 @@ class ServiceHeartbeat(scheduling.BackgroundSchedulerService):
|
|||||||
self.add_job(self.send_beat, 'interval', seconds=60,
|
self.add_job(self.send_beat, 'interval', seconds=60,
|
||||||
next_run_time=datetime.datetime.now())
|
next_run_time=datetime.datetime.now())
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_service_name(cls):
|
|
||||||
return CONF.host, cls.service_name
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start service."""
|
"""Start service."""
|
||||||
self.add_heartbeat_job()
|
self.add_heartbeat_job()
|
||||||
@@ -177,13 +168,6 @@ class Service(service.ServiceBase):
|
|||||||
self.conductor_topic = self.manager.conductor_topic
|
self.conductor_topic = self.manager.conductor_topic
|
||||||
self.notification_topics = self.manager.notification_topics
|
self.notification_topics = self.manager.notification_topics
|
||||||
|
|
||||||
self.heartbeat = None
|
|
||||||
|
|
||||||
self.service_name = self.manager.service_name
|
|
||||||
if self.service_name:
|
|
||||||
self.heartbeat = ServiceHeartbeat(
|
|
||||||
service_name=self.manager.service_name)
|
|
||||||
|
|
||||||
self.conductor_endpoints = [
|
self.conductor_endpoints = [
|
||||||
ep(self) for ep in self.manager.conductor_endpoints
|
ep(self) for ep in self.manager.conductor_endpoints
|
||||||
]
|
]
|
||||||
@@ -199,6 +183,8 @@ class Service(service.ServiceBase):
|
|||||||
self.conductor_topic_handler = None
|
self.conductor_topic_handler = None
|
||||||
self.notification_handler = None
|
self.notification_handler = None
|
||||||
|
|
||||||
|
self.heartbeat = None
|
||||||
|
|
||||||
if self.conductor_topic and self.conductor_endpoints:
|
if self.conductor_topic and self.conductor_endpoints:
|
||||||
self.conductor_topic_handler = self.build_topic_handler(
|
self.conductor_topic_handler = self.build_topic_handler(
|
||||||
self.conductor_topic, self.conductor_endpoints)
|
self.conductor_topic, self.conductor_endpoints)
|
||||||
@@ -206,6 +192,10 @@ class Service(service.ServiceBase):
|
|||||||
self.notification_handler = self.build_notification_handler(
|
self.notification_handler = self.build_notification_handler(
|
||||||
self.notification_topics, self.notification_endpoints
|
self.notification_topics, self.notification_endpoints
|
||||||
)
|
)
|
||||||
|
self.service_name = self.manager.service_name
|
||||||
|
if self.service_name:
|
||||||
|
self.heartbeat = ServiceHeartbeat(
|
||||||
|
service_name=self.manager.service_name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def transport(self):
|
def transport(self):
|
||||||
@@ -235,7 +225,6 @@ class Service(service.ServiceBase):
|
|||||||
self.conductor_client = c
|
self.conductor_client = c
|
||||||
|
|
||||||
def build_topic_handler(self, topic_name, endpoints=()):
|
def build_topic_handler(self, topic_name, endpoints=()):
|
||||||
access_policy = dispatcher.DefaultRPCAccessPolicy
|
|
||||||
serializer = rpc.RequestContextSerializer(rpc.JsonPayloadSerializer())
|
serializer = rpc.RequestContextSerializer(rpc.JsonPayloadSerializer())
|
||||||
target = om.Target(
|
target = om.Target(
|
||||||
topic=topic_name,
|
topic=topic_name,
|
||||||
@@ -245,8 +234,7 @@ class Service(service.ServiceBase):
|
|||||||
)
|
)
|
||||||
return om.get_rpc_server(
|
return om.get_rpc_server(
|
||||||
self.transport, target, endpoints,
|
self.transport, target, endpoints,
|
||||||
executor='eventlet', serializer=serializer,
|
executor='eventlet', serializer=serializer)
|
||||||
access_policy=access_policy)
|
|
||||||
|
|
||||||
def build_notification_handler(self, topic_names, endpoints=()):
|
def build_notification_handler(self, topic_names, endpoints=()):
|
||||||
serializer = rpc.RequestContextSerializer(rpc.JsonPayloadSerializer())
|
serializer = rpc.RequestContextSerializer(rpc.JsonPayloadSerializer())
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from oslo_utils import timeutils
|
|||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from watcher._i18n import _LW
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
|
|
||||||
from watcher import conf
|
from watcher import conf
|
||||||
@@ -72,9 +73,9 @@ def safe_rstrip(value, chars=None):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, six.string_types):
|
if not isinstance(value, six.string_types):
|
||||||
LOG.warning(
|
LOG.warning(_LW(
|
||||||
"Failed to remove trailing character. Returning original object."
|
"Failed to remove trailing character. Returning original object."
|
||||||
"Supplied object is not a string: %s,", value)
|
"Supplied object is not a string: %s,"), value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
return value.rstrip(chars) or value
|
return value.rstrip(chars) or value
|
||||||
|
|||||||
4
watcher/conf/__init__.py
Executable file → Normal file
4
watcher/conf/__init__.py
Executable file → Normal file
@@ -28,8 +28,6 @@ from watcher.conf import db
|
|||||||
from watcher.conf import decision_engine
|
from watcher.conf import decision_engine
|
||||||
from watcher.conf import exception
|
from watcher.conf import exception
|
||||||
from watcher.conf import glance_client
|
from watcher.conf import glance_client
|
||||||
from watcher.conf import gnocchi_client
|
|
||||||
from watcher.conf import ironic_client
|
|
||||||
from watcher.conf import monasca_client
|
from watcher.conf import monasca_client
|
||||||
from watcher.conf import neutron_client
|
from watcher.conf import neutron_client
|
||||||
from watcher.conf import nova_client
|
from watcher.conf import nova_client
|
||||||
@@ -52,9 +50,7 @@ decision_engine.register_opts(CONF)
|
|||||||
monasca_client.register_opts(CONF)
|
monasca_client.register_opts(CONF)
|
||||||
nova_client.register_opts(CONF)
|
nova_client.register_opts(CONF)
|
||||||
glance_client.register_opts(CONF)
|
glance_client.register_opts(CONF)
|
||||||
gnocchi_client.register_opts(CONF)
|
|
||||||
cinder_client.register_opts(CONF)
|
cinder_client.register_opts(CONF)
|
||||||
ceilometer_client.register_opts(CONF)
|
ceilometer_client.register_opts(CONF)
|
||||||
neutron_client.register_opts(CONF)
|
neutron_client.register_opts(CONF)
|
||||||
clients_auth.register_opts(CONF)
|
clients_auth.register_opts(CONF)
|
||||||
ironic_client.register_opts(CONF)
|
|
||||||
|
|||||||
@@ -32,10 +32,9 @@ API_SERVICE_OPTS = [
|
|||||||
cfg.PortOpt('port',
|
cfg.PortOpt('port',
|
||||||
default=9322,
|
default=9322,
|
||||||
help='The port for the watcher API server'),
|
help='The port for the watcher API server'),
|
||||||
cfg.HostAddressOpt('host',
|
cfg.StrOpt('host',
|
||||||
default='127.0.0.1',
|
default='127.0.0.1',
|
||||||
help='The listen IP address for the watcher API server'
|
help='The listen IP address for the watcher API server'),
|
||||||
),
|
|
||||||
cfg.IntOpt('max_limit',
|
cfg.IntOpt('max_limit',
|
||||||
default=1000,
|
default=1000,
|
||||||
help='The maximum number of items returned in a single '
|
help='The maximum number of items returned in a single '
|
||||||
|
|||||||
@@ -25,12 +25,7 @@ CEILOMETER_CLIENT_OPTS = [
|
|||||||
cfg.StrOpt('api_version',
|
cfg.StrOpt('api_version',
|
||||||
default='2',
|
default='2',
|
||||||
help='Version of Ceilometer API to use in '
|
help='Version of Ceilometer API to use in '
|
||||||
'ceilometerclient.'),
|
'ceilometerclient.')]
|
||||||
cfg.StrOpt('endpoint_type',
|
|
||||||
default='internalURL',
|
|
||||||
help='Type of endpoint to use in ceilometerclient.'
|
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
|
||||||
'The default is internalURL.')]
|
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -23,13 +23,8 @@ cinder_client = cfg.OptGroup(name='cinder_client',
|
|||||||
|
|
||||||
CINDER_CLIENT_OPTS = [
|
CINDER_CLIENT_OPTS = [
|
||||||
cfg.StrOpt('api_version',
|
cfg.StrOpt('api_version',
|
||||||
default='3',
|
default='2',
|
||||||
help='Version of Cinder API to use in cinderclient.'),
|
help='Version of Cinder API to use in cinderclient.')]
|
||||||
cfg.StrOpt('endpoint_type',
|
|
||||||
default='publicURL',
|
|
||||||
help='Type of endpoint to use in cinderclient.'
|
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
|
||||||
'The default is publicURL.')]
|
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -42,15 +42,6 @@ WATCHER_DECISION_ENGINE_OPTS = [
|
|||||||
required=True,
|
required=True,
|
||||||
help='The maximum number of threads that can be used to '
|
help='The maximum number of threads that can be used to '
|
||||||
'execute strategies'),
|
'execute strategies'),
|
||||||
cfg.IntOpt('action_plan_expiry',
|
|
||||||
default=24,
|
|
||||||
help='An expiry timespan(hours). Watcher invalidates any '
|
|
||||||
'action plan for which its creation time '
|
|
||||||
'-whose number of hours has been offset by this value-'
|
|
||||||
' is older that the current time.'),
|
|
||||||
cfg.IntOpt('check_periodic_interval',
|
|
||||||
default=30*60,
|
|
||||||
help='Interval (in seconds) for checking action plan expiry.')
|
|
||||||
]
|
]
|
||||||
|
|
||||||
WATCHER_CONTINUOUS_OPTS = [
|
WATCHER_CONTINUOUS_OPTS = [
|
||||||
|
|||||||
@@ -24,12 +24,7 @@ glance_client = cfg.OptGroup(name='glance_client',
|
|||||||
GLANCE_CLIENT_OPTS = [
|
GLANCE_CLIENT_OPTS = [
|
||||||
cfg.StrOpt('api_version',
|
cfg.StrOpt('api_version',
|
||||||
default='2',
|
default='2',
|
||||||
help='Version of Glance API to use in glanceclient.'),
|
help='Version of Glance API to use in glanceclient.')]
|
||||||
cfg.StrOpt('endpoint_type',
|
|
||||||
default='internalURL',
|
|
||||||
help='Type of endpoint to use in glanceclient.'
|
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
|
||||||
'The default is internalURL.')]
|
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
# -*- 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_config import cfg
|
|
||||||
|
|
||||||
gnocchi_client = cfg.OptGroup(name='gnocchi_client',
|
|
||||||
title='Configuration Options for Gnocchi')
|
|
||||||
|
|
||||||
GNOCCHI_CLIENT_OPTS = [
|
|
||||||
cfg.StrOpt('api_version',
|
|
||||||
default='1',
|
|
||||||
help='Version of Gnocchi API to use in gnocchiclient.'),
|
|
||||||
cfg.StrOpt('endpoint_type',
|
|
||||||
default='internalURL',
|
|
||||||
help='Type of endpoint to use in gnocchi client.'
|
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
|
||||||
'The default is internalURL.'),
|
|
||||||
cfg.IntOpt('query_max_retries',
|
|
||||||
default=10,
|
|
||||||
help='How many times Watcher is trying to query again'),
|
|
||||||
cfg.IntOpt('query_timeout',
|
|
||||||
default=1,
|
|
||||||
help='How many seconds Watcher should wait to do query again')]
|
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
|
||||||
conf.register_group(gnocchi_client)
|
|
||||||
conf.register_opts(GNOCCHI_CLIENT_OPTS, group=gnocchi_client)
|
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
|
||||||
return [('gnocchi_client', GNOCCHI_CLIENT_OPTS)]
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
# Copyright (c) 2017 ZTE 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
|
|
||||||
|
|
||||||
ironic_client = cfg.OptGroup(name='ironic_client',
|
|
||||||
title='Configuration Options for Ironic')
|
|
||||||
|
|
||||||
IRONIC_CLIENT_OPTS = [
|
|
||||||
cfg.StrOpt('api_version',
|
|
||||||
default=1,
|
|
||||||
help='Version of Ironic API to use in ironicclient.'),
|
|
||||||
cfg.StrOpt('endpoint_type',
|
|
||||||
default='internalURL',
|
|
||||||
help='Type of endpoint to use in ironicclient.'
|
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
|
||||||
'The default is internalURL.')]
|
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
|
||||||
conf.register_group(ironic_client)
|
|
||||||
conf.register_opts(IRONIC_CLIENT_OPTS, group=ironic_client)
|
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
|
||||||
return [('ironic_client', IRONIC_CLIENT_OPTS)]
|
|
||||||
@@ -24,12 +24,7 @@ monasca_client = cfg.OptGroup(name='monasca_client',
|
|||||||
MONASCA_CLIENT_OPTS = [
|
MONASCA_CLIENT_OPTS = [
|
||||||
cfg.StrOpt('api_version',
|
cfg.StrOpt('api_version',
|
||||||
default='2_0',
|
default='2_0',
|
||||||
help='Version of Monasca API to use in monascaclient.'),
|
help='Version of Monasca API to use in monascaclient.')]
|
||||||
cfg.StrOpt('interface',
|
|
||||||
default='internal',
|
|
||||||
help='Type of interface used for monasca endpoint.'
|
|
||||||
'Supported values: internal, public, admin'
|
|
||||||
'The default is internal.')]
|
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -24,12 +24,7 @@ neutron_client = cfg.OptGroup(name='neutron_client',
|
|||||||
NEUTRON_CLIENT_OPTS = [
|
NEUTRON_CLIENT_OPTS = [
|
||||||
cfg.StrOpt('api_version',
|
cfg.StrOpt('api_version',
|
||||||
default='2.0',
|
default='2.0',
|
||||||
help='Version of Neutron API to use in neutronclient.'),
|
help='Version of Neutron API to use in neutronclient.')]
|
||||||
cfg.StrOpt('endpoint_type',
|
|
||||||
default='internalURL',
|
|
||||||
help='Type of endpoint to use in neutronclient.'
|
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
|
||||||
'The default is internalURL.')]
|
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
7
watcher/conf/nova_client.py
Executable file → Normal file
7
watcher/conf/nova_client.py
Executable file → Normal file
@@ -24,12 +24,7 @@ nova_client = cfg.OptGroup(name='nova_client',
|
|||||||
NOVA_CLIENT_OPTS = [
|
NOVA_CLIENT_OPTS = [
|
||||||
cfg.StrOpt('api_version',
|
cfg.StrOpt('api_version',
|
||||||
default='2',
|
default='2',
|
||||||
help='Version of Nova API to use in novaclient.'),
|
help='Version of Nova API to use in novaclient.')]
|
||||||
cfg.StrOpt('endpoint_type',
|
|
||||||
default='publicURL',
|
|
||||||
help='Type of endpoint to use in novaclient.'
|
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
|
||||||
'The default is publicURL.')]
|
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -26,14 +26,13 @@ SERVICE_OPTS = [
|
|||||||
cfg.IntOpt('periodic_interval',
|
cfg.IntOpt('periodic_interval',
|
||||||
default=60,
|
default=60,
|
||||||
help=_('Seconds between running periodic tasks.')),
|
help=_('Seconds between running periodic tasks.')),
|
||||||
cfg.HostAddressOpt('host',
|
cfg.StrOpt('host',
|
||||||
default=socket.gethostname(),
|
default=socket.gethostname(),
|
||||||
help=_('Name of this node. This can be an opaque '
|
help=_('Name of this node. This can be an opaque identifier. '
|
||||||
'identifier. It is not necessarily a hostname, '
|
'It is not necessarily a hostname, FQDN, or IP address. '
|
||||||
'FQDN, or IP address. However, the node name '
|
'However, the node name must be valid within '
|
||||||
'must be valid within an AMQP key, and if using '
|
'an AMQP key, and if using ZeroMQ, a valid '
|
||||||
'ZeroMQ, a valid hostname, FQDN, or IP address.')
|
'hostname, FQDN, or IP address.')),
|
||||||
),
|
|
||||||
cfg.IntOpt('service_down_time',
|
cfg.IntOpt('service_down_time',
|
||||||
default=90,
|
default=90,
|
||||||
help=_('Maximum time since last check-in for up service.'))
|
help=_('Maximum time since last check-in for up service.'))
|
||||||
|
|||||||
@@ -32,43 +32,6 @@ class CeilometerHelper(object):
|
|||||||
self.osc = osc if osc else clients.OpenStackClients()
|
self.osc = osc if osc else clients.OpenStackClients()
|
||||||
self.ceilometer = self.osc.ceilometer()
|
self.ceilometer = self.osc.ceilometer()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def format_query(user_id, tenant_id, resource_id,
|
|
||||||
user_ids, tenant_ids, resource_ids):
|
|
||||||
query = []
|
|
||||||
|
|
||||||
def query_append(query, _id, _ids, field):
|
|
||||||
if _id:
|
|
||||||
_ids = [_id]
|
|
||||||
for x_id in _ids:
|
|
||||||
query.append({"field": field, "op": "eq", "value": x_id})
|
|
||||||
|
|
||||||
query_append(query, user_id, (user_ids or []), "user_id")
|
|
||||||
query_append(query, tenant_id, (tenant_ids or []), "project_id")
|
|
||||||
query_append(query, resource_id, (resource_ids or []), "resource_id")
|
|
||||||
|
|
||||||
return query
|
|
||||||
|
|
||||||
def _timestamps(self, start_time, end_time):
|
|
||||||
|
|
||||||
def _format_timestamp(_time):
|
|
||||||
if _time:
|
|
||||||
if isinstance(_time, datetime.datetime):
|
|
||||||
return _time.isoformat()
|
|
||||||
return _time
|
|
||||||
return None
|
|
||||||
|
|
||||||
start_timestamp = _format_timestamp(start_time)
|
|
||||||
end_timestamp = _format_timestamp(end_time)
|
|
||||||
|
|
||||||
if ((start_timestamp is not None) and (end_timestamp is not None) and
|
|
||||||
(timeutils.parse_isotime(start_timestamp) >
|
|
||||||
timeutils.parse_isotime(end_timestamp))):
|
|
||||||
raise exception.Invalid(
|
|
||||||
_("Invalid query: %(start_time)s > %(end_time)s") % dict(
|
|
||||||
start_time=start_timestamp, end_time=end_timestamp))
|
|
||||||
return start_timestamp, end_timestamp
|
|
||||||
|
|
||||||
def build_query(self, user_id=None, tenant_id=None, resource_id=None,
|
def build_query(self, user_id=None, tenant_id=None, resource_id=None,
|
||||||
user_ids=None, tenant_ids=None, resource_ids=None,
|
user_ids=None, tenant_ids=None, resource_ids=None,
|
||||||
start_time=None, end_time=None):
|
start_time=None, end_time=None):
|
||||||
@@ -86,11 +49,45 @@ class CeilometerHelper(object):
|
|||||||
:param end_time: datetime until which measurements should be collected
|
:param end_time: datetime until which measurements should be collected
|
||||||
"""
|
"""
|
||||||
|
|
||||||
query = self.format_query(user_id, tenant_id, resource_id,
|
user_ids = user_ids or []
|
||||||
user_ids, tenant_ids, resource_ids)
|
tenant_ids = tenant_ids or []
|
||||||
|
resource_ids = resource_ids or []
|
||||||
|
|
||||||
start_timestamp, end_timestamp = self._timestamps(start_time,
|
query = []
|
||||||
end_time)
|
if user_id:
|
||||||
|
user_ids = [user_id]
|
||||||
|
for u_id in user_ids:
|
||||||
|
query.append({"field": "user_id", "op": "eq", "value": u_id})
|
||||||
|
|
||||||
|
if tenant_id:
|
||||||
|
tenant_ids = [tenant_id]
|
||||||
|
for t_id in tenant_ids:
|
||||||
|
query.append({"field": "project_id", "op": "eq", "value": t_id})
|
||||||
|
|
||||||
|
if resource_id:
|
||||||
|
resource_ids = [resource_id]
|
||||||
|
for r_id in resource_ids:
|
||||||
|
query.append({"field": "resource_id", "op": "eq", "value": r_id})
|
||||||
|
|
||||||
|
start_timestamp = None
|
||||||
|
end_timestamp = None
|
||||||
|
|
||||||
|
if start_time:
|
||||||
|
start_timestamp = start_time
|
||||||
|
if isinstance(start_time, datetime.datetime):
|
||||||
|
start_timestamp = start_time.isoformat()
|
||||||
|
|
||||||
|
if end_time:
|
||||||
|
end_timestamp = end_time
|
||||||
|
if isinstance(end_time, datetime.datetime):
|
||||||
|
end_timestamp = end_time.isoformat()
|
||||||
|
|
||||||
|
if (start_timestamp and end_timestamp and
|
||||||
|
timeutils.parse_isotime(start_timestamp) >
|
||||||
|
timeutils.parse_isotime(end_timestamp)):
|
||||||
|
raise exception.Invalid(
|
||||||
|
_("Invalid query: %(start_time)s > %(end_time)s") % dict(
|
||||||
|
start_time=start_timestamp, end_time=end_timestamp))
|
||||||
|
|
||||||
if start_timestamp:
|
if start_timestamp:
|
||||||
query.append({"field": "timestamp", "op": "ge",
|
query.append({"field": "timestamp", "op": "ge",
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
# -*- 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 datetime import datetime
|
|
||||||
import time
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_log import log
|
|
||||||
|
|
||||||
from watcher.common import clients
|
|
||||||
from watcher.common import exception
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class GnocchiHelper(object):
|
|
||||||
|
|
||||||
def __init__(self, osc=None):
|
|
||||||
""":param osc: an OpenStackClients instance"""
|
|
||||||
self.osc = osc if osc else clients.OpenStackClients()
|
|
||||||
self.gnocchi = self.osc.gnocchi()
|
|
||||||
|
|
||||||
def query_retry(self, f, *args, **kwargs):
|
|
||||||
for i in range(CONF.gnocchi_client.query_max_retries):
|
|
||||||
try:
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
time.sleep(CONF.gnocchi_client.query_timeout)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def statistic_aggregation(self,
|
|
||||||
resource_id,
|
|
||||||
metric,
|
|
||||||
granularity,
|
|
||||||
start_time=None,
|
|
||||||
stop_time=None,
|
|
||||||
aggregation='mean'):
|
|
||||||
"""Representing a statistic aggregate by operators
|
|
||||||
|
|
||||||
:param metric: metric name of which we want the statistics
|
|
||||||
:param resource_id: id of resource to list statistics for
|
|
||||||
:param start_time: Start datetime from which metrics will be used
|
|
||||||
:param stop_time: End datetime from which metrics will be used
|
|
||||||
:param granularity: frequency of marking metric point, in seconds
|
|
||||||
:param aggregation: Should be chosen in accordance with policy
|
|
||||||
aggregations
|
|
||||||
:return: value of aggregated metric
|
|
||||||
"""
|
|
||||||
|
|
||||||
if start_time is not None and not isinstance(start_time, datetime):
|
|
||||||
raise exception.InvalidParameter(parameter='start_time',
|
|
||||||
parameter_type=datetime)
|
|
||||||
|
|
||||||
if stop_time is not None and not isinstance(stop_time, datetime):
|
|
||||||
raise exception.InvalidParameter(parameter='stop_time',
|
|
||||||
parameter_type=datetime)
|
|
||||||
|
|
||||||
raw_kwargs = dict(
|
|
||||||
metric=metric,
|
|
||||||
start=start_time,
|
|
||||||
stop=stop_time,
|
|
||||||
resource_id=resource_id,
|
|
||||||
granularity=granularity,
|
|
||||||
aggregation=aggregation,
|
|
||||||
)
|
|
||||||
|
|
||||||
kwargs = {k: v for k, v in raw_kwargs.items() if k and v}
|
|
||||||
|
|
||||||
statistics = self.query_retry(
|
|
||||||
f=self.gnocchi.metric.get_measures, **kwargs)
|
|
||||||
|
|
||||||
if statistics:
|
|
||||||
# return value of latest measure
|
|
||||||
# measure has structure [time, granularity, value]
|
|
||||||
return statistics[-1][2]
|
|
||||||
@@ -688,7 +688,7 @@ class BaseConnection(object):
|
|||||||
def update_efficacy_indicator(self, efficacy_indicator_id, values):
|
def update_efficacy_indicator(self, efficacy_indicator_id, values):
|
||||||
"""Update properties of an efficacy indicator.
|
"""Update properties of an efficacy indicator.
|
||||||
|
|
||||||
:param efficacy_indicator_id: The ID of an efficacy indicator
|
:param efficacy_indicator_uuid: The UUID of an efficacy indicator
|
||||||
:returns: An efficacy indicator
|
:returns: An efficacy indicator
|
||||||
:raises: :py:class:`~.EfficacyIndicatorNotFound`
|
:raises: :py:class:`~.EfficacyIndicatorNotFound`
|
||||||
:raises: :py:class:`~.Invalid`
|
:raises: :py:class:`~.Invalid`
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from oslo_utils import strutils
|
|||||||
import prettytable as ptable
|
import prettytable as ptable
|
||||||
from six.moves import input
|
from six.moves import input
|
||||||
|
|
||||||
from watcher._i18n import _
|
from watcher._i18n import _, _LI
|
||||||
from watcher._i18n import lazy_translation_enabled
|
from watcher._i18n import lazy_translation_enabled
|
||||||
from watcher.common import context
|
from watcher.common import context
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
@@ -231,7 +231,7 @@ class PurgeCommand(object):
|
|||||||
if action.action_plan_id not in action_plan_ids]
|
if action.action_plan_id not in action_plan_ids]
|
||||||
|
|
||||||
LOG.debug("Orphans found:\n%s", orphans)
|
LOG.debug("Orphans found:\n%s", orphans)
|
||||||
LOG.info("Orphans found:\n%s", orphans.get_count_table())
|
LOG.info(_LI("Orphans found:\n%s"), orphans.get_count_table())
|
||||||
|
|
||||||
return orphans
|
return orphans
|
||||||
|
|
||||||
@@ -403,13 +403,13 @@ class PurgeCommand(object):
|
|||||||
return to_be_deleted
|
return to_be_deleted
|
||||||
|
|
||||||
def do_delete(self):
|
def do_delete(self):
|
||||||
LOG.info("Deleting...")
|
LOG.info(_LI("Deleting..."))
|
||||||
# Reversed to avoid errors with foreign keys
|
# Reversed to avoid errors with foreign keys
|
||||||
for entry in reversed(list(self._objects_map)):
|
for entry in reversed(list(self._objects_map)):
|
||||||
entry.destroy()
|
entry.destroy()
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
LOG.info("Starting purge command")
|
LOG.info(_LI("Starting purge command"))
|
||||||
self._objects_map = self.find_objects_to_delete()
|
self._objects_map = self.find_objects_to_delete()
|
||||||
|
|
||||||
if (self.max_number is not None and
|
if (self.max_number is not None and
|
||||||
@@ -424,15 +424,15 @@ class PurgeCommand(object):
|
|||||||
if not self.dry_run and self.confirmation_prompt():
|
if not self.dry_run and self.confirmation_prompt():
|
||||||
self.do_delete()
|
self.do_delete()
|
||||||
print(_("Purge results summary%s:") % _orphans_note)
|
print(_("Purge results summary%s:") % _orphans_note)
|
||||||
LOG.info("Purge results summary%s:", _orphans_note)
|
LOG.info(_LI("Purge results summary%s:"), _orphans_note)
|
||||||
else:
|
else:
|
||||||
LOG.debug(self._objects_map)
|
LOG.debug(self._objects_map)
|
||||||
print(_("Here below is a table containing the objects "
|
print(_("Here below is a table containing the objects "
|
||||||
"that can be purged%s:") % _orphans_note)
|
"that can be purged%s:") % _orphans_note)
|
||||||
|
|
||||||
LOG.info("\n%s", self._objects_map.get_count_table())
|
LOG.info(_LI("\n%s"), self._objects_map.get_count_table())
|
||||||
print(self._objects_map.get_count_table())
|
print(self._objects_map.get_count_table())
|
||||||
LOG.info("Purge process completed")
|
LOG.info(_LI("Purge process completed"))
|
||||||
|
|
||||||
|
|
||||||
def purge(age_in_days, max_number, goal, exclude_orphans, dry_run):
|
def purge(age_in_days, max_number, goal, exclude_orphans, dry_run):
|
||||||
@@ -457,11 +457,11 @@ def purge(age_in_days, max_number, goal, exclude_orphans, dry_run):
|
|||||||
if max_number and max_number < 0:
|
if max_number and max_number < 0:
|
||||||
raise exception.NegativeLimitError
|
raise exception.NegativeLimitError
|
||||||
|
|
||||||
LOG.info("[options] age_in_days = %s", age_in_days)
|
LOG.info(_LI("[options] age_in_days = %s"), age_in_days)
|
||||||
LOG.info("[options] max_number = %s", max_number)
|
LOG.info(_LI("[options] max_number = %s"), max_number)
|
||||||
LOG.info("[options] goal = %s", goal)
|
LOG.info(_LI("[options] goal = %s"), goal)
|
||||||
LOG.info("[options] exclude_orphans = %s", exclude_orphans)
|
LOG.info(_LI("[options] exclude_orphans = %s"), exclude_orphans)
|
||||||
LOG.info("[options] dry_run = %s", dry_run)
|
LOG.info(_LI("[options] dry_run = %s"), dry_run)
|
||||||
|
|
||||||
uuid = PurgeCommand.get_goal_uuid(goal)
|
uuid = PurgeCommand.get_goal_uuid(goal)
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
"""Add apscheduler_jobs table to store background jobs
|
|
||||||
|
|
||||||
Revision ID: 0f6042416884
|
|
||||||
Revises: 001
|
|
||||||
Create Date: 2017-03-24 11:21:29.036532
|
|
||||||
|
|
||||||
"""
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
from watcher.db.sqlalchemy import models
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = '0f6042416884'
|
|
||||||
down_revision = '001'
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
op.create_table(
|
|
||||||
'apscheduler_jobs',
|
|
||||||
sa.Column('id', sa.Unicode(191, _warn_on_bytestring=False),
|
|
||||||
nullable=False),
|
|
||||||
sa.Column('next_run_time', sa.Float(25), index=True),
|
|
||||||
sa.Column('job_state', sa.LargeBinary, nullable=False),
|
|
||||||
sa.Column('service_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('tag', models.JSONEncodedDict(), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('id'),
|
|
||||||
sa.ForeignKeyConstraint(['service_id'], ['services.id'])
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
op.drop_table('apscheduler_jobs')
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user