Compare commits

..

8 Commits

Author SHA1 Message Date
Jenkins
1caf89686c Merge "Add checking audit state" into stable/ocata 2017-02-16 13:53:58 +00:00
Hidekazu Nakamura
ed3224835a Add checking audit state
This patch adds checking audit state when updating an existing audit
in accordance with audit state machine.

Closes-Bug: #1662406

Change-Id: I20610c83169b77f141974a5cebe33818a4bf0728
(cherry picked from commit 0d83354c57)
2017-02-16 13:16:06 +00:00
ericxiett
8a7e316f73 Fix that remove 'strategy' attribute does not work.
The 'strategy_id' attribute is the one that not exposes
to API. But the 'PATCH' API always update this attribute.
So this change does not work when choose 'remove' op.
This patch sets value for 'strategy_id' attribute.

Change-Id: I1597fb5d4985bb8271ad3cea7ea5f0adb7de65f4
Closes-Bug: #1662395
(cherry picked from commit e55c73be0e)
2017-02-16 13:15:48 +00:00
Jenkins
112ac3bbdf Merge "Fix log level error to warning" into stable/ocata 2017-02-14 10:27:17 +00:00
licanwei
d1ab697612 Fix the mapping between the instance and the node
The argument to the add_edge function should be instance.uuid
and node.uuid, not instance and node

Change-Id: Ida694f9158d3eb26e7f31062a18844472ea3c6fa
Closes-Bug: #1662810
2017-02-08 15:37:01 +00:00
Hidekazu Nakamura
095ca0ffb2 Fix log level error to warning
When action plan is currently running, new action plan is set as
SUPERSEDED and error log reported. This patch changes log level
from error to warning.

Change-Id: I931218843d8f09340bd5363256164807d514446b
Closes-Bug: #1662450
(cherry picked from commit 58711aaaec)
2017-02-08 00:05:44 +00:00
OpenStack Release Bot
e1131a65d8 Update UPPER_CONSTRAINTS_FILE for stable/ocata
Change-Id: Ie56e3186f2c9e42aff817fc7c71c4da93abb1c5f
2017-02-02 18:23:36 +00:00
OpenStack Release Bot
5e507e56f4 Update .gitreview for stable/ocata
Change-Id: I5a431c04e85183baf05736de3920d6610d4add03
2017-02-02 18:23:36 +00:00
215 changed files with 1688 additions and 6492 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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/

View File

@@ -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/

View File

@@ -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>

View File

@@ -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 run_process watcher-api "$WATCHER_BIN_DIR/watcher-api --config-file $WATCHER_CONF"
else
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
} }

View File

@@ -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"

View File

@@ -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

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -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

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

View File

@@ -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

View File

@@ -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

View File

@@ -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
------------------ ------------------

View File

@@ -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>

View File

@@ -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

View File

@@ -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 |
+--------------+----------------------------------+

View File

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

View File

@@ -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.

View File

@@ -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/>`_.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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/.

View File

@@ -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 |
+--------------------------------------+--------------------------------------+-----------+---------------------------+-----------------+

View File

@@ -1,4 +0,0 @@
---
features:
- |
Adds feature to cancel an action-plan.

View File

@@ -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.

View File

@@ -1,4 +0,0 @@
---
features:
- |
Added SUSPENDED audit state

View File

@@ -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.
# #

View File

@@ -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

View File

@@ -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"

View File

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

View File

@@ -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

View File

@@ -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]

View File

@@ -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)

View File

@@ -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
View File

@@ -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

View File

@@ -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()

View File

@@ -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,

View File

@@ -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,22 +572,21 @@ class AuditsController(rest.RestController):
try: try:
audit_dict = audit_to_update.as_dict() audit_dict = audit_to_update.as_dict()
initial_state = audit_dict['state']
new_state = api_utils.get_patch_value(patch, 'state')
if not api_utils.check_audit_state_transition(
patch, initial_state):
error_message = _("State transition not allowed: "
"(%(initial_state)s -> %(new_state)s)")
raise exception.PatchError(
patch=patch,
reason=error_message % dict(
initial_state=initial_state, new_state=new_state))
audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch)) audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch))
except api_utils.JSONPATCH_EXCEPTIONS as e: except api_utils.JSONPATCH_EXCEPTIONS as e:
raise exception.PatchError(patch=patch, reason=e) raise exception.PatchError(patch=patch, reason=e)
initial_state = audit_dict['state']
new_state = api_utils.get_patch_value(patch, 'state')
allowed_states = ALLOWED_AUDIT_TRANSITIONS.get(initial_state, [])
if new_state is not None and new_state not in allowed_states:
error_message = _("State transition not allowed: "
"(%(initial_state)s -> %(new_state)s)")
raise exception.PatchError(
patch=patch,
reason=error_message % dict(
initial_state=initial_state, new_state=new_state))
# Update only the fields that have changed # Update only the fields that have changed
for field in objects.Audit.fields: for field in objects.Audit.fields:
try: try:

View File

@@ -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)

View File

@@ -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):

View File

@@ -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.

View File

@@ -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():

View File

@@ -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'))

View File

@@ -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.
"""

View File

@@ -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()

View File

@@ -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)

View File

@@ -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.")

View File

@@ -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"

View File

@@ -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")

View File

@@ -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."

View File

@@ -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
View 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
self.engine.notify(self._db_action, objects.action.State.ONGOING) def action(self):
LOG.debug("Pre-condition action: %s", self.name) if self.loaded_action is None:
self.action.pre_condition() action = self.engine.action_factory.make_action(
self._db_action,
osc=self._engine.osc)
self.loaded_action = action
return self.loaded_action
def do_execute(self, *args, **kwargs): @property
LOG.debug("Running action: %s", self.name) def engine(self):
return self._engine
# NOTE: For result is False, set action state fail def pre_execute(self):
result = self.action.execute() try:
if result is False: self.engine.notify(self._db_action, objects.action.State.ONGOING)
self.engine.notify(self._db_action, LOG.debug("Pre-condition action: %s", self.name)
objects.action.State.FAILED) self.action.pre_condition()
else: except Exception as e:
self.engine.notify(self._db_action, LOG.exception(e)
objects.action.State.SUCCEEDED) self.engine.notify(self._db_action, objects.action.State.FAILED)
raise
def do_post_execute(self): def execute(self, *args, **kwargs):
LOG.debug("Post-condition action: %s", self.name) try:
self.action.post_condition() LOG.debug("Running action: %s", self.name)
def do_revert(self, *args, **kwargs): self.action.execute()
LOG.warning("Revert action: %s", self.name) self.engine.notify(self._db_action, objects.action.State.SUCCEEDED)
except Exception as e:
LOG.exception(e)
LOG.error(_LE('The workflow engine has failed '
'to execute the action: %s'), self.name)
self.engine.notify(self._db_action, objects.action.State.FAILED)
raise
def post_execute(self):
try:
LOG.debug("Post-condition action: %s", self.name)
self.action.post_condition()
except Exception as e:
LOG.exception(e)
self.engine.notify(self._db_action, objects.action.State.FAILED)
raise
def revert(self, *args, **kwargs):
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):

View File

@@ -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()

View File

@@ -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)

View File

@@ -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()

View File

@@ -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.'))

55
watcher/common/clients.py Executable file → Normal file
View 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,12 +109,8 @@ 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') session=self.session)
self._ceilometer = ceclient.get_client(
ceilometerclient_version,
endpoint_type=ceilometer_endpoint_type,
session=self.session)
return self._ceilometer return self._ceilometer
@exception.wrap_keystone_exception @exception.wrap_keystone_exception
@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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):

View File

@@ -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())

View File

@@ -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
View 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)

View File

@@ -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 '

View File

@@ -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):

View File

@@ -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):

View File

@@ -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 = [

View File

@@ -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):

View File

@@ -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)]

View File

@@ -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)]

View File

@@ -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):

View File

@@ -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
View 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):

View File

@@ -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.'))

View File

@@ -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",

View File

@@ -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]

View File

@@ -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`

View File

@@ -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)

View File

@@ -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