Compare commits
18 Commits
1.9.0
...
queens-eol
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5307f5a80e | ||
|
|
b5467a2a1f | ||
|
|
83411ec89f | ||
|
|
08750536e7 | ||
|
|
9f7ccfe408 | ||
|
|
fb2619e538 | ||
|
|
6bd857fa0e | ||
|
|
e0faeea608 | ||
|
|
61aca40e6e | ||
|
|
b293389734 | ||
|
|
050e6d58f1 | ||
|
|
7223d35c47 | ||
|
|
57f1971982 | ||
|
|
c9b2b2aa39 | ||
|
|
a42c31c221 | ||
|
|
403ec94bc1 | ||
|
|
3431b77388 | ||
|
|
eb4cacc00e |
@@ -1,4 +1,5 @@
|
|||||||
[gerrit]
|
[gerrit]
|
||||||
host=review.openstack.org
|
host=review.opendev.org
|
||||||
port=29418
|
port=29418
|
||||||
project=openstack/watcher.git
|
project=openstack/watcher.git
|
||||||
|
defaultbranch=stable/queens
|
||||||
|
|||||||
156
.zuul.yaml
156
.zuul.yaml
@@ -1,139 +1,45 @@
|
|||||||
- project:
|
- project:
|
||||||
|
templates:
|
||||||
|
- openstack-python-jobs
|
||||||
|
- openstack-python35-jobs
|
||||||
|
- publish-openstack-sphinx-docs
|
||||||
|
- check-requirements
|
||||||
|
- release-notes-jobs
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- watcher-tempest-functional
|
- watcher-tempest-multinode
|
||||||
- watcher-tempest-dummy_optim
|
|
||||||
- watcher-tempest-actuator
|
|
||||||
- watcher-tempest-basic_optim
|
|
||||||
- watcher-tempest-workload_balancing
|
|
||||||
- watcherclient-tempest-functional
|
|
||||||
- legacy-rally-dsvm-watcher-rally
|
|
||||||
- openstack-tox-lower-constraints
|
|
||||||
gate:
|
gate:
|
||||||
jobs:
|
queue: watcher
|
||||||
- watcher-tempest-functional
|
|
||||||
- watcher-tempest-dummy_optim
|
|
||||||
- watcher-tempest-actuator
|
|
||||||
- watcher-tempest-basic_optim
|
|
||||||
- watcher-tempest-workload_balancing
|
|
||||||
- watcherclient-tempest-functional
|
|
||||||
- legacy-rally-dsvm-watcher-rally
|
|
||||||
- openstack-tox-lower-constraints
|
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: watcher-tempest-dummy_optim
|
name: watcher-tempest-base-multinode
|
||||||
parent: watcher-tempest-multinode
|
parent: legacy-dsvm-base-multinode
|
||||||
vars:
|
run: playbooks/legacy/watcher-tempest-base-multinode/run.yaml
|
||||||
tempest_test_regex: 'watcher_tempest_plugin.tests.scenario.test_execute_dummy_optim'
|
post-run: playbooks/legacy/watcher-tempest-base-multinode/post.yaml
|
||||||
|
timeout: 4200
|
||||||
- job:
|
|
||||||
name: watcher-tempest-actuator
|
|
||||||
parent: watcher-tempest-multinode
|
|
||||||
vars:
|
|
||||||
tempest_test_regex: 'watcher_tempest_plugin.tests.scenario.test_execute_actuator'
|
|
||||||
|
|
||||||
- job:
|
|
||||||
name: watcher-tempest-basic_optim
|
|
||||||
parent: watcher-tempest-multinode
|
|
||||||
vars:
|
|
||||||
tempest_test_regex: 'watcher_tempest_plugin.tests.scenario.test_execute_basic_optim'
|
|
||||||
|
|
||||||
- job:
|
|
||||||
name: watcher-tempest-workload_balancing
|
|
||||||
parent: watcher-tempest-multinode
|
|
||||||
vars:
|
|
||||||
tempest_test_regex: 'watcher_tempest_plugin.tests.scenario.test_execute_workload_balancing'
|
|
||||||
|
|
||||||
- job:
|
|
||||||
name: watcher-tempest-multinode
|
|
||||||
parent: watcher-tempest-functional
|
|
||||||
voting: false
|
|
||||||
nodeset: openstack-two-node
|
|
||||||
pre-run: playbooks/pre.yaml
|
|
||||||
run: playbooks/orchestrate-tempest.yaml
|
|
||||||
roles:
|
|
||||||
- zuul: openstack/tempest
|
|
||||||
group-vars:
|
|
||||||
subnode:
|
|
||||||
devstack_local_conf:
|
|
||||||
post-config:
|
|
||||||
$NOVA_CONF:
|
|
||||||
libvirt:
|
|
||||||
live_migration_uri: 'qemu+ssh://root@%s/system'
|
|
||||||
devstack_services:
|
|
||||||
watcher-api: false
|
|
||||||
watcher-decision-engine: false
|
|
||||||
watcher-applier: false
|
|
||||||
# We need to add TLS support for watcher plugin
|
|
||||||
tls-proxy: false
|
|
||||||
ceilometer: false
|
|
||||||
ceilometer-acompute: false
|
|
||||||
ceilometer-acentral: false
|
|
||||||
ceilometer-anotification: false
|
|
||||||
watcher: false
|
|
||||||
gnocchi-api: false
|
|
||||||
gnocchi-metricd: false
|
|
||||||
rabbit: false
|
|
||||||
mysql: false
|
|
||||||
vars:
|
|
||||||
devstack_local_conf:
|
|
||||||
post-config:
|
|
||||||
$NOVA_CONF:
|
|
||||||
libvirt:
|
|
||||||
live_migration_uri: 'qemu+ssh://root@%s/system'
|
|
||||||
test-config:
|
|
||||||
$TEMPEST_CONFIG:
|
|
||||||
compute:
|
|
||||||
min_compute_nodes: 2
|
|
||||||
compute-feature-enabled:
|
|
||||||
live_migration: true
|
|
||||||
block_migration_for_live_migration: true
|
|
||||||
devstack_plugins:
|
|
||||||
ceilometer: https://git.openstack.org/openstack/ceilometer
|
|
||||||
|
|
||||||
- job:
|
|
||||||
name: watcher-tempest-functional
|
|
||||||
parent: devstack-tempest
|
|
||||||
timeout: 7200
|
|
||||||
required-projects:
|
required-projects:
|
||||||
- openstack/ceilometer
|
- openstack/devstack-gate
|
||||||
- openstack-infra/devstack-gate
|
|
||||||
- openstack/python-openstackclient
|
- openstack/python-openstackclient
|
||||||
- openstack/python-watcherclient
|
- openstack/python-watcherclient
|
||||||
- openstack/watcher
|
- openstack/watcher
|
||||||
- openstack/watcher-tempest-plugin
|
- openstack/watcher-tempest-plugin
|
||||||
- openstack/tempest
|
nodeset: legacy-ubuntu-xenial-2-node
|
||||||
vars:
|
|
||||||
devstack_plugins:
|
|
||||||
watcher: https://git.openstack.org/openstack/watcher
|
|
||||||
devstack_services:
|
|
||||||
tls-proxy: false
|
|
||||||
watcher-api: true
|
|
||||||
watcher-decision-engine: true
|
|
||||||
watcher-applier: true
|
|
||||||
tempest: true
|
|
||||||
s-account: false
|
|
||||||
s-container: false
|
|
||||||
s-object: false
|
|
||||||
s-proxy: false
|
|
||||||
devstack_localrc:
|
|
||||||
TEMPEST_PLUGINS: '/opt/stack/watcher-tempest-plugin'
|
|
||||||
tempest_test_regex: 'watcher_tempest_plugin.tests.api'
|
|
||||||
tox_envlist: all
|
|
||||||
tox_environment:
|
|
||||||
# Do we really need to set this? It's cargo culted
|
|
||||||
PYTHONUNBUFFERED: 'true'
|
|
||||||
zuul_copy_output:
|
|
||||||
/etc/hosts: logs
|
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
# This job is used in python-watcherclient repo
|
name: watcher-tempest-multinode
|
||||||
name: watcherclient-tempest-functional
|
parent: watcher-tempest-base-multinode
|
||||||
parent: watcher-tempest-functional
|
|
||||||
voting: false
|
voting: false
|
||||||
|
|
||||||
|
- job:
|
||||||
|
# This job is used by python-watcherclient repo
|
||||||
|
name: watcherclient-tempest-functional
|
||||||
|
parent: legacy-dsvm-base
|
||||||
|
run: playbooks/legacy/watcherclient-tempest-functional/run.yaml
|
||||||
|
post-run: playbooks/legacy/watcherclient-tempest-functional/post.yaml
|
||||||
timeout: 4200
|
timeout: 4200
|
||||||
vars:
|
required-projects:
|
||||||
tempest_concurrency: 1
|
- openstack/devstack
|
||||||
devstack_localrc:
|
- openstack/devstack-gate
|
||||||
TEMPEST_PLUGINS: '/opt/stack/python-watcherclient'
|
- openstack/python-openstackclient
|
||||||
tempest_test_regex: 'watcherclient.tests.functional'
|
- openstack/python-watcherclient
|
||||||
|
- openstack/watcher
|
||||||
|
|||||||
@@ -8,4 +8,4 @@
|
|||||||
watcher Style Commandments
|
watcher Style Commandments
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/
|
Read the OpenStack Style Commandments https://docs.openstack.org/developer/hacking/
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
Team and repository tags
|
Team and repository tags
|
||||||
========================
|
========================
|
||||||
|
|
||||||
.. image:: https://governance.openstack.org/tc/badges/watcher.svg
|
.. image:: https://governance.openstack.org/badges/watcher.svg
|
||||||
:target: https://governance.openstack.org/tc/reference/tags/index.html
|
:target: https://governance.openstack.org/reference/tags/index.html
|
||||||
|
|
||||||
.. Change things from this point on
|
.. Change things from this point on
|
||||||
|
|
||||||
@@ -22,11 +22,10 @@ service for multi-tenant OpenStack-based clouds.
|
|||||||
Watcher provides a robust framework to realize a wide range of cloud
|
Watcher provides a robust framework to realize a wide range of cloud
|
||||||
optimization goals, including the reduction of data center
|
optimization goals, including the reduction of data center
|
||||||
operating costs, increased system performance via intelligent virtual machine
|
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: https://wiki.openstack.org/wiki/Watcher
|
||||||
* Source: https://github.com/openstack/watcher
|
* Source: https://github.com/openstack/watcher
|
||||||
* Bugs: https://bugs.launchpad.net/watcher
|
* Bugs: https://bugs.launchpad.net/watcher
|
||||||
* Documentation: https://docs.openstack.org/watcher/latest/
|
* Documentation: https://docs.openstack.org/watcher/latest/
|
||||||
* Release notes: https://docs.openstack.org/releasenotes/watcher/
|
|
||||||
|
|||||||
@@ -177,20 +177,16 @@ function create_watcher_conf {
|
|||||||
iniset $WATCHER_CONF DEFAULT debug "$ENABLE_DEBUG_LOG_LEVEL"
|
iniset $WATCHER_CONF DEFAULT debug "$ENABLE_DEBUG_LOG_LEVEL"
|
||||||
iniset $WATCHER_CONF DEFAULT control_exchange watcher
|
iniset $WATCHER_CONF DEFAULT control_exchange watcher
|
||||||
|
|
||||||
iniset_rpc_backend watcher $WATCHER_CONF
|
|
||||||
|
|
||||||
iniset $WATCHER_CONF database connection $(database_connection_url watcher)
|
iniset $WATCHER_CONF database connection $(database_connection_url watcher)
|
||||||
iniset $WATCHER_CONF api host "$WATCHER_SERVICE_HOST"
|
iniset $WATCHER_CONF api host "$WATCHER_SERVICE_HOST"
|
||||||
|
iniset $WATCHER_CONF api port "$WATCHER_SERVICE_PORT"
|
||||||
if is_service_enabled tls-proxy; then
|
|
||||||
iniset $WATCHER_CONF api port "$WATCHER_SERVICE_PORT_INT"
|
|
||||||
# iniset $WATCHER_CONF api enable_ssl_api "True"
|
|
||||||
else
|
|
||||||
iniset $WATCHER_CONF api port "$WATCHER_SERVICE_PORT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
iniset $WATCHER_CONF oslo_policy policy_file $WATCHER_POLICY_YAML
|
iniset $WATCHER_CONF oslo_policy policy_file $WATCHER_POLICY_YAML
|
||||||
|
|
||||||
|
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_userid $RABBIT_USERID
|
||||||
|
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD
|
||||||
|
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_host $RABBIT_HOST
|
||||||
|
|
||||||
iniset $WATCHER_CONF oslo_messaging_notifications driver "messagingv2"
|
iniset $WATCHER_CONF oslo_messaging_notifications driver "messagingv2"
|
||||||
|
|
||||||
iniset $NOVA_CONF oslo_messaging_notifications topics "notifications,watcher_notifications"
|
iniset $NOVA_CONF oslo_messaging_notifications topics "notifications,watcher_notifications"
|
||||||
@@ -301,7 +297,8 @@ function start_watcher_api {
|
|||||||
|
|
||||||
# Start proxies if enabled
|
# Start proxies if enabled
|
||||||
if is_service_enabled tls-proxy; then
|
if is_service_enabled tls-proxy; then
|
||||||
start_tls_proxy watcher '*' $WATCHER_SERVICE_PORT $WATCHER_SERVICE_HOST $WATCHER_SERVICE_PORT_INT
|
start_tls_proxy '*' $WATCHER_SERVICE_PORT $WATCHER_SERVICE_HOST $WATCHER_SERVICE_PORT_INT &
|
||||||
|
start_tls_proxy '*' $EC2_SERVICE_PORT $WATCHER_SERVICE_HOST $WATCHER_SERVICE_PORT_INT &
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,6 +314,7 @@ function start_watcher {
|
|||||||
function stop_watcher {
|
function stop_watcher {
|
||||||
if [[ "$WATCHER_USE_MOD_WSGI" == "True" ]]; then
|
if [[ "$WATCHER_USE_MOD_WSGI" == "True" ]]; then
|
||||||
disable_apache_site watcher-api
|
disable_apache_site watcher-api
|
||||||
|
restart_apache_server
|
||||||
else
|
else
|
||||||
stop_process watcher-api
|
stop_process watcher-api
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ VNCSERVER_PROXYCLIENT_ADDRESS=$HOST_IP
|
|||||||
NOVA_INSTANCES_PATH=/opt/stack/data/instances
|
NOVA_INSTANCES_PATH=/opt/stack/data/instances
|
||||||
|
|
||||||
# Enable the Ceilometer plugin for the compute agent
|
# Enable the Ceilometer plugin for the compute agent
|
||||||
enable_plugin ceilometer git://git.openstack.org/openstack/ceilometer
|
enable_plugin ceilometer https://git.openstack.org/openstack/ceilometer
|
||||||
disable_service ceilometer-acentral,ceilometer-collector,ceilometer-api
|
disable_service ceilometer-acentral,ceilometer-collector,ceilometer-api
|
||||||
|
|
||||||
LOGFILE=$DEST/logs/stack.sh.log
|
LOGFILE=$DEST/logs/stack.sh.log
|
||||||
|
|||||||
@@ -25,13 +25,13 @@ MULTI_HOST=1
|
|||||||
disable_service n-cpu
|
disable_service n-cpu
|
||||||
|
|
||||||
# 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 https://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 https://git.openstack.org/openstack/watcher
|
||||||
|
|
||||||
# Enable the Ceilometer plugin
|
# Enable the Ceilometer plugin
|
||||||
enable_plugin ceilometer git://git.openstack.org/openstack/ceilometer
|
enable_plugin ceilometer https://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
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ It is used via a single directive in the .rst file
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from docutils.parsers.rst import Directive
|
from sphinx.util.compat import Directive
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
|
|
||||||
from watcher.notifications import base as notification
|
from watcher.notifications import base as notification
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ extensions = [
|
|||||||
'ext.versioned_notifications',
|
'ext.versioned_notifications',
|
||||||
'oslo_config.sphinxconfiggen',
|
'oslo_config.sphinxconfiggen',
|
||||||
'openstackdocstheme',
|
'openstackdocstheme',
|
||||||
'sphinx.ext.napoleon',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
wsme_protocols = ['restjson']
|
wsme_protocols = ['restjson']
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ You can easily generate and update a sample configuration file
|
|||||||
named :ref:`watcher.conf.sample <watcher_sample_configuration_files>` by using
|
named :ref:`watcher.conf.sample <watcher_sample_configuration_files>` by using
|
||||||
these following commands::
|
these following commands::
|
||||||
|
|
||||||
$ git clone git://git.openstack.org/openstack/watcher
|
$ git clone https://git.openstack.org/openstack/watcher
|
||||||
$ cd watcher/
|
$ cd watcher/
|
||||||
$ tox -e genconfig
|
$ tox -e genconfig
|
||||||
$ vi etc/watcher/watcher.conf.sample
|
$ vi etc/watcher/watcher.conf.sample
|
||||||
@@ -217,7 +217,7 @@ so that the watcher service is configured for your needs.
|
|||||||
# The SQLAlchemy connection string used to connect to the
|
# The SQLAlchemy connection string used to connect to the
|
||||||
# database (string value)
|
# database (string value)
|
||||||
#connection=<None>
|
#connection=<None>
|
||||||
connection = mysql+pymysql://watcher:WATCHER_DBPASSWORD@DB_IP/watcher?charset=utf8
|
connection = mysql://watcher:WATCHER_DBPASSWORD@DB_IP/watcher?charset=utf8
|
||||||
|
|
||||||
#. Configure the Watcher Service to use the RabbitMQ message broker by
|
#. Configure the Watcher Service to use the RabbitMQ message broker by
|
||||||
setting one or more of these options. Replace RABBIT_HOST with the
|
setting one or more of these options. Replace RABBIT_HOST with the
|
||||||
@@ -235,8 +235,21 @@ so that the watcher service is configured for your needs.
|
|||||||
# option. (string value)
|
# option. (string value)
|
||||||
control_exchange = watcher
|
control_exchange = watcher
|
||||||
|
|
||||||
# ...
|
...
|
||||||
transport_url = rabbit://RABBITMQ_USER:RABBITMQ_PASSWORD@RABBIT_HOST
|
|
||||||
|
[oslo_messaging_rabbit]
|
||||||
|
|
||||||
|
# The username used by the message broker (string value)
|
||||||
|
rabbit_userid = RABBITMQ_USER
|
||||||
|
|
||||||
|
# The password of user used by the message broker (string value)
|
||||||
|
rabbit_password = RABBITMQ_PASSWORD
|
||||||
|
|
||||||
|
# The host where the message brokeris installed (string value)
|
||||||
|
rabbit_host = RABBIT_HOST
|
||||||
|
|
||||||
|
# The port used bythe message broker (string value)
|
||||||
|
#rabbit_port = 5672
|
||||||
|
|
||||||
|
|
||||||
#. Watcher API shall validate the token provided by every incoming request,
|
#. Watcher API shall validate the token provided by every incoming request,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
===================
|
|
||||||
Configuration Guide
|
|
||||||
===================
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 1
|
||||||
|
|
||||||
configuring
|
configuring
|
||||||
watcher
|
watcher
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ notifications of important events.
|
|||||||
|
|
||||||
* https://launchpad.net
|
* https://launchpad.net
|
||||||
* https://launchpad.net/watcher
|
* https://launchpad.net/watcher
|
||||||
* https://launchpad.net/openstack
|
* https://launchpad.net/~openstack
|
||||||
|
|
||||||
|
|
||||||
Project Hosting Details
|
Project Hosting Details
|
||||||
@@ -49,7 +49,7 @@ Bug tracker
|
|||||||
https://launchpad.net/watcher
|
https://launchpad.net/watcher
|
||||||
|
|
||||||
Mailing list (prefix subjects with ``[watcher]`` for faster responses)
|
Mailing list (prefix subjects with ``[watcher]`` for faster responses)
|
||||||
http://lists.openstack.org/pipermail/openstack-dev/
|
https://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
|
||||||
|
|
||||||
Wiki
|
Wiki
|
||||||
https://wiki.openstack.org/Watcher
|
https://wiki.openstack.org/Watcher
|
||||||
@@ -65,7 +65,7 @@ IRC Channel
|
|||||||
|
|
||||||
Weekly Meetings
|
Weekly Meetings
|
||||||
On Wednesdays at 14:00 UTC on even weeks in the ``#openstack-meeting-4``
|
On Wednesdays at 14:00 UTC on even weeks in the ``#openstack-meeting-4``
|
||||||
IRC channel, 08:00 UTC on odd weeks in the ``#openstack-meeting-alt``
|
IRC channel, 13:00 UTC on odd weeks in the ``#openstack-meeting-alt``
|
||||||
IRC channel (`meetings logs`_)
|
IRC channel (`meetings logs`_)
|
||||||
|
|
||||||
.. _changelog: http://eavesdrop.openstack.org/irclogs/%23openstack-watcher/
|
.. _changelog: http://eavesdrop.openstack.org/irclogs/%23openstack-watcher/
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ model. To enable the Watcher plugin with DevStack, add the following to the
|
|||||||
`[[local|localrc]]` section of your controller's `local.conf` to enable the
|
`[[local|localrc]]` section of your controller's `local.conf` to enable the
|
||||||
Watcher plugin::
|
Watcher plugin::
|
||||||
|
|
||||||
enable_plugin watcher git://git.openstack.org/openstack/watcher
|
enable_plugin watcher https://git.openstack.org/openstack/watcher
|
||||||
|
|
||||||
For more detailed instructions, see `Detailed DevStack Instructions`_. Check
|
For more detailed instructions, see `Detailed DevStack Instructions`_. Check
|
||||||
out the `DevStack documentation`_ for more information regarding DevStack.
|
out the `DevStack documentation`_ for more information regarding DevStack.
|
||||||
|
|||||||
@@ -123,10 +123,9 @@ You can re-activate this virtualenv for your current shell using:
|
|||||||
|
|
||||||
$ workon watcher
|
$ workon watcher
|
||||||
|
|
||||||
For more information on virtual environments, see virtualenv_ and
|
For more information on virtual environments, see virtualenv_.
|
||||||
virtualenvwrapper_.
|
|
||||||
|
|
||||||
.. _virtualenv: https://pypi.python.org/pypi/virtualenv/
|
.. _virtualenv: https://www.virtualenv.org/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ Here below is how to register ``DummyClusterDataModelCollector`` using pbr_:
|
|||||||
watcher_cluster_data_model_collectors =
|
watcher_cluster_data_model_collectors =
|
||||||
dummy = thirdparty.dummy:DummyClusterDataModelCollector
|
dummy = thirdparty.dummy:DummyClusterDataModelCollector
|
||||||
|
|
||||||
.. _pbr: https://docs.openstack.org/pbr/latest/
|
.. _pbr: http://docs.openstack.org/pbr/latest
|
||||||
|
|
||||||
|
|
||||||
Add new notification endpoints
|
Add new notification endpoints
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ the following::
|
|||||||
(watcher) $ tox -e pep8
|
(watcher) $ tox -e pep8
|
||||||
|
|
||||||
.. _tox: https://tox.readthedocs.org/
|
.. _tox: https://tox.readthedocs.org/
|
||||||
.. _Gerrit: https://review.openstack.org/
|
.. _Gerrit: http://review.openstack.org/
|
||||||
|
|
||||||
You may pass options to the test programs using positional arguments. To run a
|
You may pass options to the test programs using positional arguments. To run a
|
||||||
specific unit test, you can pass extra options to `os-testr`_ after putting
|
specific unit test, you can pass extra options to `os-testr`_ after putting
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ In OpenStack Identity, a :ref:`project <project_definition>` must be owned by a
|
|||||||
specific domain.
|
specific domain.
|
||||||
|
|
||||||
Please, read `the official OpenStack definition of a Project
|
Please, read `the official OpenStack definition of a Project
|
||||||
<https://docs.openstack.org/doc-contrib-guide/common/glossary.html>`_.
|
<http://docs.openstack.org/glossary/content/glossary.html>`_.
|
||||||
|
|
||||||
.. _scoring_engine_definition:
|
.. _scoring_engine_definition:
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ metrics receiver, complex event processor and profiler, optimization processor
|
|||||||
and an action plan applier. This provides a robust framework to realize a wide
|
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
|
range of cloud optimization goals, including the reduction of data center
|
||||||
operating costs, increased system performance via intelligent virtual machine
|
operating costs, increased system performance via intelligent virtual machine
|
||||||
migration, increased energy efficiency and more!
|
migration, increased energy efficiency—and more!
|
||||||
|
|
||||||
Watcher project consists of several source code repositories:
|
Watcher project consists of several source code repositories:
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ optimization algorithms, data metrics and data profilers can be
|
|||||||
developed and inserted into the Watcher framework.
|
developed and inserted into the Watcher framework.
|
||||||
|
|
||||||
Check the documentation for watcher optimization strategies at
|
Check the documentation for watcher optimization strategies at
|
||||||
`Strategies <https://docs.openstack.org/watcher/latest/strategies/index.html>`_.
|
https://docs.openstack.org/watcher/latest/strategies/index.html
|
||||||
|
|
||||||
Check watcher glossary at `Glossary
|
Check watcher glossary at
|
||||||
<https://docs.openstack.org/watcher/latest/glossary.html>`_.
|
https://docs.openstack.org/watcher/latest/glossary.html
|
||||||
|
|
||||||
|
|
||||||
This chapter assumes a working setup of OpenStack following the
|
This chapter assumes a working setup of OpenStack following the
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ Service for the Watcher API
|
|||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
:Author: openstack@lists.launchpad.net
|
:Author: openstack@lists.launchpad.net
|
||||||
|
:Date:
|
||||||
:Copyright: OpenStack Foundation
|
:Copyright: OpenStack Foundation
|
||||||
|
:Version:
|
||||||
:Manual section: 1
|
:Manual section: 1
|
||||||
:Manual group: cloud computing
|
:Manual group: cloud computing
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ Service for the Watcher Applier
|
|||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
:Author: openstack@lists.launchpad.net
|
:Author: openstack@lists.launchpad.net
|
||||||
|
:Date:
|
||||||
:Copyright: OpenStack Foundation
|
:Copyright: OpenStack Foundation
|
||||||
|
:Version:
|
||||||
:Manual section: 1
|
:Manual section: 1
|
||||||
:Manual group: cloud computing
|
:Manual group: cloud computing
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ Service for the Watcher Decision Engine
|
|||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
:Author: openstack@lists.launchpad.net
|
:Author: openstack@lists.launchpad.net
|
||||||
|
:Date:
|
||||||
:Copyright: OpenStack Foundation
|
:Copyright: OpenStack Foundation
|
||||||
|
:Version:
|
||||||
:Manual section: 1
|
:Manual section: 1
|
||||||
:Manual group: cloud computing
|
:Manual group: cloud computing
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``unclassified``
|
**goal**: ``unclassified``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.actuation.Actuator
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.actuation
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``server_consolidation``
|
**goal**: ``server_consolidation``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.basic_consolidation.BasicConsolidation
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.basic_consolidation
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``thermal_optimization``
|
**goal**: ``thermal_optimization``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.outlet_temp_control
|
Outlet (Exhaust Air) temperature is a new thermal telemetry which can be
|
||||||
|
used to measure the host's thermal/workload status. This strategy makes
|
||||||
|
decisions to migrate workloads to the hosts with good thermal condition
|
||||||
|
(lowest outlet temperature) when the outlet temperature of source hosts
|
||||||
|
reach a configurable threshold.
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``saving_energy``
|
**goal**: ``saving_energy``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.saving_energy.SavingEnergy
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.saving_energy
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
@@ -67,13 +67,13 @@ parameter type default description
|
|||||||
Efficacy Indicator
|
Efficacy Indicator
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
None
|
Energy saving strategy efficacy indicator is unclassified.
|
||||||
|
https://github.com/openstack/watcher/blob/master/watcher/decision_engine/goal/goals.py#L215-L218
|
||||||
|
|
||||||
Algorithm
|
Algorithm
|
||||||
---------
|
---------
|
||||||
|
|
||||||
For more information on the Energy Saving Strategy please refer to:
|
For more information on the Energy Saving Strategy please refer to:http://specs.openstack.org/openstack/watcher-specs/specs/pike/implemented/energy-saving-strategy.html
|
||||||
http://specs.openstack.org/openstack/watcher-specs/specs/pike/implemented/energy-saving-strategy.html
|
|
||||||
|
|
||||||
How to use it ?
|
How to use it ?
|
||||||
---------------
|
---------------
|
||||||
@@ -91,10 +91,10 @@ step 2: Create audit to do optimization
|
|||||||
$ openstack optimize audittemplate create \
|
$ openstack optimize audittemplate create \
|
||||||
at1 saving_energy --strategy saving_energy
|
at1 saving_energy --strategy saving_energy
|
||||||
|
|
||||||
$ openstack optimize audit create -a at1 \
|
$ openstack optimize audit create -a at1
|
||||||
-p free_used_percent=20.0
|
|
||||||
|
|
||||||
External Links
|
External Links
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
None
|
*Spec URL*
|
||||||
|
http://specs.openstack.org/openstack/watcher-specs/specs/pike/implemented/energy-saving-strategy.html
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``airflow_optimization``
|
**goal**: ``airflow_optimization``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.uniform_airflow.UniformAirflow
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.uniform_airflow
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``vm_consolidation``
|
**goal**: ``vm_consolidation``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.vm_workload_consolidation.VMWorkloadConsolidation
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.vm_workload_consolidation
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``workload_balancing``
|
**goal**: ``workload_balancing``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.workload_stabilization.WorkloadStabilization
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.workload_stabilization
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``workload_balancing``
|
**goal**: ``workload_balancing``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.workload_balance.WorkloadBalance
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.workload_balance
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Synopsis
|
|||||||
|
|
||||||
**goal**: ``hardware_maintenance``
|
**goal**: ``hardware_maintenance``
|
||||||
|
|
||||||
.. watcher-term:: watcher.decision_engine.strategy.strategies.zone_migration.ZoneMigration
|
.. watcher-term:: watcher.decision_engine.strategy.strategies.zone_migration
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -39,22 +39,6 @@ named ``watcher``, or by using the `OpenStack CLI`_ ``openstack``.
|
|||||||
If you want to deploy Watcher in Horizon, please refer to the `Watcher Horizon
|
If you want to deploy Watcher in Horizon, please refer to the `Watcher Horizon
|
||||||
plugin installation guide`_.
|
plugin installation guide`_.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Notice, that in this guide we'll use `OpenStack CLI`_ as major interface.
|
|
||||||
Nevertheless, you can use `Watcher CLI`_ in the same way. It can be
|
|
||||||
achieved by replacing
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
$ openstack optimize ...
|
|
||||||
|
|
||||||
with
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
$ watcher ...
|
|
||||||
|
|
||||||
.. _`installation guide`: https://docs.openstack.org/python-watcherclient/latest
|
.. _`installation guide`: https://docs.openstack.org/python-watcherclient/latest
|
||||||
.. _`Watcher Horizon plugin installation guide`: https://docs.openstack.org/watcher-dashboard/latest/install/installation.html
|
.. _`Watcher Horizon plugin installation guide`: https://docs.openstack.org/watcher-dashboard/latest/install/installation.html
|
||||||
.. _`OpenStack CLI`: https://docs.openstack.org/python-openstackclient/latest/cli/man/openstack.html
|
.. _`OpenStack CLI`: https://docs.openstack.org/python-openstackclient/latest/cli/man/openstack.html
|
||||||
@@ -67,6 +51,10 @@ watcher binary without options.
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher help
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack help optimize
|
$ openstack help optimize
|
||||||
|
|
||||||
How do I run an audit of my cluster ?
|
How do I run an audit of my cluster ?
|
||||||
@@ -76,6 +64,10 @@ First, you need to find the :ref:`goal <goal_definition>` you want to achieve:
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher goal list
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize goal list
|
$ openstack optimize goal list
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
@@ -89,6 +81,10 @@ An :ref:`audit template <audit_template_definition>` defines an optimization
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher audittemplate create my_first_audit_template <your_goal>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize audittemplate create my_first_audit_template <your_goal>
|
$ openstack optimize audittemplate create my_first_audit_template <your_goal>
|
||||||
|
|
||||||
Although optional, you may want to actually set a specific strategy for your
|
Although optional, you may want to actually set a specific strategy for your
|
||||||
@@ -97,6 +93,10 @@ following command:
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher strategy list --goal <your_goal_uuid_or_name>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize strategy list --goal <your_goal_uuid_or_name>
|
$ openstack optimize strategy list --goal <your_goal_uuid_or_name>
|
||||||
|
|
||||||
You can use the following command to check strategy details including which
|
You can use the following command to check strategy details including which
|
||||||
@@ -104,12 +104,21 @@ parameters of which format it supports:
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher strategy show <your_strategy>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize strategy show <your_strategy>
|
$ openstack optimize strategy show <your_strategy>
|
||||||
|
|
||||||
The command to create your audit template would then be:
|
The command to create your audit template would then be:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher audittemplate create my_first_audit_template <your_goal> \
|
||||||
|
--strategy <your_strategy>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize audittemplate create my_first_audit_template <your_goal> \
|
$ openstack optimize audittemplate create my_first_audit_template <your_goal> \
|
||||||
--strategy <your_strategy>
|
--strategy <your_strategy>
|
||||||
|
|
||||||
@@ -124,6 +133,10 @@ audit) that you want to use.
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher audittemplate list
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize audittemplate list
|
$ openstack optimize audittemplate list
|
||||||
|
|
||||||
- Start an audit based on this :ref:`audit template
|
- Start an audit based on this :ref:`audit template
|
||||||
@@ -131,6 +144,10 @@ audit) that you want to use.
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher audit create -a <your_audit_template>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize audit create -a <your_audit_template>
|
$ openstack optimize audit create -a <your_audit_template>
|
||||||
|
|
||||||
If your_audit_template was created by --strategy <your_strategy>, and it
|
If your_audit_template was created by --strategy <your_strategy>, and it
|
||||||
@@ -139,6 +156,11 @@ format), your can append `-p` to input required parameters:
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher audit create -a <your_audit_template> \
|
||||||
|
-p <your_strategy_para1>=5.5 -p <your_strategy_para2>=hi
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize audit create -a <your_audit_template> \
|
$ openstack optimize audit create -a <your_audit_template> \
|
||||||
-p <your_strategy_para1>=5.5 -p <your_strategy_para2>=hi
|
-p <your_strategy_para1>=5.5 -p <your_strategy_para2>=hi
|
||||||
|
|
||||||
@@ -151,13 +173,19 @@ Input parameter could cause audit creation failure, when:
|
|||||||
Watcher service will compute an :ref:`Action Plan <action_plan_definition>`
|
Watcher service will compute an :ref:`Action Plan <action_plan_definition>`
|
||||||
composed of a list of potential optimization :ref:`actions <action_definition>`
|
composed of a list of potential optimization :ref:`actions <action_definition>`
|
||||||
(instance migration, disabling of a compute node, ...) according to the
|
(instance migration, disabling of a compute node, ...) according to the
|
||||||
:ref:`goal <goal_definition>` to achieve.
|
:ref:`goal <goal_definition>` to achieve. You can see all of the goals
|
||||||
|
available in section ``[watcher_strategies]`` of the Watcher service
|
||||||
|
configuration file.
|
||||||
|
|
||||||
- Wait until the Watcher audit has produced a new :ref:`action plan
|
- Wait until the Watcher audit has produced a new :ref:`action plan
|
||||||
<action_plan_definition>`, and get it:
|
<action_plan_definition>`, and get it:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher actionplan list --audit <the_audit_uuid>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize actionplan list --audit <the_audit_uuid>
|
$ openstack optimize actionplan list --audit <the_audit_uuid>
|
||||||
|
|
||||||
- Have a look on the list of optimization :ref:`actions <action_definition>`
|
- Have a look on the list of optimization :ref:`actions <action_definition>`
|
||||||
@@ -165,6 +193,10 @@ composed of a list of potential optimization :ref:`actions <action_definition>`
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher action list --action-plan <the_action_plan_uuid>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize action list --action-plan <the_action_plan_uuid>
|
$ openstack optimize action list --action-plan <the_action_plan_uuid>
|
||||||
|
|
||||||
Once you have learned how to create an :ref:`Action Plan
|
Once you have learned how to create an :ref:`Action Plan
|
||||||
@@ -175,6 +207,10 @@ cluster:
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher actionplan start <the_action_plan_uuid>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize actionplan start <the_action_plan_uuid>
|
$ openstack optimize actionplan start <the_action_plan_uuid>
|
||||||
|
|
||||||
You can follow the states of the :ref:`actions <action_definition>` by
|
You can follow the states of the :ref:`actions <action_definition>` by
|
||||||
@@ -182,11 +218,19 @@ periodically calling:
|
|||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher action list
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize action list
|
$ openstack optimize action list
|
||||||
|
|
||||||
You can also obtain more detailed information about a specific action:
|
You can also obtain more detailed information about a specific action:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
$ watcher action show <the_action_uuid>
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
$ openstack optimize action show <the_action_uuid>
|
$ openstack optimize action show <the_action_uuid>
|
||||||
|
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
alabaster==0.7.10
|
|
||||||
alembic==0.9.8
|
|
||||||
amqp==2.2.2
|
|
||||||
appdirs==1.4.3
|
|
||||||
APScheduler==3.5.1
|
|
||||||
asn1crypto==0.24.0
|
|
||||||
automaton==1.14.0
|
|
||||||
Babel==2.5.3
|
|
||||||
bandit==1.4.0
|
|
||||||
beautifulsoup4==4.6.0
|
|
||||||
cachetools==2.0.1
|
|
||||||
certifi==2018.1.18
|
|
||||||
cffi==1.11.5
|
|
||||||
chardet==3.0.4
|
|
||||||
cliff==2.11.0
|
|
||||||
cmd2==0.8.1
|
|
||||||
contextlib2==0.5.5
|
|
||||||
coverage==4.5.1
|
|
||||||
croniter==0.3.20
|
|
||||||
cryptography==2.1.4
|
|
||||||
debtcollector==1.19.0
|
|
||||||
decorator==4.2.1
|
|
||||||
deprecation==2.0
|
|
||||||
doc8==0.8.0
|
|
||||||
docutils==0.14
|
|
||||||
dogpile.cache==0.6.5
|
|
||||||
dulwich==0.19.0
|
|
||||||
enum-compat==0.0.2
|
|
||||||
eventlet==0.20.0
|
|
||||||
extras==1.0.0
|
|
||||||
fasteners==0.14.1
|
|
||||||
fixtures==3.0.0
|
|
||||||
flake8==2.5.5
|
|
||||||
freezegun==0.3.10
|
|
||||||
future==0.16.0
|
|
||||||
futurist==1.6.0
|
|
||||||
gitdb2==2.0.3
|
|
||||||
GitPython==2.1.8
|
|
||||||
gnocchiclient==7.0.1
|
|
||||||
greenlet==0.4.13
|
|
||||||
hacking==0.12.0
|
|
||||||
idna==2.6
|
|
||||||
imagesize==1.0.0
|
|
||||||
iso8601==0.1.12
|
|
||||||
Jinja2==2.10
|
|
||||||
jmespath==0.9.3
|
|
||||||
jsonpatch==1.21
|
|
||||||
jsonpointer==2.0
|
|
||||||
jsonschema==2.6.0
|
|
||||||
keystoneauth1==3.4.0
|
|
||||||
keystonemiddleware==4.21.0
|
|
||||||
kombu==4.1.0
|
|
||||||
linecache2==1.0.0
|
|
||||||
logutils==0.3.5
|
|
||||||
lxml==4.1.1
|
|
||||||
Mako==1.0.7
|
|
||||||
MarkupSafe==1.0
|
|
||||||
mccabe==0.2.1
|
|
||||||
mock==2.0.0
|
|
||||||
monotonic==1.4
|
|
||||||
mox3==0.25.0
|
|
||||||
msgpack==0.5.6
|
|
||||||
munch==2.2.0
|
|
||||||
netaddr==0.7.19
|
|
||||||
netifaces==0.10.6
|
|
||||||
networkx==1.11
|
|
||||||
openstackdocstheme==1.20.0
|
|
||||||
openstacksdk==0.12.0
|
|
||||||
os-client-config==1.29.0
|
|
||||||
os-service-types==1.2.0
|
|
||||||
os-testr==1.0.0
|
|
||||||
osc-lib==1.10.0
|
|
||||||
oslo.cache==1.29.0
|
|
||||||
oslo.concurrency==3.26.0
|
|
||||||
oslo.config==5.2.0
|
|
||||||
oslo.context==2.20.0
|
|
||||||
oslo.db==4.35.0
|
|
||||||
oslo.i18n==3.20.0
|
|
||||||
oslo.log==3.37.0
|
|
||||||
oslo.messaging==5.36.0
|
|
||||||
oslo.middleware==3.35.0
|
|
||||||
oslo.policy==1.34.0
|
|
||||||
oslo.reports==1.27.0
|
|
||||||
oslo.serialization==2.25.0
|
|
||||||
oslo.service==1.30.0
|
|
||||||
oslo.utils==3.36.0
|
|
||||||
oslo.versionedobjects==1.32.0
|
|
||||||
oslotest==3.3.0
|
|
||||||
packaging==17.1
|
|
||||||
Paste==2.0.3
|
|
||||||
PasteDeploy==1.5.2
|
|
||||||
pbr==3.1.1
|
|
||||||
pecan==1.2.1
|
|
||||||
pep8==1.5.7
|
|
||||||
pika==0.10.0
|
|
||||||
pika-pool==0.1.3
|
|
||||||
prettytable==0.7.2
|
|
||||||
psutil==5.4.3
|
|
||||||
pycadf==2.7.0
|
|
||||||
pycparser==2.18
|
|
||||||
pyflakes==0.8.1
|
|
||||||
Pygments==2.2.0
|
|
||||||
pyinotify==0.9.6
|
|
||||||
pyOpenSSL==17.5.0
|
|
||||||
pyparsing==2.2.0
|
|
||||||
pyperclip==1.6.0
|
|
||||||
python-ceilometerclient==2.9.0
|
|
||||||
python-cinderclient==3.5.0
|
|
||||||
python-dateutil==2.7.0
|
|
||||||
python-editor==1.0.3
|
|
||||||
python-glanceclient==2.9.1
|
|
||||||
python-ironicclient==2.3.0
|
|
||||||
python-keystoneclient==3.15.0
|
|
||||||
python-mimeparse==1.6.0
|
|
||||||
python-monascaclient==1.10.0
|
|
||||||
python-neutronclient==6.7.0
|
|
||||||
python-novaclient==10.1.0
|
|
||||||
python-openstackclient==3.14.0
|
|
||||||
python-subunit==1.2.0
|
|
||||||
pytz==2018.3
|
|
||||||
PyYAML==3.12
|
|
||||||
reno==2.7.0
|
|
||||||
repoze.lru==0.7
|
|
||||||
requests==2.18.4
|
|
||||||
requestsexceptions==1.4.0
|
|
||||||
restructuredtext-lint==1.1.3
|
|
||||||
rfc3986==1.1.0
|
|
||||||
Routes==2.4.1
|
|
||||||
simplegeneric==0.8.1
|
|
||||||
simplejson==3.13.2
|
|
||||||
six==1.11.0
|
|
||||||
smmap2==2.0.3
|
|
||||||
snowballstemmer==1.2.1
|
|
||||||
Sphinx==1.6.5
|
|
||||||
sphinxcontrib-httpdomain==1.6.1
|
|
||||||
sphinxcontrib-pecanwsme==0.8.0
|
|
||||||
sphinxcontrib-websupport==1.0.1
|
|
||||||
SQLAlchemy==1.2.5
|
|
||||||
sqlalchemy-migrate==0.11.0
|
|
||||||
sqlparse==0.2.4
|
|
||||||
statsd==3.2.2
|
|
||||||
stestr==2.0.0
|
|
||||||
stevedore==1.28.0
|
|
||||||
taskflow==3.1.0
|
|
||||||
Tempita==0.5.2
|
|
||||||
tenacity==4.9.0
|
|
||||||
testrepository==0.0.20
|
|
||||||
testresources==2.0.1
|
|
||||||
testscenarios==0.5.0
|
|
||||||
testtools==2.3.0
|
|
||||||
traceback2==1.4.0
|
|
||||||
tzlocal==1.5.1
|
|
||||||
ujson==1.35
|
|
||||||
unittest2==1.1.0
|
|
||||||
urllib3==1.22
|
|
||||||
vine==1.1.4
|
|
||||||
voluptuous==0.11.1
|
|
||||||
waitress==1.1.0
|
|
||||||
warlock==1.3.0
|
|
||||||
WebOb==1.7.4
|
|
||||||
WebTest==2.0.29
|
|
||||||
wrapt==1.10.11
|
|
||||||
WSME==0.9.2
|
|
||||||
15
playbooks/legacy/watcher-tempest-base-multinode/post.yaml
Normal file
15
playbooks/legacy/watcher-tempest-base-multinode/post.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
- hosts: primary
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||||
|
synchronize:
|
||||||
|
src: '{{ ansible_user_dir }}/workspace/'
|
||||||
|
dest: '{{ zuul.executor.log_root }}'
|
||||||
|
mode: pull
|
||||||
|
copy_links: true
|
||||||
|
verify_host: true
|
||||||
|
rsync_opts:
|
||||||
|
- --include=/logs/**
|
||||||
|
- --include=*/
|
||||||
|
- --exclude=*
|
||||||
|
- --prune-empty-dirs
|
||||||
67
playbooks/legacy/watcher-tempest-base-multinode/run.yaml
Normal file
67
playbooks/legacy/watcher-tempest-base-multinode/run.yaml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
- hosts: primary
|
||||||
|
name: Legacy Watcher tempest base multinode
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
- name: Ensure legacy workspace directory
|
||||||
|
file:
|
||||||
|
path: '{{ ansible_user_dir }}/workspace'
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
cat > clonemap.yaml << EOF
|
||||||
|
clonemap:
|
||||||
|
- name: openstack/devstack-gate
|
||||||
|
dest: devstack-gate
|
||||||
|
EOF
|
||||||
|
/usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
|
||||||
|
https://opendev.org \
|
||||||
|
openstack/devstack-gate
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
cat << 'EOF' >>"/tmp/dg-local.conf"
|
||||||
|
[[local|localrc]]
|
||||||
|
TEMPEST_PLUGINS='/opt/stack/new/watcher-tempest-plugin'
|
||||||
|
enable_plugin ceilometer https://opendev.org/openstack/ceilometer
|
||||||
|
# Enable watcher devstack plugin.
|
||||||
|
enable_plugin watcher https://opendev.org/openstack/watcher
|
||||||
|
|
||||||
|
EOF
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
export DEVSTACK_SUBNODE_CONFIG=" "
|
||||||
|
export PYTHONUNBUFFERED=true
|
||||||
|
export DEVSTACK_GATE_TEMPEST=1
|
||||||
|
export DEVSTACK_GATE_NEUTRON=1
|
||||||
|
export DEVSTACK_GATE_TOPOLOGY="multinode"
|
||||||
|
export PROJECTS="openstack/watcher $PROJECTS"
|
||||||
|
export PROJECTS="openstack/python-watcherclient $PROJECTS"
|
||||||
|
export PROJECTS="openstack/watcher-tempest-plugin $PROJECTS"
|
||||||
|
|
||||||
|
export DEVSTACK_GATE_TEMPEST_REGEX="watcher_tempest_plugin"
|
||||||
|
|
||||||
|
export BRANCH_OVERRIDE=default
|
||||||
|
if [ "$BRANCH_OVERRIDE" != "default" ] ; then
|
||||||
|
export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
|
||||||
|
./safe-devstack-vm-gate-wrap.sh
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
80
playbooks/legacy/watcherclient-tempest-functional/post.yaml
Normal file
80
playbooks/legacy/watcherclient-tempest-functional/post.yaml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
- hosts: primary
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||||
|
synchronize:
|
||||||
|
src: '{{ ansible_user_dir }}/workspace/'
|
||||||
|
dest: '{{ zuul.executor.log_root }}'
|
||||||
|
mode: pull
|
||||||
|
copy_links: true
|
||||||
|
verify_host: true
|
||||||
|
rsync_opts:
|
||||||
|
- --include=**/*nose_results.html
|
||||||
|
- --include=*/
|
||||||
|
- --exclude=*
|
||||||
|
- --prune-empty-dirs
|
||||||
|
|
||||||
|
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||||
|
synchronize:
|
||||||
|
src: '{{ ansible_user_dir }}/workspace/'
|
||||||
|
dest: '{{ zuul.executor.log_root }}'
|
||||||
|
mode: pull
|
||||||
|
copy_links: true
|
||||||
|
verify_host: true
|
||||||
|
rsync_opts:
|
||||||
|
- --include=**/*testr_results.html.gz
|
||||||
|
- --include=*/
|
||||||
|
- --exclude=*
|
||||||
|
- --prune-empty-dirs
|
||||||
|
|
||||||
|
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||||
|
synchronize:
|
||||||
|
src: '{{ ansible_user_dir }}/workspace/'
|
||||||
|
dest: '{{ zuul.executor.log_root }}'
|
||||||
|
mode: pull
|
||||||
|
copy_links: true
|
||||||
|
verify_host: true
|
||||||
|
rsync_opts:
|
||||||
|
- --include=/.testrepository/tmp*
|
||||||
|
- --include=*/
|
||||||
|
- --exclude=*
|
||||||
|
- --prune-empty-dirs
|
||||||
|
|
||||||
|
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||||
|
synchronize:
|
||||||
|
src: '{{ ansible_user_dir }}/workspace/'
|
||||||
|
dest: '{{ zuul.executor.log_root }}'
|
||||||
|
mode: pull
|
||||||
|
copy_links: true
|
||||||
|
verify_host: true
|
||||||
|
rsync_opts:
|
||||||
|
- --include=**/*testrepository.subunit.gz
|
||||||
|
- --include=*/
|
||||||
|
- --exclude=*
|
||||||
|
- --prune-empty-dirs
|
||||||
|
|
||||||
|
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||||
|
synchronize:
|
||||||
|
src: '{{ ansible_user_dir }}/workspace/'
|
||||||
|
dest: '{{ zuul.executor.log_root }}/tox'
|
||||||
|
mode: pull
|
||||||
|
copy_links: true
|
||||||
|
verify_host: true
|
||||||
|
rsync_opts:
|
||||||
|
- --include=/.tox/*/log/*
|
||||||
|
- --include=*/
|
||||||
|
- --exclude=*
|
||||||
|
- --prune-empty-dirs
|
||||||
|
|
||||||
|
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||||
|
synchronize:
|
||||||
|
src: '{{ ansible_user_dir }}/workspace/'
|
||||||
|
dest: '{{ zuul.executor.log_root }}'
|
||||||
|
mode: pull
|
||||||
|
copy_links: true
|
||||||
|
verify_host: true
|
||||||
|
rsync_opts:
|
||||||
|
- --include=/logs/**
|
||||||
|
- --include=*/
|
||||||
|
- --exclude=*
|
||||||
|
- --prune-empty-dirs
|
||||||
64
playbooks/legacy/watcherclient-tempest-functional/run.yaml
Normal file
64
playbooks/legacy/watcherclient-tempest-functional/run.yaml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
- hosts: all
|
||||||
|
name: Legacy watcherclient-dsvm-functional
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
- name: Ensure legacy workspace directory
|
||||||
|
file:
|
||||||
|
path: '{{ ansible_user_dir }}/workspace'
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
cat > clonemap.yaml << EOF
|
||||||
|
clonemap:
|
||||||
|
- name: openstack/devstack-gate
|
||||||
|
dest: devstack-gate
|
||||||
|
EOF
|
||||||
|
/usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
|
||||||
|
https://opendev.org \
|
||||||
|
openstack/devstack-gate
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
cat << 'EOF' >>"/tmp/dg-local.conf"
|
||||||
|
[[local|localrc]]
|
||||||
|
enable_plugin watcher https://opendev.org/openstack/watcher
|
||||||
|
|
||||||
|
EOF
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
|
|
||||||
|
- shell:
|
||||||
|
cmd: |
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
ENABLED_SERVICES=tempest
|
||||||
|
ENABLED_SERVICES+=,watcher-api,watcher-decision-engine,watcher-applier
|
||||||
|
export ENABLED_SERVICES
|
||||||
|
|
||||||
|
export PYTHONUNBUFFERED=true
|
||||||
|
export BRANCH_OVERRIDE=default
|
||||||
|
export PROJECTS="openstack/watcher $PROJECTS"
|
||||||
|
export DEVSTACK_PROJECT_FROM_GIT=python-watcherclient
|
||||||
|
if [ "$BRANCH_OVERRIDE" != "default" ] ; then
|
||||||
|
export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
|
||||||
|
fi
|
||||||
|
function post_test_hook {
|
||||||
|
# Configure and run functional tests
|
||||||
|
$BASE/new/python-watcherclient/watcherclient/tests/functional/hooks/post_test_hook.sh
|
||||||
|
}
|
||||||
|
export -f post_test_hook
|
||||||
|
|
||||||
|
cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
|
||||||
|
./safe-devstack-vm-gate-wrap.sh
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: '{{ ansible_user_dir }}/workspace'
|
||||||
|
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
- hosts: all
|
|
||||||
# This is the default strategy, however since orchestrate-devstack requires
|
|
||||||
# "linear", it is safer to enforce it in case this is running in an
|
|
||||||
# environment configured with a different default strategy.
|
|
||||||
strategy: linear
|
|
||||||
roles:
|
|
||||||
- orchestrate-devstack
|
|
||||||
|
|
||||||
- hosts: tempest
|
|
||||||
roles:
|
|
||||||
- setup-tempest-run-dir
|
|
||||||
- setup-tempest-data-dir
|
|
||||||
- acl-devstack-files
|
|
||||||
- run-tempest
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
- hosts: all
|
|
||||||
roles:
|
|
||||||
- add-hostnames-to-hosts
|
|
||||||
@@ -29,7 +29,7 @@ Useful links
|
|||||||
|
|
||||||
* How to install: https://docs.openstack.org/rally/latest/install_and_upgrade/install.html
|
* How to install: https://docs.openstack.org/rally/latest/install_and_upgrade/install.html
|
||||||
|
|
||||||
* How to set Rally up and launch your first scenario: https://rally.readthedocs.io/en/latest/quick_start/tutorial/step_1_setting_up_env_and_running_benchmark_from_samples.html
|
* How to set Rally up and launch your first scenario: https://rally.readthedocs.io/en/latest/tutorial/step_1_setting_up_env_and_running_benchmark_from_samples.html
|
||||||
|
|
||||||
* More about Rally: https://docs.openstack.org/rally/latest/
|
* More about Rally: https://docs.openstack.org/rally/latest/
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
features:
|
|
||||||
- Audits have 'name' field now, that is more friendly to end users.
|
|
||||||
Audit's name can't exceed 63 characters.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
features:
|
|
||||||
- Watcher has a whole scope of the cluster, when building
|
|
||||||
compute CDM which includes all instances.
|
|
||||||
It filters excluded instances when migration during the
|
|
||||||
audit.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
features:
|
|
||||||
- Watcher got an ability to calculate multiple global efficacy indicators
|
|
||||||
during audit's execution. Now global efficacy can be calculated for many
|
|
||||||
resource types (like volumes, instances, network) if strategy supports
|
|
||||||
efficacy indicators.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
features:
|
|
||||||
- Added notifications about cancelling of action plan.
|
|
||||||
Now event based plugins know when action plan cancel
|
|
||||||
started and completed.
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
features:
|
|
||||||
- |
|
|
||||||
Instance cold migration logic is now replaced with using Nova migrate
|
|
||||||
Server(migrate Action) API which has host option since v2.56.
|
|
||||||
upgrade:
|
|
||||||
- |
|
|
||||||
Nova API version is now set to 2.56 by default. This needs the migrate
|
|
||||||
action of migration type cold with destination_node parameter to work.
|
|
||||||
fixes:
|
|
||||||
- |
|
|
||||||
The migrate action of migration type cold with destination_node parameter
|
|
||||||
was fixed. Before fixing, it booted an instance in the service project
|
|
||||||
as a migrated instance.
|
|
||||||
@@ -21,7 +21,6 @@ Contents:
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
unreleased
|
unreleased
|
||||||
queens
|
|
||||||
pike
|
pike
|
||||||
ocata
|
ocata
|
||||||
newton
|
newton
|
||||||
|
|||||||
@@ -1,426 +0,0 @@
|
|||||||
# Andi Chandler <andi@gowling.com>, 2017. #zanata
|
|
||||||
# Andi Chandler <andi@gowling.com>, 2018. #zanata
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: watcher\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2018-02-28 12:27+0000\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"PO-Revision-Date: 2018-02-16 07:20+0000\n"
|
|
||||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
|
||||||
"Language-Team: English (United Kingdom)\n"
|
|
||||||
"Language: en_GB\n"
|
|
||||||
"X-Generator: Zanata 4.3.3\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
|
||||||
|
|
||||||
msgid "0.29.0"
|
|
||||||
msgstr "0.29.0"
|
|
||||||
|
|
||||||
msgid "0.34.0"
|
|
||||||
msgstr "0.34.0"
|
|
||||||
|
|
||||||
msgid "1.0.0"
|
|
||||||
msgstr "1.0.0"
|
|
||||||
|
|
||||||
msgid "1.1.0"
|
|
||||||
msgstr "1.1.0"
|
|
||||||
|
|
||||||
msgid "1.3.0"
|
|
||||||
msgstr "1.3.0"
|
|
||||||
|
|
||||||
msgid "1.4.0"
|
|
||||||
msgstr "1.4.0"
|
|
||||||
|
|
||||||
msgid "1.4.1"
|
|
||||||
msgstr "1.4.1"
|
|
||||||
|
|
||||||
msgid "1.5.0"
|
|
||||||
msgstr "1.5.0"
|
|
||||||
|
|
||||||
msgid "1.6.0"
|
|
||||||
msgstr "1.6.0"
|
|
||||||
|
|
||||||
msgid "1.7.0"
|
|
||||||
msgstr "1.7.0"
|
|
||||||
|
|
||||||
msgid "Add a service supervisor to watch Watcher deamons."
|
|
||||||
msgstr "Add a service supervisor to watch Watcher daemons."
|
|
||||||
|
|
||||||
msgid "Add action for compute node power on/off"
|
|
||||||
msgstr "Add action for compute node power on/off"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Add description property for dynamic action. Admin can see detail "
|
|
||||||
"information of any specify action."
|
|
||||||
msgstr ""
|
|
||||||
"Add description property for dynamic action. Admin can see detail "
|
|
||||||
"information of any specify action."
|
|
||||||
|
|
||||||
msgid "Add notifications related to Action object."
|
|
||||||
msgstr "Add notifications related to Action object."
|
|
||||||
|
|
||||||
msgid "Add notifications related to Action plan object."
|
|
||||||
msgstr "Add notifications related to Action plan object."
|
|
||||||
|
|
||||||
msgid "Add notifications related to Audit object."
|
|
||||||
msgstr "Add notifications related to Audit object."
|
|
||||||
|
|
||||||
msgid "Add notifications related to Service object."
|
|
||||||
msgstr "Add notifications related to Service object."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Add superseded state for an action plan if the cluster data model has "
|
|
||||||
"changed after it has been created."
|
|
||||||
msgstr ""
|
|
||||||
"Add superseded state for an action plan if the cluster data model has "
|
|
||||||
"changed after it has been created."
|
|
||||||
|
|
||||||
msgid "Added SUSPENDED audit state"
|
|
||||||
msgstr "Added SUSPENDED audit state"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a generic scoring engine module, which will standarize interactions "
|
|
||||||
"with scoring engines through the common API. It is possible to use the "
|
|
||||||
"scoring engine by different Strategies, which improve the code and data "
|
|
||||||
"model re-use."
|
|
||||||
msgstr ""
|
|
||||||
"Added a generic scoring engine module, which will standardise interactions "
|
|
||||||
"with scoring engines through the common API. It is possible to use the "
|
|
||||||
"scoring engine by different Strategies, which improve the code and data "
|
|
||||||
"model re-use."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a new strategy based on the airflow of servers. This strategy makes "
|
|
||||||
"decisions to migrate VMs to make the airflow uniform."
|
|
||||||
msgstr ""
|
|
||||||
"Added a new strategy based on the airflow of servers. This strategy makes "
|
|
||||||
"decisions to migrate VMs to make the airflow uniform."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a standard way to both declare and fetch configuration options so that "
|
|
||||||
"whenever the administrator generates the Watcher configuration sample file, "
|
|
||||||
"it contains the configuration options of the plugins that are currently "
|
|
||||||
"available."
|
|
||||||
msgstr ""
|
|
||||||
"Added a standard way to both declare and fetch configuration options so that "
|
|
||||||
"whenever the administrator generates the Watcher configuration sample file, "
|
|
||||||
"it contains the configuration options of the plugins that are currently "
|
|
||||||
"available."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a strategy based on the VM workloads of hypervisors. This strategy "
|
|
||||||
"makes decisions to migrate workloads to make the total VM workloads of each "
|
|
||||||
"hypervisor balanced, when the total VM workloads of hypervisor reaches "
|
|
||||||
"threshold."
|
|
||||||
msgstr ""
|
|
||||||
"Added a strategy based on the VM workloads of hypervisors. This strategy "
|
|
||||||
"makes decisions to migrate workloads to make the total VM workloads of each "
|
|
||||||
"hypervisor balanced, when the total VM workloads of hypervisor reaches "
|
|
||||||
"threshold."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a strategy that monitors if there is a higher load on some hosts "
|
|
||||||
"compared to other hosts in the cluster and re-balances the work across hosts "
|
|
||||||
"to minimize the standard deviation of the loads in the cluster."
|
|
||||||
msgstr ""
|
|
||||||
"Added a strategy that monitors if there is a higher load on some hosts "
|
|
||||||
"compared to other hosts in the cluster and re-balances the work across hosts "
|
|
||||||
"to minimise the standard deviation of the loads in the cluster."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a way to add a new action without having to amend the source code of "
|
|
||||||
"the default planner."
|
|
||||||
msgstr ""
|
|
||||||
"Added a way to add a new action without having to amend the source code of "
|
|
||||||
"the default planner."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a way to check state of strategy before audit's execution. "
|
|
||||||
"Administrator can use \"watcher strategy state <strategy_name>\" command to "
|
|
||||||
"get information about metrics' availability, datasource's availability and "
|
|
||||||
"CDM's availability."
|
|
||||||
msgstr ""
|
|
||||||
"Added a way to check state of strategy before audit's execution. "
|
|
||||||
"Administrator can use \"watcher strategy state <strategy_name>\" command to "
|
|
||||||
"get information about metrics' availability, datasource's availability and "
|
|
||||||
"CDM's availability."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a way to compare the efficacy of different strategies for a give "
|
|
||||||
"optimization goal."
|
|
||||||
msgstr ""
|
|
||||||
"Added a way to compare the efficacy of different strategies for a give "
|
|
||||||
"optimisation goal."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a way to create periodic audit to be able to optimize continuously the "
|
|
||||||
"cloud infrastructure."
|
|
||||||
msgstr ""
|
|
||||||
"Added a way to create periodic audit to be able to continuously optimise the "
|
|
||||||
"cloud infrastructure."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added a way to return the of available goals depending on which strategies "
|
|
||||||
"have been deployed on the node where the decison engine is running."
|
|
||||||
msgstr ""
|
|
||||||
"Added a way to return the of available goals depending on which strategies "
|
|
||||||
"have been deployed on the node where the decision engine is running."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added an in-memory cache of the cluster model built up and kept fresh via "
|
|
||||||
"notifications from services of interest in addition to periodic syncing "
|
|
||||||
"logic."
|
|
||||||
msgstr ""
|
|
||||||
"Added an in-memory cache of the cluster model built up and kept fresh via "
|
|
||||||
"notifications from services of interest in addition to periodic syncing "
|
|
||||||
"logic."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added binding between apscheduler job and Watcher decision engine service. "
|
|
||||||
"It will allow to provide HA support in the future."
|
|
||||||
msgstr ""
|
|
||||||
"Added binding between apscheduler job and Watcher decision engine service. "
|
|
||||||
"It will allow to provide HA support in the future."
|
|
||||||
|
|
||||||
msgid "Added cinder cluster data model"
|
|
||||||
msgstr "Added cinder cluster data model"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added gnocchi support as data source for metrics. Administrator can change "
|
|
||||||
"data source for each strategy using config file."
|
|
||||||
msgstr ""
|
|
||||||
"Added Gnocchi support as data source for metrics. Administrator can change "
|
|
||||||
"data source for each strategy using config file."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added notifications about cancelling of action plan. Now event based plugins "
|
|
||||||
"know when action plan cancel started and completed."
|
|
||||||
msgstr ""
|
|
||||||
"Added notifications about cancelling of action plan. Now event based plugins "
|
|
||||||
"know when action plan cancel started and completed."
|
|
||||||
|
|
||||||
msgid "Added policies to handle user rights to access Watcher API."
|
|
||||||
msgstr "Added policies to handle user rights to access Watcher API."
|
|
||||||
|
|
||||||
msgid "Added storage capacity balance strategy."
|
|
||||||
msgstr "Added storage capacity balance strategy."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added strategy \"Zone migration\" and it's goal \"Hardware maintenance\". "
|
|
||||||
"The strategy migrates many instances and volumes efficiently with minimum "
|
|
||||||
"downtime automatically."
|
|
||||||
msgstr ""
|
|
||||||
"Added strategy \"Zone migration\" and it's goal \"Hardware maintenance\". "
|
|
||||||
"The strategy migrates many instances and volumes efficiently with minimum "
|
|
||||||
"downtime automatically."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added strategy to identify and migrate a Noisy Neighbor - a low priority VM "
|
|
||||||
"that negatively affects peformance of a high priority VM by over utilizing "
|
|
||||||
"Last Level Cache."
|
|
||||||
msgstr ""
|
|
||||||
"Added strategy to identify and migrate a Noisy Neighbour - a low priority VM "
|
|
||||||
"that negatively affects performance of a high priority VM by over utilising "
|
|
||||||
"Last Level Cache."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Added the functionality to filter out instances which have metadata field "
|
|
||||||
"'optimize' set to False. For now, this is only available for the "
|
|
||||||
"basic_consolidation strategy (if \"check_optimize_metadata\" configuration "
|
|
||||||
"option is enabled)."
|
|
||||||
msgstr ""
|
|
||||||
"Added the functionality to filter out instances which have metadata field "
|
|
||||||
"'optimize' set to False. For now, this is only available for the "
|
|
||||||
"basic_consolidation strategy (if \"check_optimize_metadata\" configuration "
|
|
||||||
"option is enabled)."
|
|
||||||
|
|
||||||
msgid "Added using of JSONSchema instead of voluptuous to validate Actions."
|
|
||||||
msgstr "Added using of JSONSchema instead of voluptuous to validate Actions."
|
|
||||||
|
|
||||||
msgid "Added volume migrate action"
|
|
||||||
msgstr "Added volume migrate action"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Adds audit scoper for storage data model, now watcher users can specify "
|
|
||||||
"audit scope for storage CDM in the same manner as compute scope."
|
|
||||||
msgstr ""
|
|
||||||
"Adds audit scoper for storage data model, now watcher users can specify "
|
|
||||||
"audit scope for storage CDM in the same manner as compute scope."
|
|
||||||
|
|
||||||
msgid "Adds baremetal data model in Watcher"
|
|
||||||
msgstr "Adds baremetal data model in Watcher"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Allow decision engine to pass strategy parameters, like optimization "
|
|
||||||
"threshold, to selected strategy, also strategy to provide parameters info to "
|
|
||||||
"end user."
|
|
||||||
msgstr ""
|
|
||||||
"Allow decision engine to pass strategy parameters, like optimisation "
|
|
||||||
"threshold, to selected strategy, also strategy to provide parameters info to "
|
|
||||||
"end user."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Audits have 'name' field now, that is more friendly to end users. Audit's "
|
|
||||||
"name can't exceed 63 characters."
|
|
||||||
msgstr ""
|
|
||||||
"Audits have 'name' field now, that is more friendly to end users. Audit's "
|
|
||||||
"name can't exceed 63 characters."
|
|
||||||
|
|
||||||
msgid "Centralize all configuration options for Watcher."
|
|
||||||
msgstr "Centralise all configuration options for Watcher."
|
|
||||||
|
|
||||||
msgid "Contents:"
|
|
||||||
msgstr "Contents:"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Copy all audit templates parameters into audit instead of having a reference "
|
|
||||||
"to the audit template."
|
|
||||||
msgstr ""
|
|
||||||
"Copy all audit templates parameters into audit instead of having a reference "
|
|
||||||
"to the audit template."
|
|
||||||
|
|
||||||
msgid "Current Series Release Notes"
|
|
||||||
msgstr "Current Series Release Notes"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Each CDM collector can have its own CDM scoper now. This changed Scope JSON "
|
|
||||||
"schema definition for the audit template POST data. Please see audit "
|
|
||||||
"template create help message in python-watcherclient."
|
|
||||||
msgstr ""
|
|
||||||
"Each CDM collector can have its own CDM scoper now. This changed Scope JSON "
|
|
||||||
"schema definition for the audit template POST data. Please see audit "
|
|
||||||
"template create help message in python-watcherclient."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Enhancement of vm_workload_consolidation strategy by using 'memory.resident' "
|
|
||||||
"metric in place of 'memory.usage', as memory.usage shows the memory usage "
|
|
||||||
"inside guest-os and memory.resident represents volume of RAM used by "
|
|
||||||
"instance on host machine."
|
|
||||||
msgstr ""
|
|
||||||
"Enhancement of vm_workload_consolidation strategy by using 'memory.resident' "
|
|
||||||
"metric in place of 'memory.usage', as memory.usage shows the memory usage "
|
|
||||||
"inside guest-os and memory.resident represents volume of RAM used by "
|
|
||||||
"instance on host machine."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Existing workload_balance strategy based on the VM workloads of CPU. This "
|
|
||||||
"feature improves the strategy. By the input parameter \"metrics\", it makes "
|
|
||||||
"decision to migrate a VM base on CPU or memory utilization."
|
|
||||||
msgstr ""
|
|
||||||
"Existing workload_balance strategy based on the VM workloads of CPU. This "
|
|
||||||
"feature improves the strategy. By the input parameter \"metrics\", it makes "
|
|
||||||
"decision to migrate a VM base on CPU or memory utilisation."
|
|
||||||
|
|
||||||
msgid "New Features"
|
|
||||||
msgstr "New Features"
|
|
||||||
|
|
||||||
msgid "Newton Series Release Notes"
|
|
||||||
msgstr "Newton Series Release Notes"
|
|
||||||
|
|
||||||
msgid "Ocata Series Release Notes"
|
|
||||||
msgstr "Ocata Series Release Notes"
|
|
||||||
|
|
||||||
msgid "Pike Series Release Notes"
|
|
||||||
msgstr "Pike Series Release Notes"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Provide a notification mechanism into Watcher that supports versioning. "
|
|
||||||
"Whenever a Watcher object is created, updated or deleted, a versioned "
|
|
||||||
"notification will, if it's relevant, be automatically sent to notify in "
|
|
||||||
"order to allow an event-driven style of architecture within Watcher. "
|
|
||||||
"Moreover, it will also give other services and/or 3rd party softwares (e.g. "
|
|
||||||
"monitoring solutions or rules engines) the ability to react to such events."
|
|
||||||
msgstr ""
|
|
||||||
"Provide a notification mechanism into Watcher that supports versioning. "
|
|
||||||
"Whenever a Watcher object is created, updated or deleted, a versioned "
|
|
||||||
"notification will, if it's relevant, be automatically sent to notify in "
|
|
||||||
"order to allow an event-driven style of architecture within Watcher. "
|
|
||||||
"Moreover, it will also give other services and/or 3rd party software (e.g. "
|
|
||||||
"monitoring solutions or rules engines) the ability to react to such events."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Provides a generic way to define the scope of an audit. The set of audited "
|
|
||||||
"resources will be called \"Audit scope\" and will be defined in each audit "
|
|
||||||
"template (which contains the audit settings)."
|
|
||||||
msgstr ""
|
|
||||||
"Provides a generic way to define the scope of an audit. The set of audited "
|
|
||||||
"resources will be called \"Audit scope\" and will be defined in each audit "
|
|
||||||
"template (which contains the audit settings)."
|
|
||||||
|
|
||||||
msgid "Queens Series Release Notes"
|
|
||||||
msgstr "Queens Series Release Notes"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"The graph model describes how VMs are associated to compute hosts. This "
|
|
||||||
"allows for seeing relationships upfront between the entities and hence can "
|
|
||||||
"be used to identify hot/cold spots in the data center and influence a "
|
|
||||||
"strategy decision."
|
|
||||||
msgstr ""
|
|
||||||
"The graph model describes how VMs are associated to compute hosts. This "
|
|
||||||
"allows for seeing relationships upfront between the entities and hence can "
|
|
||||||
"be used to identify hot/cold spots in the data centre and influence a "
|
|
||||||
"strategy decision."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"There is new ability to create Watcher continuous audits with cron interval. "
|
|
||||||
"It means you may use, for example, optional argument '--interval \"\\*/5 \\* "
|
|
||||||
"\\* \\* \\*\"' to launch audit every 5 minutes. These jobs are executed on a "
|
|
||||||
"best effort basis and therefore, we recommend you to use a minimal cron "
|
|
||||||
"interval of at least one minute."
|
|
||||||
msgstr ""
|
|
||||||
"There is new ability to create Watcher continuous audits with cron interval. "
|
|
||||||
"It means you may use, for example, optional argument '--interval \"\\*/5 \\* "
|
|
||||||
"\\* \\* \\*\"' to launch audit every 5 minutes. These jobs are executed on a "
|
|
||||||
"best effort basis and therefore, we recommend you to use a minimal cron "
|
|
||||||
"interval of at least one minute."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Watcher can continuously optimize the OpenStack cloud for a specific "
|
|
||||||
"strategy or goal by triggering an audit periodically which generates an "
|
|
||||||
"action plan and run it automatically."
|
|
||||||
msgstr ""
|
|
||||||
"Watcher can continuously optimise the OpenStack cloud for a specific "
|
|
||||||
"strategy or goal by triggering an audit periodically which generates an "
|
|
||||||
"action plan and run it automatically."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Watcher can now run specific actions in parallel improving the performances "
|
|
||||||
"dramatically when executing an action plan."
|
|
||||||
msgstr ""
|
|
||||||
"Watcher can now run specific actions in parallel improving the performance "
|
|
||||||
"dramatically when executing an action plan."
|
|
||||||
|
|
||||||
msgid "Watcher database can now be upgraded thanks to Alembic."
|
|
||||||
msgstr "Watcher database can now be upgraded thanks to Alembic."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Watcher got an ability to calculate multiple global efficacy indicators "
|
|
||||||
"during audit's execution. Now global efficacy can be calculated for many "
|
|
||||||
"resource types (like volumes, instances, network) if strategy supports "
|
|
||||||
"efficacy indicators."
|
|
||||||
msgstr ""
|
|
||||||
"Watcher got an ability to calculate multiple global efficacy indicators "
|
|
||||||
"during audit's execution. Now global efficacy can be calculated for many "
|
|
||||||
"resource types (like volumes, instances, network) if strategy supports "
|
|
||||||
"efficacy indicators."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Watcher supports multiple metrics backend and relies on Ceilometer and "
|
|
||||||
"Monasca."
|
|
||||||
msgstr ""
|
|
||||||
"Watcher supports multiple metrics backend and relies on Ceilometer and "
|
|
||||||
"Monasca."
|
|
||||||
|
|
||||||
msgid "Welcome to watcher's Release Notes documentation!"
|
|
||||||
msgstr "Welcome to watcher's Release Notes documentation!"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"all Watcher objects have been refactored to support OVO (oslo."
|
|
||||||
"versionedobjects) which was a prerequisite step in order to implement "
|
|
||||||
"versioned notifications."
|
|
||||||
msgstr ""
|
|
||||||
"all Watcher objects have been refactored to support OVO (oslo."
|
|
||||||
"versionedobjects) which was a prerequisite step in order to implement "
|
|
||||||
"versioned notifications."
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
===================================
|
|
||||||
Queens Series Release Notes
|
|
||||||
===================================
|
|
||||||
|
|
||||||
.. release-notes::
|
|
||||||
:branch: stable/queens
|
|
||||||
@@ -5,14 +5,14 @@
|
|||||||
apscheduler>=3.0.5 # MIT License
|
apscheduler>=3.0.5 # MIT License
|
||||||
enum34>=1.0.4;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
|
enum34>=1.0.4;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
|
||||||
jsonpatch!=1.20,>=1.16 # BSD
|
jsonpatch!=1.20,>=1.16 # BSD
|
||||||
keystoneauth1>=3.4.0 # Apache-2.0
|
keystoneauth1>=3.3.0 # Apache-2.0
|
||||||
jsonschema<3.0.0,>=2.6.0 # MIT
|
jsonschema<3.0.0,>=2.6.0 # MIT
|
||||||
keystonemiddleware>=4.17.0 # Apache-2.0
|
keystonemiddleware>=4.17.0 # Apache-2.0
|
||||||
lxml!=3.7.0,>=3.4.1 # BSD
|
lxml!=3.7.0,>=3.4.1 # BSD
|
||||||
croniter>=0.3.4 # MIT License
|
croniter>=0.3.4 # MIT License
|
||||||
oslo.concurrency>=3.26.0 # Apache-2.0
|
oslo.concurrency>=3.25.0 # Apache-2.0
|
||||||
oslo.cache>=1.26.0 # Apache-2.0
|
oslo.cache>=1.26.0 # Apache-2.0
|
||||||
oslo.config>=5.2.0 # Apache-2.0
|
oslo.config>=5.1.0 # Apache-2.0
|
||||||
oslo.context>=2.19.2 # Apache-2.0
|
oslo.context>=2.19.2 # Apache-2.0
|
||||||
oslo.db>=4.27.0 # Apache-2.0
|
oslo.db>=4.27.0 # Apache-2.0
|
||||||
oslo.i18n>=3.15.3 # Apache-2.0
|
oslo.i18n>=3.15.3 # Apache-2.0
|
||||||
@@ -35,10 +35,10 @@ python-cinderclient>=3.3.0 # Apache-2.0
|
|||||||
python-glanceclient>=2.8.0 # Apache-2.0
|
python-glanceclient>=2.8.0 # Apache-2.0
|
||||||
python-keystoneclient>=3.8.0 # Apache-2.0
|
python-keystoneclient>=3.8.0 # Apache-2.0
|
||||||
python-monascaclient>=1.7.0 # Apache-2.0
|
python-monascaclient>=1.7.0 # Apache-2.0
|
||||||
python-neutronclient>=6.7.0 # Apache-2.0
|
python-neutronclient>=6.3.0 # Apache-2.0
|
||||||
python-novaclient>=9.1.0 # Apache-2.0
|
python-novaclient>=9.1.0 # Apache-2.0
|
||||||
python-openstackclient>=3.12.0 # Apache-2.0
|
python-openstackclient>=3.12.0 # Apache-2.0
|
||||||
python-ironicclient>=2.3.0 # Apache-2.0
|
python-ironicclient>=2.2.0 # Apache-2.0
|
||||||
six>=1.10.0 # MIT
|
six>=1.10.0 # MIT
|
||||||
SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT
|
SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT
|
||||||
stevedore>=1.20.0 # Apache-2.0
|
stevedore>=1.20.0 # Apache-2.0
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
- name: Set up the list of hostnames and addresses
|
|
||||||
set_fact:
|
|
||||||
hostname_addresses: >
|
|
||||||
{% set hosts = {} -%}
|
|
||||||
{% for host, vars in hostvars.items() -%}
|
|
||||||
{% set _ = hosts.update({vars['ansible_hostname']: vars['nodepool']['private_ipv4']}) -%}
|
|
||||||
{% endfor -%}
|
|
||||||
{{- hosts -}}
|
|
||||||
- name: Add inventory hostnames to the hosts file
|
|
||||||
become: yes
|
|
||||||
lineinfile:
|
|
||||||
dest: /etc/hosts
|
|
||||||
state: present
|
|
||||||
insertafter: EOF
|
|
||||||
line: "{{ item.value }} {{ item.key }}"
|
|
||||||
with_dict: "{{ hostname_addresses }}"
|
|
||||||
@@ -15,7 +15,7 @@ testtools>=2.2.0 # MIT
|
|||||||
|
|
||||||
# Doc requirements
|
# Doc requirements
|
||||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||||
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
|
sphinx!=1.6.6,>=1.6.2 # BSD
|
||||||
sphinxcontrib-pecanwsme>=0.8.0 # Apache-2.0
|
sphinxcontrib-pecanwsme>=0.8.0 # Apache-2.0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
9
tox.ini
9
tox.ini
@@ -7,7 +7,7 @@ skipsdist = True
|
|||||||
usedevelop = True
|
usedevelop = True
|
||||||
whitelist_externals = find
|
whitelist_externals = find
|
||||||
rm
|
rm
|
||||||
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/queens} {opts} {packages}
|
||||||
setenv =
|
setenv =
|
||||||
VIRTUAL_ENV={envdir}
|
VIRTUAL_ENV={envdir}
|
||||||
deps = -r{toxinidir}/test-requirements.txt
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
@@ -76,10 +76,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:lower-constraints]
|
|
||||||
basepython = python3
|
|
||||||
deps =
|
|
||||||
-c{toxinidir}/lower-constraints.txt
|
|
||||||
-r{toxinidir}/test-requirements.txt
|
|
||||||
-r{toxinidir}/requirements.txt
|
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ class ActionCollection(collection.Collection):
|
|||||||
collection = ActionCollection()
|
collection = ActionCollection()
|
||||||
collection.actions = [Action.convert_with_links(p, expand)
|
collection.actions = [Action.convert_with_links(p, expand)
|
||||||
for p in actions]
|
for p in actions]
|
||||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
|
||||||
return collection
|
return collection
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -232,10 +232,6 @@ class ActionsController(rest.RestController):
|
|||||||
sort_key, sort_dir, expand=False,
|
sort_key, sort_dir, expand=False,
|
||||||
resource_url=None,
|
resource_url=None,
|
||||||
action_plan_uuid=None, audit_uuid=None):
|
action_plan_uuid=None, audit_uuid=None):
|
||||||
additional_fields = ['action_plan_uuid']
|
|
||||||
|
|
||||||
api_utils.validate_sort_key(sort_key, list(objects.Action.fields) +
|
|
||||||
additional_fields)
|
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
api_utils.validate_sort_dir(sort_dir)
|
api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
@@ -251,10 +247,7 @@ class ActionsController(rest.RestController):
|
|||||||
if audit_uuid:
|
if audit_uuid:
|
||||||
filters['audit_uuid'] = audit_uuid
|
filters['audit_uuid'] = audit_uuid
|
||||||
|
|
||||||
need_api_sort = api_utils.check_need_api_sort(sort_key,
|
sort_db_key = sort_key
|
||||||
additional_fields)
|
|
||||||
sort_db_key = (sort_key if not need_api_sort
|
|
||||||
else None)
|
|
||||||
|
|
||||||
actions = objects.Action.list(pecan.request.context,
|
actions = objects.Action.list(pecan.request.context,
|
||||||
limit,
|
limit,
|
||||||
@@ -262,15 +255,11 @@ class ActionsController(rest.RestController):
|
|||||||
sort_dir=sort_dir,
|
sort_dir=sort_dir,
|
||||||
filters=filters)
|
filters=filters)
|
||||||
|
|
||||||
actions_collection = ActionCollection.convert_with_links(
|
return ActionCollection.convert_with_links(actions, limit,
|
||||||
actions, limit, url=resource_url, expand=expand,
|
url=resource_url,
|
||||||
sort_key=sort_key, sort_dir=sort_dir)
|
expand=expand,
|
||||||
|
sort_key=sort_key,
|
||||||
if need_api_sort:
|
sort_dir=sort_dir)
|
||||||
api_utils.make_api_sort(actions_collection.actions,
|
|
||||||
sort_key, sort_dir)
|
|
||||||
|
|
||||||
return actions_collection
|
|
||||||
|
|
||||||
@wsme_pecan.wsexpose(ActionCollection, types.uuid, int,
|
@wsme_pecan.wsexpose(ActionCollection, types.uuid, int,
|
||||||
wtypes.text, wtypes.text, types.uuid,
|
wtypes.text, wtypes.text, types.uuid,
|
||||||
|
|||||||
@@ -305,6 +305,17 @@ class ActionPlanCollection(collection.Collection):
|
|||||||
ap_collection = ActionPlanCollection()
|
ap_collection = ActionPlanCollection()
|
||||||
ap_collection.action_plans = [ActionPlan.convert_with_links(
|
ap_collection.action_plans = [ActionPlan.convert_with_links(
|
||||||
p, expand) for p in rpc_action_plans]
|
p, expand) for p in rpc_action_plans]
|
||||||
|
|
||||||
|
if 'sort_key' in kwargs:
|
||||||
|
reverse = False
|
||||||
|
if kwargs['sort_key'] == 'audit_uuid':
|
||||||
|
if 'sort_dir' in kwargs:
|
||||||
|
reverse = True if kwargs['sort_dir'] == 'desc' else False
|
||||||
|
ap_collection.action_plans = sorted(
|
||||||
|
ap_collection.action_plans,
|
||||||
|
key=lambda action_plan: action_plan.audit_uuid,
|
||||||
|
reverse=reverse)
|
||||||
|
|
||||||
ap_collection.next = ap_collection.get_next(limit, url=url, **kwargs)
|
ap_collection.next = ap_collection.get_next(limit, url=url, **kwargs)
|
||||||
return ap_collection
|
return ap_collection
|
||||||
|
|
||||||
@@ -333,10 +344,7 @@ class ActionPlansController(rest.RestController):
|
|||||||
sort_key, sort_dir, expand=False,
|
sort_key, sort_dir, expand=False,
|
||||||
resource_url=None, audit_uuid=None,
|
resource_url=None, audit_uuid=None,
|
||||||
strategy=None):
|
strategy=None):
|
||||||
additional_fields = ['audit_uuid', 'strategy_uuid', 'strategy_name']
|
|
||||||
|
|
||||||
api_utils.validate_sort_key(
|
|
||||||
sort_key, list(objects.ActionPlan.fields) + additional_fields)
|
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
api_utils.validate_sort_dir(sort_dir)
|
api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
@@ -355,10 +363,10 @@ class ActionPlansController(rest.RestController):
|
|||||||
else:
|
else:
|
||||||
filters['strategy_name'] = strategy
|
filters['strategy_name'] = strategy
|
||||||
|
|
||||||
need_api_sort = api_utils.check_need_api_sort(sort_key,
|
if sort_key == 'audit_uuid':
|
||||||
additional_fields)
|
sort_db_key = None
|
||||||
sort_db_key = (sort_key if not need_api_sort
|
else:
|
||||||
else None)
|
sort_db_key = sort_key
|
||||||
|
|
||||||
action_plans = objects.ActionPlan.list(
|
action_plans = objects.ActionPlan.list(
|
||||||
pecan.request.context,
|
pecan.request.context,
|
||||||
@@ -366,15 +374,12 @@ class ActionPlansController(rest.RestController):
|
|||||||
marker_obj, sort_key=sort_db_key,
|
marker_obj, sort_key=sort_db_key,
|
||||||
sort_dir=sort_dir, filters=filters)
|
sort_dir=sort_dir, filters=filters)
|
||||||
|
|
||||||
action_plans_collection = ActionPlanCollection.convert_with_links(
|
return ActionPlanCollection.convert_with_links(
|
||||||
action_plans, limit, url=resource_url, expand=expand,
|
action_plans, limit,
|
||||||
sort_key=sort_key, sort_dir=sort_dir)
|
url=resource_url,
|
||||||
|
expand=expand,
|
||||||
if need_api_sort:
|
sort_key=sort_key,
|
||||||
api_utils.make_api_sort(action_plans_collection.action_plans,
|
sort_dir=sort_dir)
|
||||||
sort_key, sort_dir)
|
|
||||||
|
|
||||||
return action_plans_collection
|
|
||||||
|
|
||||||
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, int, wtypes.text,
|
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, int, wtypes.text,
|
||||||
wtypes.text, types.uuid, wtypes.text)
|
wtypes.text, types.uuid, wtypes.text)
|
||||||
|
|||||||
@@ -389,6 +389,17 @@ class AuditCollection(collection.Collection):
|
|||||||
collection = AuditCollection()
|
collection = AuditCollection()
|
||||||
collection.audits = [Audit.convert_with_links(p, expand)
|
collection.audits = [Audit.convert_with_links(p, expand)
|
||||||
for p in rpc_audits]
|
for p in rpc_audits]
|
||||||
|
|
||||||
|
if 'sort_key' in kwargs:
|
||||||
|
reverse = False
|
||||||
|
if kwargs['sort_key'] == 'goal_uuid':
|
||||||
|
if 'sort_dir' in kwargs:
|
||||||
|
reverse = True if kwargs['sort_dir'] == 'desc' else False
|
||||||
|
collection.audits = sorted(
|
||||||
|
collection.audits,
|
||||||
|
key=lambda audit: audit.goal_uuid,
|
||||||
|
reverse=reverse)
|
||||||
|
|
||||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||||
return collection
|
return collection
|
||||||
|
|
||||||
@@ -416,14 +427,8 @@ class AuditsController(rest.RestController):
|
|||||||
sort_key, sort_dir, expand=False,
|
sort_key, sort_dir, expand=False,
|
||||||
resource_url=None, goal=None,
|
resource_url=None, goal=None,
|
||||||
strategy=None):
|
strategy=None):
|
||||||
additional_fields = ["goal_uuid", "goal_name", "strategy_uuid",
|
|
||||||
"strategy_name"]
|
|
||||||
|
|
||||||
api_utils.validate_sort_key(
|
|
||||||
sort_key, list(objects.Audit.fields) + additional_fields)
|
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
api_utils.validate_sort_dir(sort_dir)
|
api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
marker_obj = None
|
marker_obj = None
|
||||||
if marker:
|
if marker:
|
||||||
marker_obj = objects.Audit.get_by_uuid(pecan.request.context,
|
marker_obj = objects.Audit.get_by_uuid(pecan.request.context,
|
||||||
@@ -444,25 +449,23 @@ class AuditsController(rest.RestController):
|
|||||||
# TODO(michaelgugino): add method to get goal by name.
|
# TODO(michaelgugino): add method to get goal by name.
|
||||||
filters['strategy_name'] = strategy
|
filters['strategy_name'] = strategy
|
||||||
|
|
||||||
need_api_sort = api_utils.check_need_api_sort(sort_key,
|
if sort_key == 'goal_uuid':
|
||||||
additional_fields)
|
sort_db_key = 'goal_id'
|
||||||
sort_db_key = (sort_key if not need_api_sort
|
elif sort_key == 'strategy_uuid':
|
||||||
else None)
|
sort_db_key = 'strategy_id'
|
||||||
|
else:
|
||||||
|
sort_db_key = sort_key
|
||||||
|
|
||||||
audits = objects.Audit.list(pecan.request.context,
|
audits = objects.Audit.list(pecan.request.context,
|
||||||
limit,
|
limit,
|
||||||
marker_obj, sort_key=sort_db_key,
|
marker_obj, sort_key=sort_db_key,
|
||||||
sort_dir=sort_dir, filters=filters)
|
sort_dir=sort_dir, filters=filters)
|
||||||
|
|
||||||
audits_collection = AuditCollection.convert_with_links(
|
return AuditCollection.convert_with_links(audits, limit,
|
||||||
audits, limit, url=resource_url, expand=expand,
|
url=resource_url,
|
||||||
sort_key=sort_key, sort_dir=sort_dir)
|
expand=expand,
|
||||||
|
sort_key=sort_key,
|
||||||
if need_api_sort:
|
sort_dir=sort_dir)
|
||||||
api_utils.make_api_sort(audits_collection.audits, sort_key,
|
|
||||||
sort_dir)
|
|
||||||
|
|
||||||
return audits_collection
|
|
||||||
|
|
||||||
@wsme_pecan.wsexpose(AuditCollection, types.uuid, int, wtypes.text,
|
@wsme_pecan.wsexpose(AuditCollection, types.uuid, int, wtypes.text,
|
||||||
wtypes.text, wtypes.text, wtypes.text, int)
|
wtypes.text, wtypes.text, wtypes.text, int)
|
||||||
|
|||||||
@@ -474,13 +474,9 @@ class AuditTemplatesController(rest.RestController):
|
|||||||
def _get_audit_templates_collection(self, filters, marker, limit,
|
def _get_audit_templates_collection(self, filters, marker, limit,
|
||||||
sort_key, sort_dir, expand=False,
|
sort_key, sort_dir, expand=False,
|
||||||
resource_url=None):
|
resource_url=None):
|
||||||
additional_fields = ["goal_uuid", "goal_name", "strategy_uuid",
|
|
||||||
"strategy_name"]
|
|
||||||
|
|
||||||
api_utils.validate_sort_key(
|
|
||||||
sort_key, list(objects.AuditTemplate.fields) + additional_fields)
|
|
||||||
api_utils.validate_search_filters(
|
api_utils.validate_search_filters(
|
||||||
filters, list(objects.AuditTemplate.fields) + additional_fields)
|
filters, list(objects.audit_template.AuditTemplate.fields) +
|
||||||
|
["goal_uuid", "goal_name", "strategy_uuid", "strategy_name"])
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
api_utils.validate_sort_dir(sort_dir)
|
api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
@@ -490,26 +486,19 @@ class AuditTemplatesController(rest.RestController):
|
|||||||
pecan.request.context,
|
pecan.request.context,
|
||||||
marker)
|
marker)
|
||||||
|
|
||||||
need_api_sort = api_utils.check_need_api_sort(sort_key,
|
|
||||||
additional_fields)
|
|
||||||
sort_db_key = (sort_key if not need_api_sort
|
|
||||||
else None)
|
|
||||||
|
|
||||||
audit_templates = objects.AuditTemplate.list(
|
audit_templates = objects.AuditTemplate.list(
|
||||||
pecan.request.context, filters, limit, marker_obj,
|
pecan.request.context,
|
||||||
sort_key=sort_db_key, sort_dir=sort_dir)
|
filters,
|
||||||
|
limit,
|
||||||
|
marker_obj, sort_key=sort_key,
|
||||||
|
sort_dir=sort_dir)
|
||||||
|
|
||||||
audit_templates_collection = \
|
return AuditTemplateCollection.convert_with_links(audit_templates,
|
||||||
AuditTemplateCollection.convert_with_links(
|
limit,
|
||||||
audit_templates, limit, url=resource_url, expand=expand,
|
url=resource_url,
|
||||||
sort_key=sort_key, sort_dir=sort_dir)
|
expand=expand,
|
||||||
|
sort_key=sort_key,
|
||||||
if need_api_sort:
|
sort_dir=sort_dir)
|
||||||
api_utils.make_api_sort(
|
|
||||||
audit_templates_collection.audit_templates, sort_key,
|
|
||||||
sort_dir)
|
|
||||||
|
|
||||||
return audit_templates_collection
|
|
||||||
|
|
||||||
@wsme_pecan.wsexpose(AuditTemplateCollection, wtypes.text, wtypes.text,
|
@wsme_pecan.wsexpose(AuditTemplateCollection, wtypes.text, wtypes.text,
|
||||||
types.uuid, int, wtypes.text, wtypes.text)
|
types.uuid, int, wtypes.text, wtypes.text)
|
||||||
|
|||||||
@@ -130,6 +130,17 @@ class GoalCollection(collection.Collection):
|
|||||||
goal_collection = GoalCollection()
|
goal_collection = GoalCollection()
|
||||||
goal_collection.goals = [
|
goal_collection.goals = [
|
||||||
Goal.convert_with_links(g, expand) for g in goals]
|
Goal.convert_with_links(g, expand) for g in goals]
|
||||||
|
|
||||||
|
if 'sort_key' in kwargs:
|
||||||
|
reverse = False
|
||||||
|
if kwargs['sort_key'] == 'strategy':
|
||||||
|
if 'sort_dir' in kwargs:
|
||||||
|
reverse = True if kwargs['sort_dir'] == 'desc' else False
|
||||||
|
goal_collection.goals = sorted(
|
||||||
|
goal_collection.goals,
|
||||||
|
key=lambda goal: goal.uuid,
|
||||||
|
reverse=reverse)
|
||||||
|
|
||||||
goal_collection.next = goal_collection.get_next(
|
goal_collection.next = goal_collection.get_next(
|
||||||
limit, url=url, **kwargs)
|
limit, url=url, **kwargs)
|
||||||
return goal_collection
|
return goal_collection
|
||||||
@@ -156,19 +167,17 @@ class GoalsController(rest.RestController):
|
|||||||
|
|
||||||
def _get_goals_collection(self, marker, limit, sort_key, sort_dir,
|
def _get_goals_collection(self, marker, limit, sort_key, sort_dir,
|
||||||
expand=False, resource_url=None):
|
expand=False, resource_url=None):
|
||||||
api_utils.validate_sort_key(
|
|
||||||
sort_key, list(objects.Goal.fields))
|
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
api_utils.validate_sort_dir(sort_dir)
|
api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
|
sort_db_key = (sort_key if sort_key in objects.Goal.fields
|
||||||
|
else None)
|
||||||
|
|
||||||
marker_obj = None
|
marker_obj = None
|
||||||
if marker:
|
if marker:
|
||||||
marker_obj = objects.Goal.get_by_uuid(
|
marker_obj = objects.Goal.get_by_uuid(
|
||||||
pecan.request.context, marker)
|
pecan.request.context, marker)
|
||||||
|
|
||||||
sort_db_key = (sort_key if sort_key in objects.Goal.fields
|
|
||||||
else None)
|
|
||||||
|
|
||||||
goals = objects.Goal.list(pecan.request.context, limit, marker_obj,
|
goals = objects.Goal.list(pecan.request.context, limit, marker_obj,
|
||||||
sort_key=sort_db_key, sort_dir=sort_dir)
|
sort_key=sort_db_key, sort_dir=sort_dir)
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,17 @@ class ScoringEngineCollection(collection.Collection):
|
|||||||
collection = ScoringEngineCollection()
|
collection = ScoringEngineCollection()
|
||||||
collection.scoring_engines = [ScoringEngine.convert_with_links(
|
collection.scoring_engines = [ScoringEngine.convert_with_links(
|
||||||
se, expand) for se in scoring_engines]
|
se, expand) for se in scoring_engines]
|
||||||
|
|
||||||
|
if 'sort_key' in kwargs:
|
||||||
|
reverse = False
|
||||||
|
if kwargs['sort_key'] == 'name':
|
||||||
|
if 'sort_dir' in kwargs:
|
||||||
|
reverse = True if kwargs['sort_dir'] == 'desc' else False
|
||||||
|
collection.goals = sorted(
|
||||||
|
collection.scoring_engines,
|
||||||
|
key=lambda se: se.name,
|
||||||
|
reverse=reverse)
|
||||||
|
|
||||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||||
return collection
|
return collection
|
||||||
|
|
||||||
@@ -149,8 +160,7 @@ class ScoringEngineController(rest.RestController):
|
|||||||
def _get_scoring_engines_collection(self, marker, limit,
|
def _get_scoring_engines_collection(self, marker, limit,
|
||||||
sort_key, sort_dir, expand=False,
|
sort_key, sort_dir, expand=False,
|
||||||
resource_url=None):
|
resource_url=None):
|
||||||
api_utils.validate_sort_key(
|
|
||||||
sort_key, list(objects.ScoringEngine.fields))
|
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
api_utils.validate_sort_dir(sort_dir)
|
api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
@@ -161,8 +171,7 @@ class ScoringEngineController(rest.RestController):
|
|||||||
|
|
||||||
filters = {}
|
filters = {}
|
||||||
|
|
||||||
sort_db_key = (sort_key if sort_key in objects.ScoringEngine.fields
|
sort_db_key = sort_key
|
||||||
else None)
|
|
||||||
|
|
||||||
scoring_engines = objects.ScoringEngine.list(
|
scoring_engines = objects.ScoringEngine.list(
|
||||||
context=pecan.request.context,
|
context=pecan.request.context,
|
||||||
|
|||||||
@@ -154,6 +154,17 @@ class ServiceCollection(collection.Collection):
|
|||||||
service_collection = ServiceCollection()
|
service_collection = ServiceCollection()
|
||||||
service_collection.services = [
|
service_collection.services = [
|
||||||
Service.convert_with_links(g, expand) for g in services]
|
Service.convert_with_links(g, expand) for g in services]
|
||||||
|
|
||||||
|
if 'sort_key' in kwargs:
|
||||||
|
reverse = False
|
||||||
|
if kwargs['sort_key'] == 'service':
|
||||||
|
if 'sort_dir' in kwargs:
|
||||||
|
reverse = True if kwargs['sort_dir'] == 'desc' else False
|
||||||
|
service_collection.services = sorted(
|
||||||
|
service_collection.services,
|
||||||
|
key=lambda service: service.id,
|
||||||
|
reverse=reverse)
|
||||||
|
|
||||||
service_collection.next = service_collection.get_next(
|
service_collection.next = service_collection.get_next(
|
||||||
limit, url=url, marker_field='id', **kwargs)
|
limit, url=url, marker_field='id', **kwargs)
|
||||||
return service_collection
|
return service_collection
|
||||||
@@ -180,19 +191,17 @@ class ServicesController(rest.RestController):
|
|||||||
|
|
||||||
def _get_services_collection(self, marker, limit, sort_key, sort_dir,
|
def _get_services_collection(self, marker, limit, sort_key, sort_dir,
|
||||||
expand=False, resource_url=None):
|
expand=False, resource_url=None):
|
||||||
api_utils.validate_sort_key(
|
|
||||||
sort_key, list(objects.Service.fields))
|
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
api_utils.validate_sort_dir(sort_dir)
|
api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
|
sort_db_key = (sort_key if sort_key in objects.Service.fields
|
||||||
|
else None)
|
||||||
|
|
||||||
marker_obj = None
|
marker_obj = None
|
||||||
if marker:
|
if marker:
|
||||||
marker_obj = objects.Service.get(
|
marker_obj = objects.Service.get(
|
||||||
pecan.request.context, marker)
|
pecan.request.context, marker)
|
||||||
|
|
||||||
sort_db_key = (sort_key if sort_key in objects.Service.fields
|
|
||||||
else None)
|
|
||||||
|
|
||||||
services = objects.Service.list(
|
services = objects.Service.list(
|
||||||
pecan.request.context, limit, marker_obj,
|
pecan.request.context, limit, marker_obj,
|
||||||
sort_key=sort_db_key, sort_dir=sort_dir)
|
sort_key=sort_db_key, sort_dir=sort_dir)
|
||||||
|
|||||||
@@ -173,6 +173,17 @@ class StrategyCollection(collection.Collection):
|
|||||||
strategy_collection = StrategyCollection()
|
strategy_collection = StrategyCollection()
|
||||||
strategy_collection.strategies = [
|
strategy_collection.strategies = [
|
||||||
Strategy.convert_with_links(g, expand) for g in strategies]
|
Strategy.convert_with_links(g, expand) for g in strategies]
|
||||||
|
|
||||||
|
if 'sort_key' in kwargs:
|
||||||
|
reverse = False
|
||||||
|
if kwargs['sort_key'] == 'strategy':
|
||||||
|
if 'sort_dir' in kwargs:
|
||||||
|
reverse = True if kwargs['sort_dir'] == 'desc' else False
|
||||||
|
strategy_collection.strategies = sorted(
|
||||||
|
strategy_collection.strategies,
|
||||||
|
key=lambda strategy: strategy.uuid,
|
||||||
|
reverse=reverse)
|
||||||
|
|
||||||
strategy_collection.next = strategy_collection.get_next(
|
strategy_collection.next = strategy_collection.get_next(
|
||||||
limit, url=url, **kwargs)
|
limit, url=url, **kwargs)
|
||||||
return strategy_collection
|
return strategy_collection
|
||||||
@@ -200,39 +211,28 @@ class StrategiesController(rest.RestController):
|
|||||||
|
|
||||||
def _get_strategies_collection(self, filters, marker, limit, sort_key,
|
def _get_strategies_collection(self, filters, marker, limit, sort_key,
|
||||||
sort_dir, expand=False, resource_url=None):
|
sort_dir, expand=False, resource_url=None):
|
||||||
additional_fields = ["goal_uuid", "goal_name"]
|
|
||||||
|
|
||||||
api_utils.validate_sort_key(
|
|
||||||
sort_key, list(objects.Strategy.fields) + additional_fields)
|
|
||||||
api_utils.validate_search_filters(
|
api_utils.validate_search_filters(
|
||||||
filters, list(objects.Strategy.fields) + additional_fields)
|
filters, list(objects.strategy.Strategy.fields) +
|
||||||
|
["goal_uuid", "goal_name"])
|
||||||
limit = api_utils.validate_limit(limit)
|
limit = api_utils.validate_limit(limit)
|
||||||
api_utils.validate_sort_dir(sort_dir)
|
api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
|
sort_db_key = (sort_key if sort_key in objects.Strategy.fields
|
||||||
|
else None)
|
||||||
|
|
||||||
marker_obj = None
|
marker_obj = None
|
||||||
if marker:
|
if marker:
|
||||||
marker_obj = objects.Strategy.get_by_uuid(
|
marker_obj = objects.Strategy.get_by_uuid(
|
||||||
pecan.request.context, marker)
|
pecan.request.context, marker)
|
||||||
|
|
||||||
need_api_sort = api_utils.check_need_api_sort(sort_key,
|
|
||||||
additional_fields)
|
|
||||||
sort_db_key = (sort_key if not need_api_sort
|
|
||||||
else None)
|
|
||||||
|
|
||||||
strategies = objects.Strategy.list(
|
strategies = objects.Strategy.list(
|
||||||
pecan.request.context, limit, marker_obj, filters=filters,
|
pecan.request.context, limit, marker_obj, filters=filters,
|
||||||
sort_key=sort_db_key, sort_dir=sort_dir)
|
sort_key=sort_db_key, sort_dir=sort_dir)
|
||||||
|
|
||||||
strategies_collection = StrategyCollection.convert_with_links(
|
return StrategyCollection.convert_with_links(
|
||||||
strategies, limit, url=resource_url, expand=expand,
|
strategies, limit, url=resource_url, expand=expand,
|
||||||
sort_key=sort_key, sort_dir=sort_dir)
|
sort_key=sort_key, sort_dir=sort_dir)
|
||||||
|
|
||||||
if need_api_sort:
|
|
||||||
api_utils.make_api_sort(strategies_collection.strategies,
|
|
||||||
sort_key, sort_dir)
|
|
||||||
|
|
||||||
return strategies_collection
|
|
||||||
|
|
||||||
@wsme_pecan.wsexpose(StrategyCollection, wtypes.text, wtypes.text,
|
@wsme_pecan.wsexpose(StrategyCollection, wtypes.text, wtypes.text,
|
||||||
int, wtypes.text, wtypes.text)
|
int, wtypes.text, wtypes.text)
|
||||||
def get_all(self, goal=None, marker=None, limit=None,
|
def get_all(self, goal=None, marker=None, limit=None,
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from operator import attrgetter
|
|
||||||
|
|
||||||
import jsonpatch
|
import jsonpatch
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils import reflection
|
from oslo_utils import reflection
|
||||||
@@ -56,13 +54,6 @@ def validate_sort_dir(sort_dir):
|
|||||||
"'asc' or 'desc'") % sort_dir)
|
"'asc' or 'desc'") % sort_dir)
|
||||||
|
|
||||||
|
|
||||||
def validate_sort_key(sort_key, allowed_fields):
|
|
||||||
# Very lightweight validation for now
|
|
||||||
if sort_key not in allowed_fields:
|
|
||||||
raise wsme.exc.ClientSideError(
|
|
||||||
_("Invalid sort key: %s") % sort_key)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_search_filters(filters, allowed_fields):
|
def validate_search_filters(filters, allowed_fields):
|
||||||
# Very lightweight validation for now
|
# Very lightweight 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)
|
||||||
@@ -72,19 +63,6 @@ def validate_search_filters(filters, allowed_fields):
|
|||||||
_("Invalid filter: %s") % filter_name)
|
_("Invalid filter: %s") % filter_name)
|
||||||
|
|
||||||
|
|
||||||
def check_need_api_sort(sort_key, additional_fields):
|
|
||||||
return sort_key in additional_fields
|
|
||||||
|
|
||||||
|
|
||||||
def make_api_sort(sorting_list, sort_key, sort_dir):
|
|
||||||
# First sort by uuid field, than sort by sort_key
|
|
||||||
# sort() ensures stable sorting, so we could
|
|
||||||
# make lexicographical sort
|
|
||||||
reverse_direction = (sort_dir == 'desc')
|
|
||||||
sorting_list.sort(key=attrgetter('uuid'), reverse=reverse_direction)
|
|
||||||
sorting_list.sort(key=attrgetter(sort_key), reverse=reverse_direction)
|
|
||||||
|
|
||||||
|
|
||||||
def apply_jsonpatch(doc, patch):
|
def apply_jsonpatch(doc, patch):
|
||||||
for p in patch:
|
for p in patch:
|
||||||
if p['op'] == 'add' and p['path'].count('/') == 1:
|
if p['op'] == 'add' and p['path'].count('/') == 1:
|
||||||
|
|||||||
@@ -50,12 +50,6 @@ class Migrate(base.BaseAction):
|
|||||||
source and the destination compute hostname (list of available compute
|
source and the destination compute hostname (list of available compute
|
||||||
hosts is returned by this command: ``nova service-list --binary
|
hosts is returned by this command: ``nova service-list --binary
|
||||||
nova-compute``).
|
nova-compute``).
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Nova API version must be 2.56 or above if `destination_node` parameter
|
|
||||||
is given.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# input parameters constants
|
# input parameters constants
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class CinderHelper(object):
|
|||||||
search_opts={'all_tenants': True})
|
search_opts={'all_tenants': True})
|
||||||
|
|
||||||
def get_volume_type_by_backendname(self, backendname):
|
def get_volume_type_by_backendname(self, backendname):
|
||||||
"""Return a list of volume type"""
|
"""Retrun a list of volume type"""
|
||||||
volume_type_list = self.get_volume_type_list()
|
volume_type_list = self.get_volume_type_list()
|
||||||
|
|
||||||
volume_type = [volume_type.name for volume_type in volume_type_list
|
volume_type = [volume_type.name for volume_type in volume_type_list
|
||||||
|
|||||||
@@ -83,8 +83,10 @@ class OpenStackClients(object):
|
|||||||
|
|
||||||
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')
|
nova_endpoint_type = self._get_client_option('nova', 'endpoint_type')
|
||||||
|
nova_region_name = self._get_client_option('nova', 'region_name')
|
||||||
self._nova = nvclient.Client(novaclient_version,
|
self._nova = nvclient.Client(novaclient_version,
|
||||||
endpoint_type=nova_endpoint_type,
|
endpoint_type=nova_endpoint_type,
|
||||||
|
region_name=nova_region_name,
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._nova
|
return self._nova
|
||||||
|
|
||||||
@@ -96,8 +98,10 @@ class OpenStackClients(object):
|
|||||||
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',
|
glance_endpoint_type = self._get_client_option('glance',
|
||||||
'endpoint_type')
|
'endpoint_type')
|
||||||
|
glance_region_name = self._get_client_option('glance', 'region_name')
|
||||||
self._glance = glclient.Client(glanceclient_version,
|
self._glance = glclient.Client(glanceclient_version,
|
||||||
interface=glance_endpoint_type,
|
interface=glance_endpoint_type,
|
||||||
|
region_name=glance_region_name,
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._glance
|
return self._glance
|
||||||
|
|
||||||
@@ -110,8 +114,11 @@ class OpenStackClients(object):
|
|||||||
'api_version')
|
'api_version')
|
||||||
gnocchiclient_interface = self._get_client_option('gnocchi',
|
gnocchiclient_interface = self._get_client_option('gnocchi',
|
||||||
'endpoint_type')
|
'endpoint_type')
|
||||||
|
gnocchiclient_region_name = self._get_client_option('gnocchi',
|
||||||
|
'region_name')
|
||||||
adapter_options = {
|
adapter_options = {
|
||||||
"interface": gnocchiclient_interface
|
"interface": gnocchiclient_interface,
|
||||||
|
"region_name": gnocchiclient_region_name
|
||||||
}
|
}
|
||||||
|
|
||||||
self._gnocchi = gnclient.Client(gnocchiclient_version,
|
self._gnocchi = gnclient.Client(gnocchiclient_version,
|
||||||
@@ -127,8 +134,10 @@ class OpenStackClients(object):
|
|||||||
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',
|
cinder_endpoint_type = self._get_client_option('cinder',
|
||||||
'endpoint_type')
|
'endpoint_type')
|
||||||
|
cinder_region_name = self._get_client_option('cinder', 'region_name')
|
||||||
self._cinder = ciclient.Client(cinderclient_version,
|
self._cinder = ciclient.Client(cinderclient_version,
|
||||||
endpoint_type=cinder_endpoint_type,
|
endpoint_type=cinder_endpoint_type,
|
||||||
|
region_name=cinder_region_name,
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._cinder
|
return self._cinder
|
||||||
|
|
||||||
@@ -141,9 +150,12 @@ class OpenStackClients(object):
|
|||||||
'api_version')
|
'api_version')
|
||||||
ceilometer_endpoint_type = self._get_client_option('ceilometer',
|
ceilometer_endpoint_type = self._get_client_option('ceilometer',
|
||||||
'endpoint_type')
|
'endpoint_type')
|
||||||
|
ceilometer_region_name = self._get_client_option('ceilometer',
|
||||||
|
'region_name')
|
||||||
self._ceilometer = ceclient.get_client(
|
self._ceilometer = ceclient.get_client(
|
||||||
ceilometerclient_version,
|
ceilometerclient_version,
|
||||||
endpoint_type=ceilometer_endpoint_type,
|
endpoint_type=ceilometer_endpoint_type,
|
||||||
|
region_name=ceilometer_region_name,
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._ceilometer
|
return self._ceilometer
|
||||||
|
|
||||||
@@ -156,6 +168,8 @@ class OpenStackClients(object):
|
|||||||
'monasca', 'api_version')
|
'monasca', 'api_version')
|
||||||
monascaclient_interface = self._get_client_option(
|
monascaclient_interface = self._get_client_option(
|
||||||
'monasca', 'interface')
|
'monasca', 'interface')
|
||||||
|
monascaclient_region = self._get_client_option(
|
||||||
|
'monasca', 'region_name')
|
||||||
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'
|
||||||
@@ -172,7 +186,8 @@ class OpenStackClients(object):
|
|||||||
'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)
|
interface=monascaclient_interface,
|
||||||
|
region_name=monascaclient_region)
|
||||||
|
|
||||||
self._monasca = monclient.Client(
|
self._monasca = monclient.Client(
|
||||||
monascaclient_version, endpoint, **monasca_kwargs)
|
monascaclient_version, endpoint, **monasca_kwargs)
|
||||||
@@ -188,9 +203,11 @@ class OpenStackClients(object):
|
|||||||
'api_version')
|
'api_version')
|
||||||
neutron_endpoint_type = self._get_client_option('neutron',
|
neutron_endpoint_type = self._get_client_option('neutron',
|
||||||
'endpoint_type')
|
'endpoint_type')
|
||||||
|
neutron_region_name = self._get_client_option('neutron', 'region_name')
|
||||||
|
|
||||||
self._neutron = netclient.Client(neutronclient_version,
|
self._neutron = netclient.Client(neutronclient_version,
|
||||||
endpoint_type=neutron_endpoint_type,
|
endpoint_type=neutron_endpoint_type,
|
||||||
|
region_name=neutron_region_name,
|
||||||
session=self.session)
|
session=self.session)
|
||||||
self._neutron.format = 'json'
|
self._neutron.format = 'json'
|
||||||
return self._neutron
|
return self._neutron
|
||||||
@@ -202,7 +219,9 @@ class OpenStackClients(object):
|
|||||||
|
|
||||||
ironicclient_version = self._get_client_option('ironic', 'api_version')
|
ironicclient_version = self._get_client_option('ironic', 'api_version')
|
||||||
endpoint_type = self._get_client_option('ironic', 'endpoint_type')
|
endpoint_type = self._get_client_option('ironic', 'endpoint_type')
|
||||||
|
ironic_region_name = self._get_client_option('ironic', 'region_name')
|
||||||
self._ironic = irclient.get_client(ironicclient_version,
|
self._ironic = irclient.get_client(ironicclient_version,
|
||||||
os_endpoint_type=endpoint_type,
|
os_endpoint_type=endpoint_type,
|
||||||
|
region_name=ironic_region_name,
|
||||||
session=self.session)
|
session=self.session)
|
||||||
return self._ironic
|
return self._ironic
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ 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)
|
||||||
kwargs.pop('global_request_id', None)
|
kwargs.pop('global_request_id', None)
|
||||||
kwargs.pop('project', None)
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
LOG.warning('Arguments dropped when creating context: %s',
|
LOG.warning('Arguments dropped when creating context: %s',
|
||||||
str(kwargs))
|
str(kwargs))
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import random
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from novaclient import api_versions
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
import cinderclient.exceptions as ciexceptions
|
import cinderclient.exceptions as ciexceptions
|
||||||
@@ -29,12 +29,9 @@ import novaclient.exceptions as nvexceptions
|
|||||||
from watcher.common import clients
|
from watcher.common import clients
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher.common import utils
|
from watcher.common import utils
|
||||||
from watcher import conf
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
CONF = conf.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class NovaHelper(object):
|
class NovaHelper(object):
|
||||||
|
|
||||||
@@ -75,7 +72,8 @@ class NovaHelper(object):
|
|||||||
raise exception.ComputeNodeNotFound(name=node_hostname)
|
raise exception.ComputeNodeNotFound(name=node_hostname)
|
||||||
|
|
||||||
def get_instance_list(self):
|
def get_instance_list(self):
|
||||||
return self.nova.servers.list(search_opts={'all_tenants': True})
|
return self.nova.servers.list(search_opts={'all_tenants': True},
|
||||||
|
limit=-1)
|
||||||
|
|
||||||
def get_flavor_list(self):
|
def get_flavor_list(self):
|
||||||
return self.nova.flavors.list(**{'is_public': None})
|
return self.nova.flavors.list(**{'is_public': None})
|
||||||
@@ -133,24 +131,31 @@ class NovaHelper(object):
|
|||||||
return volume.status == status
|
return volume.status == status
|
||||||
|
|
||||||
def watcher_non_live_migrate_instance(self, instance_id, dest_hostname,
|
def watcher_non_live_migrate_instance(self, instance_id, dest_hostname,
|
||||||
|
keep_original_image_name=True,
|
||||||
retry=120):
|
retry=120):
|
||||||
"""This method migrates a given instance
|
"""This method migrates a given instance
|
||||||
|
|
||||||
This method uses the Nova built-in migrate()
|
using an image of this instance and creating a new instance
|
||||||
action to do a migration of a given instance.
|
from this image. It saves some configuration information
|
||||||
For migrating a given dest_hostname, Nova API version
|
about the original instance : security group, list of networks,
|
||||||
must be 2.56 or higher.
|
list of attached volumes, floating IP, ...
|
||||||
|
in order to apply the same settings to the new instance.
|
||||||
|
At the end of the process the original instance is deleted.
|
||||||
It returns True if the migration was successful,
|
It returns True if the migration was successful,
|
||||||
False otherwise.
|
False otherwise.
|
||||||
|
|
||||||
|
if destination hostname not given, this method calls nova api
|
||||||
|
to migrate the instance.
|
||||||
|
|
||||||
:param instance_id: the unique id of the instance to migrate.
|
:param instance_id: the unique id of the instance to migrate.
|
||||||
:param dest_hostname: the name of the destination compute node, if
|
:param keep_original_image_name: flag indicating whether the
|
||||||
destination_node is None, nova scheduler choose
|
image name from which the original instance was built must be
|
||||||
the destination host
|
used as the name of the intermediate image used for migration.
|
||||||
|
If this flag is False, a temporary image name is built
|
||||||
"""
|
"""
|
||||||
|
new_image_name = ""
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Trying a cold migrate of instance '%s' ", instance_id)
|
"Trying a non-live migrate of instance '%s' ", instance_id)
|
||||||
|
|
||||||
# Looking for the instance to migrate
|
# Looking for the instance to migrate
|
||||||
instance = self.find_instance(instance_id)
|
instance = self.find_instance(instance_id)
|
||||||
@@ -158,43 +163,215 @@ class NovaHelper(object):
|
|||||||
LOG.debug("Instance %s not found !", instance_id)
|
LOG.debug("Instance %s not found !", instance_id)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
# NOTE: If destination node is None call Nova API to migrate
|
||||||
|
# instance
|
||||||
host_name = getattr(instance, "OS-EXT-SRV-ATTR:host")
|
host_name = getattr(instance, "OS-EXT-SRV-ATTR:host")
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Instance %(instance)s found on host '%(host)s'.",
|
"Instance %(instance)s found on host '%(host)s'.",
|
||||||
{'instance': instance_id, 'host': host_name})
|
{'instance': instance_id, 'host': host_name})
|
||||||
|
|
||||||
previous_status = getattr(instance, 'status')
|
if dest_hostname is None:
|
||||||
|
previous_status = getattr(instance, 'status')
|
||||||
|
|
||||||
if (dest_hostname and
|
instance.migrate()
|
||||||
not self._check_nova_api_version(self.nova, "2.56")):
|
instance = self.nova.servers.get(instance_id)
|
||||||
LOG.error("For migrating a given dest_hostname,"
|
while (getattr(instance, 'status') not in
|
||||||
"Nova API version must be 2.56 or higher")
|
["VERIFY_RESIZE", "ERROR"] and retry):
|
||||||
return False
|
instance = self.nova.servers.get(instance.id)
|
||||||
|
time.sleep(2)
|
||||||
|
retry -= 1
|
||||||
|
new_hostname = getattr(instance, 'OS-EXT-SRV-ATTR:host')
|
||||||
|
|
||||||
instance.migrate(host=dest_hostname)
|
if (host_name != new_hostname and
|
||||||
instance = self.nova.servers.get(instance_id)
|
instance.status == 'VERIFY_RESIZE'):
|
||||||
|
if not self.confirm_resize(instance, previous_status):
|
||||||
while (getattr(instance, 'status') not in
|
return False
|
||||||
["VERIFY_RESIZE", "ERROR"] and retry):
|
LOG.debug(
|
||||||
instance = self.nova.servers.get(instance.id)
|
"cold migration succeeded : "
|
||||||
time.sleep(2)
|
"instance %s is now on host '%s'.", (
|
||||||
retry -= 1
|
instance_id, new_hostname))
|
||||||
new_hostname = getattr(instance, 'OS-EXT-SRV-ATTR:host')
|
return True
|
||||||
|
else:
|
||||||
if (host_name != new_hostname and
|
LOG.debug(
|
||||||
instance.status == 'VERIFY_RESIZE'):
|
"cold migration for instance %s failed", instance_id)
|
||||||
if not self.confirm_resize(instance, previous_status):
|
|
||||||
return False
|
return False
|
||||||
LOG.debug(
|
|
||||||
"cold migration succeeded : "
|
if not keep_original_image_name:
|
||||||
"instance %(instance)s is now on host '%(host)s'.",
|
# randrange gives you an integral value
|
||||||
{'instance': instance_id, 'host': new_hostname})
|
irand = random.randint(0, 1000)
|
||||||
return True
|
|
||||||
|
# Building the temporary image name
|
||||||
|
# which will be used for the migration
|
||||||
|
new_image_name = "tmp-migrate-%s-%s" % (instance_id, irand)
|
||||||
else:
|
else:
|
||||||
|
# Get the image name of the current instance.
|
||||||
|
# We'll use the same name for the new instance.
|
||||||
|
imagedict = getattr(instance, "image")
|
||||||
|
image_id = imagedict["id"]
|
||||||
|
image = self.glance.images.get(image_id)
|
||||||
|
new_image_name = getattr(image, "name")
|
||||||
|
|
||||||
|
instance_name = getattr(instance, "name")
|
||||||
|
flavor_name = instance.flavor.get('original_name')
|
||||||
|
keypair_name = getattr(instance, "key_name")
|
||||||
|
|
||||||
|
addresses = getattr(instance, "addresses")
|
||||||
|
|
||||||
|
floating_ip = ""
|
||||||
|
network_names_list = []
|
||||||
|
|
||||||
|
for network_name, network_conf_obj in addresses.items():
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"cold migration for instance %s failed", instance_id)
|
"Extracting network configuration for network '%s'",
|
||||||
|
network_name)
|
||||||
|
|
||||||
|
network_names_list.append(network_name)
|
||||||
|
|
||||||
|
for net_conf_item in network_conf_obj:
|
||||||
|
if net_conf_item['OS-EXT-IPS:type'] == "floating":
|
||||||
|
floating_ip = net_conf_item['addr']
|
||||||
|
break
|
||||||
|
|
||||||
|
sec_groups_list = getattr(instance, "security_groups")
|
||||||
|
sec_groups = []
|
||||||
|
|
||||||
|
for sec_group_dict in sec_groups_list:
|
||||||
|
sec_groups.append(sec_group_dict['name'])
|
||||||
|
|
||||||
|
# Stopping the old instance properly so
|
||||||
|
# that no new data is sent to it and to its attached volumes
|
||||||
|
stopped_ok = self.stop_instance(instance_id)
|
||||||
|
|
||||||
|
if not stopped_ok:
|
||||||
|
LOG.debug("Could not stop instance: %s", instance_id)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Building the temporary image which will be used
|
||||||
|
# to re-build the same instance on another target host
|
||||||
|
image_uuid = self.create_image_from_instance(instance_id,
|
||||||
|
new_image_name)
|
||||||
|
|
||||||
|
if not image_uuid:
|
||||||
|
LOG.debug(
|
||||||
|
"Could not build temporary image of instance: %s",
|
||||||
|
instance_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
#
|
||||||
|
# We need to get the list of attached volumes and detach
|
||||||
|
# them from the instance in order to attache them later
|
||||||
|
# to the new instance
|
||||||
|
#
|
||||||
|
blocks = []
|
||||||
|
|
||||||
|
# Looks like this :
|
||||||
|
# os-extended-volumes:volumes_attached |
|
||||||
|
# [{u'id': u'c5c3245f-dd59-4d4f-8d3a-89d80135859a'}]
|
||||||
|
attached_volumes = getattr(instance,
|
||||||
|
"os-extended-volumes:volumes_attached")
|
||||||
|
|
||||||
|
for attached_volume in attached_volumes:
|
||||||
|
volume_id = attached_volume['id']
|
||||||
|
|
||||||
|
try:
|
||||||
|
volume = self.cinder.volumes.get(volume_id)
|
||||||
|
|
||||||
|
attachments_list = getattr(volume, "attachments")
|
||||||
|
|
||||||
|
device_name = attachments_list[0]['device']
|
||||||
|
# When a volume is attached to an instance
|
||||||
|
# it contains the following property :
|
||||||
|
# attachments = [{u'device': u'/dev/vdb',
|
||||||
|
# u'server_id': u'742cc508-a2f2-4769-a794-bcdad777e814',
|
||||||
|
# u'id': u'f6d62785-04b8-400d-9626-88640610f65e',
|
||||||
|
# u'host_name': None, u'volume_id':
|
||||||
|
# u'f6d62785-04b8-400d-9626-88640610f65e'}]
|
||||||
|
|
||||||
|
# boot_index indicates a number
|
||||||
|
# designating the boot order of the device.
|
||||||
|
# Use -1 for the boot volume,
|
||||||
|
# choose 0 for an attached volume.
|
||||||
|
block_device_mapping_v2_item = {"device_name": device_name,
|
||||||
|
"source_type": "volume",
|
||||||
|
"destination_type":
|
||||||
|
"volume",
|
||||||
|
"uuid": volume_id,
|
||||||
|
"boot_index": "0"}
|
||||||
|
|
||||||
|
blocks.append(
|
||||||
|
block_device_mapping_v2_item)
|
||||||
|
|
||||||
|
LOG.debug(
|
||||||
|
"Detaching volume %(volume)s from "
|
||||||
|
"instance: %(instance)s",
|
||||||
|
{'volume': volume_id, 'instance': instance_id})
|
||||||
|
# volume.detach()
|
||||||
|
self.nova.volumes.delete_server_volume(instance_id,
|
||||||
|
volume_id)
|
||||||
|
|
||||||
|
if not self.wait_for_volume_status(volume, "available", 5,
|
||||||
|
10):
|
||||||
|
LOG.debug(
|
||||||
|
"Could not detach volume %(volume)s "
|
||||||
|
"from instance: %(instance)s",
|
||||||
|
{'volume': volume_id, 'instance': instance_id})
|
||||||
|
return False
|
||||||
|
except ciexceptions.NotFound:
|
||||||
|
LOG.debug("Volume '%s' not found ", image_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# We create the new instance from
|
||||||
|
# the intermediate image of the original instance
|
||||||
|
new_instance = self. \
|
||||||
|
create_instance(dest_hostname,
|
||||||
|
instance_name,
|
||||||
|
image_uuid,
|
||||||
|
flavor_name,
|
||||||
|
sec_groups,
|
||||||
|
network_names_list=network_names_list,
|
||||||
|
keypair_name=keypair_name,
|
||||||
|
create_new_floating_ip=False,
|
||||||
|
block_device_mapping_v2=blocks)
|
||||||
|
|
||||||
|
if not new_instance:
|
||||||
|
LOG.debug(
|
||||||
|
"Could not create new instance "
|
||||||
|
"for non-live migration of instance %s", instance_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
LOG.debug(
|
||||||
|
"Detaching floating ip '%(floating_ip)s' "
|
||||||
|
"from instance %(instance)s",
|
||||||
|
{'floating_ip': floating_ip, 'instance': instance_id})
|
||||||
|
# We detach the floating ip from the current instance
|
||||||
|
instance.remove_floating_ip(floating_ip)
|
||||||
|
|
||||||
|
LOG.debug(
|
||||||
|
"Attaching floating ip '%(ip)s' to the new "
|
||||||
|
"instance %(id)s",
|
||||||
|
{'ip': floating_ip, 'id': new_instance.id})
|
||||||
|
|
||||||
|
# We attach the same floating ip to the new instance
|
||||||
|
new_instance.add_floating_ip(floating_ip)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.debug(e)
|
||||||
|
|
||||||
|
new_host_name = getattr(new_instance, "OS-EXT-SRV-ATTR:host")
|
||||||
|
|
||||||
|
# Deleting the old instance (because no more useful)
|
||||||
|
delete_ok = self.delete_instance(instance_id)
|
||||||
|
if not delete_ok:
|
||||||
|
LOG.debug("Could not delete instance: %s", instance_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
LOG.debug(
|
||||||
|
"Instance %s has been successfully migrated "
|
||||||
|
"to new host '%s' and its new id is %s.", (
|
||||||
|
instance_id, new_host_name, new_instance.id))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def resize_instance(self, instance_id, flavor, retry=120):
|
def resize_instance(self, instance_id, flavor, retry=120):
|
||||||
"""This method resizes given instance with specified flavor.
|
"""This method resizes given instance with specified flavor.
|
||||||
|
|
||||||
@@ -380,31 +557,21 @@ class NovaHelper(object):
|
|||||||
"for the instance %s" % instance_id)
|
"for the instance %s" % instance_id)
|
||||||
|
|
||||||
def enable_service_nova_compute(self, hostname):
|
def enable_service_nova_compute(self, hostname):
|
||||||
if float(CONF.nova_client.api_version) < 2.53:
|
if self.nova.services.enable(host=hostname,
|
||||||
status = self.nova.services.enable(
|
binary='nova-compute'). \
|
||||||
host=hostname, binary='nova-compute').status == 'enabled'
|
status == 'enabled':
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
service_uuid = self.nova.services.list(host=hostname,
|
return False
|
||||||
binary='nova-compute')[0].id
|
|
||||||
status = self.nova.services.enable(
|
|
||||||
service_uuid=service_uuid).status == 'enabled'
|
|
||||||
|
|
||||||
return status
|
|
||||||
|
|
||||||
def disable_service_nova_compute(self, hostname, reason=None):
|
def disable_service_nova_compute(self, hostname, reason=None):
|
||||||
if float(CONF.nova_client.api_version) < 2.53:
|
if self.nova.services.disable_log_reason(host=hostname,
|
||||||
status = self.nova.services.disable_log_reason(
|
binary='nova-compute',
|
||||||
host=hostname,
|
reason=reason). \
|
||||||
binary='nova-compute',
|
status == 'disabled':
|
||||||
reason=reason).status == 'disabled'
|
return True
|
||||||
else:
|
else:
|
||||||
service_uuid = self.nova.services.list(host=hostname,
|
return False
|
||||||
binary='nova-compute')[0].id
|
|
||||||
status = self.nova.services.disable_log_reason(
|
|
||||||
service_uuid=service_uuid,
|
|
||||||
reason=reason).status == 'disabled'
|
|
||||||
|
|
||||||
return status
|
|
||||||
|
|
||||||
def set_host_offline(self, hostname):
|
def set_host_offline(self, hostname):
|
||||||
# See API on https://developer.openstack.org/api-ref/compute/
|
# See API on https://developer.openstack.org/api-ref/compute/
|
||||||
@@ -705,8 +872,9 @@ class NovaHelper(object):
|
|||||||
|
|
||||||
def get_instances_by_node(self, host):
|
def get_instances_by_node(self, host):
|
||||||
return [instance for instance in
|
return [instance for instance in
|
||||||
self.nova.servers.list(search_opts={"all_tenants": True})
|
self.nova.servers.list(search_opts={"all_tenants": True,
|
||||||
if self.get_hostname(instance) == host]
|
"host": host},
|
||||||
|
limit=-1)]
|
||||||
|
|
||||||
def get_hostname(self, instance):
|
def get_hostname(self, instance):
|
||||||
return str(getattr(instance, 'OS-EXT-SRV-ATTR:host'))
|
return str(getattr(instance, 'OS-EXT-SRV-ATTR:host'))
|
||||||
@@ -757,12 +925,3 @@ class NovaHelper(object):
|
|||||||
"Volume %s is now on host '%s'.",
|
"Volume %s is now on host '%s'.",
|
||||||
(new_volume.id, host_name))
|
(new_volume.id, host_name))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _check_nova_api_version(self, client, version):
|
|
||||||
api_version = api_versions.APIVersion(version_str=version)
|
|
||||||
try:
|
|
||||||
api_versions.discover_version(client, api_version)
|
|
||||||
return True
|
|
||||||
except nvexceptions.UnsupportedVersion as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
return False
|
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ class Service(service.ServiceBase):
|
|||||||
return api_manager_version
|
return api_manager_version
|
||||||
|
|
||||||
|
|
||||||
def launch(conf, service_, workers=1, restart_method='mutate'):
|
def launch(conf, service_, workers=1, restart_method='reload'):
|
||||||
return service.launch(conf, service_, workers, restart_method)
|
return service.launch(conf, service_, workers, restart_method)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ CEILOMETER_CLIENT_OPTS = [
|
|||||||
default='internalURL',
|
default='internalURL',
|
||||||
help='Type of endpoint to use in ceilometerclient.'
|
help='Type of endpoint to use in ceilometerclient.'
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
'Supported values: internalURL, publicURL, adminURL'
|
||||||
'The default is internalURL.')]
|
'The default is internalURL.'),
|
||||||
|
cfg.StrOpt('region_name',
|
||||||
|
help='Region in Identity service catalog to use for '
|
||||||
|
'communication with the OpenStack service.')]
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ CINDER_CLIENT_OPTS = [
|
|||||||
default='publicURL',
|
default='publicURL',
|
||||||
help='Type of endpoint to use in cinderclient.'
|
help='Type of endpoint to use in cinderclient.'
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
'Supported values: internalURL, publicURL, adminURL'
|
||||||
'The default is publicURL.')]
|
'The default is publicURL.'),
|
||||||
|
cfg.StrOpt('region_name',
|
||||||
|
help='Region in Identity service catalog to use for '
|
||||||
|
'communication with the OpenStack service.')]
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -44,21 +44,18 @@ WATCHER_DECISION_ENGINE_OPTS = [
|
|||||||
'execute strategies'),
|
'execute strategies'),
|
||||||
cfg.IntOpt('action_plan_expiry',
|
cfg.IntOpt('action_plan_expiry',
|
||||||
default=24,
|
default=24,
|
||||||
mutable=True,
|
|
||||||
help='An expiry timespan(hours). Watcher invalidates any '
|
help='An expiry timespan(hours). Watcher invalidates any '
|
||||||
'action plan for which its creation time '
|
'action plan for which its creation time '
|
||||||
'-whose number of hours has been offset by this value-'
|
'-whose number of hours has been offset by this value-'
|
||||||
' is older that the current time.'),
|
' is older that the current time.'),
|
||||||
cfg.IntOpt('check_periodic_interval',
|
cfg.IntOpt('check_periodic_interval',
|
||||||
default=30 * 60,
|
default=30 * 60,
|
||||||
mutable=True,
|
|
||||||
help='Interval (in seconds) for checking action plan expiry.')
|
help='Interval (in seconds) for checking action plan expiry.')
|
||||||
]
|
]
|
||||||
|
|
||||||
WATCHER_CONTINUOUS_OPTS = [
|
WATCHER_CONTINUOUS_OPTS = [
|
||||||
cfg.IntOpt('continuous_audit_interval',
|
cfg.IntOpt('continuous_audit_interval',
|
||||||
default=10,
|
default=10,
|
||||||
mutable=True,
|
|
||||||
help='Interval (in seconds) for checking newly created '
|
help='Interval (in seconds) for checking newly created '
|
||||||
'continuous audits.')
|
'continuous audits.')
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ GLANCE_CLIENT_OPTS = [
|
|||||||
default='publicURL',
|
default='publicURL',
|
||||||
help='Type of endpoint to use in glanceclient.'
|
help='Type of endpoint to use in glanceclient.'
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
'Supported values: internalURL, publicURL, adminURL'
|
||||||
'The default is publicURL.')]
|
'The default is publicURL.'),
|
||||||
|
cfg.StrOpt('region_name',
|
||||||
|
help='Region in Identity service catalog to use for '
|
||||||
|
'communication with the OpenStack service.')]
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -30,13 +30,14 @@ GNOCCHI_CLIENT_OPTS = [
|
|||||||
help='Type of endpoint to use in gnocchi client.'
|
help='Type of endpoint to use in gnocchi client.'
|
||||||
'Supported values: internal, public, admin'
|
'Supported values: internal, public, admin'
|
||||||
'The default is public.'),
|
'The default is public.'),
|
||||||
|
cfg.StrOpt('region_name',
|
||||||
|
help='Region in Identity service catalog to use for '
|
||||||
|
'communication with the OpenStack service.'),
|
||||||
cfg.IntOpt('query_max_retries',
|
cfg.IntOpt('query_max_retries',
|
||||||
default=10,
|
default=10,
|
||||||
mutable=True,
|
|
||||||
help='How many times Watcher is trying to query again'),
|
help='How many times Watcher is trying to query again'),
|
||||||
cfg.IntOpt('query_timeout',
|
cfg.IntOpt('query_timeout',
|
||||||
default=1,
|
default=1,
|
||||||
mutable=True,
|
|
||||||
help='How many seconds Watcher should wait to do query again')]
|
help='How many seconds Watcher should wait to do query again')]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ IRONIC_CLIENT_OPTS = [
|
|||||||
default='publicURL',
|
default='publicURL',
|
||||||
help='Type of endpoint to use in ironicclient.'
|
help='Type of endpoint to use in ironicclient.'
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
'Supported values: internalURL, publicURL, adminURL'
|
||||||
'The default is publicURL.')]
|
'The default is publicURL.'),
|
||||||
|
cfg.StrOpt('region_name',
|
||||||
|
help='Region in Identity service catalog to use for '
|
||||||
|
'communication with the OpenStack service.')]
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ MONASCA_CLIENT_OPTS = [
|
|||||||
default='internal',
|
default='internal',
|
||||||
help='Type of interface used for monasca endpoint.'
|
help='Type of interface used for monasca endpoint.'
|
||||||
'Supported values: internal, public, admin'
|
'Supported values: internal, public, admin'
|
||||||
'The default is internal.')]
|
'The default is internal.'),
|
||||||
|
cfg.StrOpt('region_name',
|
||||||
|
help='Region in Identity service catalog to use for '
|
||||||
|
'communication with the OpenStack service.')]
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ NEUTRON_CLIENT_OPTS = [
|
|||||||
default='publicURL',
|
default='publicURL',
|
||||||
help='Type of endpoint to use in neutronclient.'
|
help='Type of endpoint to use in neutronclient.'
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
'Supported values: internalURL, publicURL, adminURL'
|
||||||
'The default is publicURL.')]
|
'The default is publicURL.'),
|
||||||
|
cfg.StrOpt('region_name',
|
||||||
|
help='Region in Identity service catalog to use for '
|
||||||
|
'communication with the OpenStack service.')]
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -23,13 +23,16 @@ nova_client = cfg.OptGroup(name='nova_client',
|
|||||||
|
|
||||||
NOVA_CLIENT_OPTS = [
|
NOVA_CLIENT_OPTS = [
|
||||||
cfg.StrOpt('api_version',
|
cfg.StrOpt('api_version',
|
||||||
default='2.56',
|
default='2.53',
|
||||||
help='Version of Nova API to use in novaclient.'),
|
help='Version of Nova API to use in novaclient.'),
|
||||||
cfg.StrOpt('endpoint_type',
|
cfg.StrOpt('endpoint_type',
|
||||||
default='publicURL',
|
default='publicURL',
|
||||||
help='Type of endpoint to use in novaclient.'
|
help='Type of endpoint to use in novaclient.'
|
||||||
'Supported values: internalURL, publicURL, adminURL'
|
'Supported values: internalURL, publicURL, adminURL'
|
||||||
'The default is publicURL.')]
|
'The default is publicURL.'),
|
||||||
|
cfg.StrOpt('region_name',
|
||||||
|
help='Region in Identity service catalog to use for '
|
||||||
|
'communication with the OpenStack service.')]
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ from watcher._i18n import _
|
|||||||
SERVICE_OPTS = [
|
SERVICE_OPTS = [
|
||||||
cfg.IntOpt('periodic_interval',
|
cfg.IntOpt('periodic_interval',
|
||||||
default=60,
|
default=60,
|
||||||
mutable=True,
|
|
||||||
help=_('Seconds between running periodic tasks.')),
|
help=_('Seconds between running periodic tasks.')),
|
||||||
cfg.HostAddressOpt('host',
|
cfg.HostAddressOpt('host',
|
||||||
default=socket.gethostname(),
|
default=socket.gethostname(),
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ class ModelBuilder(object):
|
|||||||
Create an instance node for the graph using nova and the
|
Create an instance node for the graph using nova and the
|
||||||
`server` nova object.
|
`server` nova object.
|
||||||
:param instance: Nova VM object.
|
:param instance: Nova VM object.
|
||||||
:return: An instance node for the graph.
|
:return: A instance node for the graph.
|
||||||
"""
|
"""
|
||||||
flavor = instance.flavor
|
flavor = instance.flavor
|
||||||
instance_attributes = {
|
instance_attributes = {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class InstanceState(enum.Enum):
|
|||||||
STOPPED = 'stopped' # Instance is shut off, the disk image is still there.
|
STOPPED = 'stopped' # Instance is shut off, the disk image is still there.
|
||||||
RESCUED = 'rescued' # A rescue image is running with the original image
|
RESCUED = 'rescued' # A rescue image is running with the original image
|
||||||
# attached.
|
# attached.
|
||||||
RESIZED = 'resized' # an Instance with the new size is active.
|
RESIZED = 'resized' # a Instance with the new size is active.
|
||||||
|
|
||||||
SOFT_DELETED = 'soft-delete'
|
SOFT_DELETED = 'soft-delete'
|
||||||
# still available to restore.
|
# still available to restore.
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class Pool(storage_resource.StorageResource):
|
|||||||
"free_capacity_gb": wfields.NonNegativeIntegerField(),
|
"free_capacity_gb": wfields.NonNegativeIntegerField(),
|
||||||
"provisioned_capacity_gb": wfields.NonNegativeIntegerField(),
|
"provisioned_capacity_gb": wfields.NonNegativeIntegerField(),
|
||||||
"allocated_capacity_gb": wfields.NonNegativeIntegerField(),
|
"allocated_capacity_gb": wfields.NonNegativeIntegerField(),
|
||||||
"virtual_free": wfields.NonNegativeIntegerField(default=0),
|
"virtual_free": wfields.NonNegativeIntegerField(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def accept(self, visitor):
|
def accept(self, visitor):
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ class StorageResource(base.Element):
|
|||||||
VERSION = '1.0'
|
VERSION = '1.0'
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
"uuid": wfields.StringField(default=""),
|
"uuid": wfields.StringField(),
|
||||||
"human_id": wfields.StringField(default=""),
|
"human_id": wfields.StringField(default=""),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
Openstack implementation of the cluster graph.
|
Openstack implementation of the cluster graph.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import ast
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
@@ -58,7 +57,7 @@ class ModelRoot(nx.DiGraph, base.Model):
|
|||||||
@lockutils.synchronized("model_root")
|
@lockutils.synchronized("model_root")
|
||||||
def add_node(self, node):
|
def add_node(self, node):
|
||||||
self.assert_node(node)
|
self.assert_node(node)
|
||||||
super(ModelRoot, self).add_node(node.uuid, attr=node)
|
super(ModelRoot, self).add_node(node.uuid, node)
|
||||||
|
|
||||||
@lockutils.synchronized("model_root")
|
@lockutils.synchronized("model_root")
|
||||||
def remove_node(self, node):
|
def remove_node(self, node):
|
||||||
@@ -73,7 +72,7 @@ class ModelRoot(nx.DiGraph, base.Model):
|
|||||||
def add_instance(self, instance):
|
def add_instance(self, instance):
|
||||||
self.assert_instance(instance)
|
self.assert_instance(instance)
|
||||||
try:
|
try:
|
||||||
super(ModelRoot, self).add_node(instance.uuid, attr=instance)
|
super(ModelRoot, self).add_node(instance.uuid, instance)
|
||||||
except nx.NetworkXError as exc:
|
except nx.NetworkXError as exc:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
raise exception.InstanceNotFound(name=instance.uuid)
|
raise exception.InstanceNotFound(name=instance.uuid)
|
||||||
@@ -138,8 +137,8 @@ class ModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
@lockutils.synchronized("model_root")
|
@lockutils.synchronized("model_root")
|
||||||
def get_all_compute_nodes(self):
|
def get_all_compute_nodes(self):
|
||||||
return {uuid: cn['attr'] for uuid, cn in self.nodes(data=True)
|
return {uuid: cn for uuid, cn in self.nodes(data=True)
|
||||||
if isinstance(cn['attr'], element.ComputeNode)}
|
if isinstance(cn, element.ComputeNode)}
|
||||||
|
|
||||||
@lockutils.synchronized("model_root")
|
@lockutils.synchronized("model_root")
|
||||||
def get_node_by_uuid(self, uuid):
|
def get_node_by_uuid(self, uuid):
|
||||||
@@ -157,7 +156,7 @@ class ModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
def _get_by_uuid(self, uuid):
|
def _get_by_uuid(self, uuid):
|
||||||
try:
|
try:
|
||||||
return self.node[uuid]['attr']
|
return self.node[uuid]
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
raise exception.ComputeResourceNotFound(name=uuid)
|
raise exception.ComputeResourceNotFound(name=uuid)
|
||||||
@@ -173,8 +172,8 @@ class ModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
@lockutils.synchronized("model_root")
|
@lockutils.synchronized("model_root")
|
||||||
def get_all_instances(self):
|
def get_all_instances(self):
|
||||||
return {uuid: inst['attr'] for uuid, inst in self.nodes(data=True)
|
return {uuid: inst for uuid, inst in self.nodes(data=True)
|
||||||
if isinstance(inst['attr'], element.Instance)}
|
if isinstance(inst, element.Instance)}
|
||||||
|
|
||||||
@lockutils.synchronized("model_root")
|
@lockutils.synchronized("model_root")
|
||||||
def get_node_instances(self, node):
|
def get_node_instances(self, node):
|
||||||
@@ -226,8 +225,6 @@ class ModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
for inst in root.findall('.//Instance'):
|
for inst in root.findall('.//Instance'):
|
||||||
instance = element.Instance(**inst.attrib)
|
instance = element.Instance(**inst.attrib)
|
||||||
instance.watcher_exclude = ast.literal_eval(
|
|
||||||
inst.attrib["watcher_exclude"])
|
|
||||||
model.add_instance(instance)
|
model.add_instance(instance)
|
||||||
|
|
||||||
parent = inst.getparent()
|
parent = inst.getparent()
|
||||||
@@ -242,7 +239,7 @@ class ModelRoot(nx.DiGraph, base.Model):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def is_isomorphic(cls, G1, G2):
|
def is_isomorphic(cls, G1, G2):
|
||||||
def node_match(node1, node2):
|
def node_match(node1, node2):
|
||||||
return node1['attr'].as_dict() == node2['attr'].as_dict()
|
return node1.as_dict() == node2.as_dict()
|
||||||
return nx.algorithms.isomorphism.isomorph.is_isomorphic(
|
return nx.algorithms.isomorphism.isomorph.is_isomorphic(
|
||||||
G1, G2, node_match=node_match)
|
G1, G2, node_match=node_match)
|
||||||
|
|
||||||
@@ -280,12 +277,12 @@ class StorageModelRoot(nx.DiGraph, base.Model):
|
|||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def add_node(self, node):
|
def add_node(self, node):
|
||||||
self.assert_node(node)
|
self.assert_node(node)
|
||||||
super(StorageModelRoot, self).add_node(node.host, attr=node)
|
super(StorageModelRoot, self).add_node(node.host, node)
|
||||||
|
|
||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def add_pool(self, pool):
|
def add_pool(self, pool):
|
||||||
self.assert_pool(pool)
|
self.assert_pool(pool)
|
||||||
super(StorageModelRoot, self).add_node(pool.name, attr=pool)
|
super(StorageModelRoot, self).add_node(pool.name, pool)
|
||||||
|
|
||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def remove_node(self, node):
|
def remove_node(self, node):
|
||||||
@@ -338,7 +335,7 @@ class StorageModelRoot(nx.DiGraph, base.Model):
|
|||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def add_volume(self, volume):
|
def add_volume(self, volume):
|
||||||
self.assert_volume(volume)
|
self.assert_volume(volume)
|
||||||
super(StorageModelRoot, self).add_node(volume.uuid, attr=volume)
|
super(StorageModelRoot, self).add_node(volume.uuid, volume)
|
||||||
|
|
||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def remove_volume(self, volume):
|
def remove_volume(self, volume):
|
||||||
@@ -385,8 +382,8 @@ class StorageModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def get_all_storage_nodes(self):
|
def get_all_storage_nodes(self):
|
||||||
return {host: cn['attr'] for host, cn in self.nodes(data=True)
|
return {host: cn for host, cn in self.nodes(data=True)
|
||||||
if isinstance(cn['attr'], element.StorageNode)}
|
if isinstance(cn, element.StorageNode)}
|
||||||
|
|
||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def get_node_by_name(self, name):
|
def get_node_by_name(self, name):
|
||||||
@@ -415,14 +412,14 @@ class StorageModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
def _get_by_uuid(self, uuid):
|
def _get_by_uuid(self, uuid):
|
||||||
try:
|
try:
|
||||||
return self.node[uuid]['attr']
|
return self.node[uuid]
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
raise exception.StorageResourceNotFound(name=uuid)
|
raise exception.StorageResourceNotFound(name=uuid)
|
||||||
|
|
||||||
def _get_by_name(self, name):
|
def _get_by_name(self, name):
|
||||||
try:
|
try:
|
||||||
return self.node[name]['attr']
|
return self.node[name]
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
raise exception.StorageResourceNotFound(name=name)
|
raise exception.StorageResourceNotFound(name=name)
|
||||||
@@ -459,8 +456,8 @@ class StorageModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def get_all_volumes(self):
|
def get_all_volumes(self):
|
||||||
return {name: vol['attr'] for name, vol in self.nodes(data=True)
|
return {name: vol for name, vol in self.nodes(data=True)
|
||||||
if isinstance(vol['attr'], element.Volume)}
|
if isinstance(vol, element.Volume)}
|
||||||
|
|
||||||
@lockutils.synchronized("storage_model")
|
@lockutils.synchronized("storage_model")
|
||||||
def get_pool_volumes(self, pool):
|
def get_pool_volumes(self, pool):
|
||||||
@@ -572,7 +569,7 @@ class BaremetalModelRoot(nx.DiGraph, base.Model):
|
|||||||
@lockutils.synchronized("baremetal_model")
|
@lockutils.synchronized("baremetal_model")
|
||||||
def add_node(self, node):
|
def add_node(self, node):
|
||||||
self.assert_node(node)
|
self.assert_node(node)
|
||||||
super(BaremetalModelRoot, self).add_node(node.uuid, attr=node)
|
super(BaremetalModelRoot, self).add_node(node.uuid, node)
|
||||||
|
|
||||||
@lockutils.synchronized("baremetal_model")
|
@lockutils.synchronized("baremetal_model")
|
||||||
def remove_node(self, node):
|
def remove_node(self, node):
|
||||||
@@ -585,8 +582,8 @@ class BaremetalModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
@lockutils.synchronized("baremetal_model")
|
@lockutils.synchronized("baremetal_model")
|
||||||
def get_all_ironic_nodes(self):
|
def get_all_ironic_nodes(self):
|
||||||
return {uuid: cn['attr'] for uuid, cn in self.nodes(data=True)
|
return {uuid: cn for uuid, cn in self.nodes(data=True)
|
||||||
if isinstance(cn['attr'], element.IronicNode)}
|
if isinstance(cn, element.IronicNode)}
|
||||||
|
|
||||||
@lockutils.synchronized("baremetal_model")
|
@lockutils.synchronized("baremetal_model")
|
||||||
def get_node_by_uuid(self, uuid):
|
def get_node_by_uuid(self, uuid):
|
||||||
@@ -597,7 +594,7 @@ class BaremetalModelRoot(nx.DiGraph, base.Model):
|
|||||||
|
|
||||||
def _get_by_uuid(self, uuid):
|
def _get_by_uuid(self, uuid):
|
||||||
try:
|
try:
|
||||||
return self.node[uuid]['attr']
|
return self.node[uuid]
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
raise exception.BaremetalResourceNotFound(name=uuid)
|
raise exception.BaremetalResourceNotFound(name=uuid)
|
||||||
|
|||||||
@@ -14,6 +14,16 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
*Actuator*
|
||||||
|
|
||||||
|
This strategy allows anyone to create an action plan with a predefined set of
|
||||||
|
actions. This strategy can be used for 2 different purposes:
|
||||||
|
|
||||||
|
- Test actions
|
||||||
|
- Use this strategy based on an event trigger to perform some explicit task
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
@@ -24,17 +34,7 @@ LOG = log.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Actuator(base.UnclassifiedStrategy):
|
class Actuator(base.UnclassifiedStrategy):
|
||||||
"""Actuator
|
"""Actuator that simply executes the actions given as parameter"""
|
||||||
|
|
||||||
Actuator that simply executes the actions given as parameter
|
|
||||||
|
|
||||||
This strategy allows anyone to create an action plan with a predefined
|
|
||||||
set of actions. This strategy can be used for 2 different purposes:
|
|
||||||
|
|
||||||
- Test actions
|
|
||||||
- Use this strategy based on an event trigger to perform some explicit task
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_name(cls):
|
def get_name(cls):
|
||||||
|
|||||||
@@ -16,6 +16,24 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
*Good server consolidation strategy*
|
||||||
|
|
||||||
|
Consolidation of VMs is essential to achieve energy optimization in cloud
|
||||||
|
environments such as OpenStack. As VMs are spinned up and/or moved over time,
|
||||||
|
it becomes necessary to migrate VMs among servers to lower the costs. However,
|
||||||
|
migration of VMs introduces runtime overheads and consumes extra energy, thus
|
||||||
|
a good server consolidation strategy should carefully plan for migration in
|
||||||
|
order to both minimize energy consumption and comply to the various SLAs.
|
||||||
|
|
||||||
|
This algorithm not only minimizes the overall number of used servers, but also
|
||||||
|
minimizes the number of migrations.
|
||||||
|
|
||||||
|
It has been developed only for tests. You must have at least 2 physical compute
|
||||||
|
nodes to run it, so you can easily run it on DevStack. It assumes that live
|
||||||
|
migration is possible on your OpenStack cluster.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
@@ -29,25 +47,7 @@ LOG = log.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
||||||
"""Good server consolidation strategy
|
"""Basic offline consolidation using live migration"""
|
||||||
|
|
||||||
Basic offline consolidation using live migration
|
|
||||||
|
|
||||||
Consolidation of VMs is essential to achieve energy optimization in cloud
|
|
||||||
environments such as OpenStack. As VMs are spinned up and/or moved over
|
|
||||||
time, it becomes necessary to migrate VMs among servers to lower the
|
|
||||||
costs. However, migration of VMs introduces runtime overheads and
|
|
||||||
consumes extra energy, thus a good server consolidation strategy should
|
|
||||||
carefully plan for migration in order to both minimize energy consumption
|
|
||||||
and comply to the various SLAs.
|
|
||||||
|
|
||||||
This algorithm not only minimizes the overall number of used servers,
|
|
||||||
but also minimizes the number of migrations.
|
|
||||||
|
|
||||||
It has been developed only for tests. You must have at least 2 physical
|
|
||||||
compute nodes to run it, so you can easily run it on DevStack. It assumes
|
|
||||||
that live migration is possible on your OpenStack cluster.
|
|
||||||
"""
|
|
||||||
|
|
||||||
HOST_CPU_USAGE_METRIC_NAME = 'compute.node.cpu.percent'
|
HOST_CPU_USAGE_METRIC_NAME = 'compute.node.cpu.percent'
|
||||||
INSTANCE_CPU_USAGE_METRIC_NAME = 'cpu_util'
|
INSTANCE_CPU_USAGE_METRIC_NAME = 'cpu_util'
|
||||||
@@ -109,12 +109,6 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
def granularity(self):
|
def granularity(self):
|
||||||
return self.input_parameters.get('granularity', 300)
|
return self.input_parameters.get('granularity', 300)
|
||||||
|
|
||||||
@property
|
|
||||||
def aggregation_method(self):
|
|
||||||
return self.input_parameters.get(
|
|
||||||
'aggregation_method',
|
|
||||||
{"instance": 'mean', "node": 'mean'})
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_display_name(cls):
|
def get_display_name(cls):
|
||||||
return _("Basic offline consolidation")
|
return _("Basic offline consolidation")
|
||||||
@@ -148,26 +142,6 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
"type": "number",
|
"type": "number",
|
||||||
"default": 300
|
"default": 300
|
||||||
},
|
},
|
||||||
"aggregation_method": {
|
|
||||||
"description": "Function used to aggregate multiple "
|
|
||||||
"measures into an aggregate. For example, "
|
|
||||||
"the min aggregation method will aggregate "
|
|
||||||
"the values of different measures to the "
|
|
||||||
"minimum value of all the measures in the "
|
|
||||||
"time range.",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"instance": {
|
|
||||||
"type": "string",
|
|
||||||
"default": 'mean'
|
|
||||||
},
|
|
||||||
"node": {
|
|
||||||
"type": "string",
|
|
||||||
"default": 'mean'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"default": {"instance": 'mean', "node": 'mean'}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +178,7 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
:param source_node: the current node of the virtual machine
|
:param source_node: the current node of the virtual machine
|
||||||
:param destination_node: the destination of the virtual machine
|
:param destination_node: the destination of the virtual machine
|
||||||
:param instance_to_migrate: the instance / virtual machine
|
:param instance_to_migrate: the instance / virtual machine
|
||||||
:return: True if there is enough place otherwise false
|
:return: True if the there is enough place otherwise false
|
||||||
"""
|
"""
|
||||||
if source_node == destination_node:
|
if source_node == destination_node:
|
||||||
return False
|
return False
|
||||||
@@ -284,13 +258,11 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
def get_node_cpu_usage(self, node):
|
def get_node_cpu_usage(self, node):
|
||||||
resource_id = "%s_%s" % (node.uuid, node.hostname)
|
resource_id = "%s_%s" % (node.uuid, node.hostname)
|
||||||
return self.datasource_backend.get_host_cpu_usage(
|
return self.datasource_backend.get_host_cpu_usage(
|
||||||
resource_id, self.period, self.aggregation_method['node'],
|
resource_id, self.period, 'mean', granularity=300)
|
||||||
granularity=self.granularity)
|
|
||||||
|
|
||||||
def get_instance_cpu_usage(self, instance):
|
def get_instance_cpu_usage(self, instance):
|
||||||
return self.datasource_backend.get_instance_cpu_usage(
|
return self.datasource_backend.get_instance_cpu_usage(
|
||||||
instance.uuid, self.period, self.aggregation_method['instance'],
|
instance.uuid, self.period, 'mean', granularity=300)
|
||||||
granularity=self.granularity)
|
|
||||||
|
|
||||||
def calculate_score_node(self, node):
|
def calculate_score_node(self, node):
|
||||||
"""Calculate the score that represent the utilization level
|
"""Calculate the score that represent the utilization level
|
||||||
@@ -400,11 +372,6 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
sorted_score):
|
sorted_score):
|
||||||
number_migrations = 0
|
number_migrations = 0
|
||||||
for mig_instance, __ in sorted_instances:
|
for mig_instance, __ in sorted_instances:
|
||||||
# skip exclude instance when migrating
|
|
||||||
if mig_instance.watcher_exclude:
|
|
||||||
LOG.debug("Instance is excluded by scope, "
|
|
||||||
"skipped: %s", mig_instance.uuid)
|
|
||||||
continue
|
|
||||||
for node_uuid, __ in sorted_score:
|
for node_uuid, __ in sorted_score:
|
||||||
mig_source_node = self.compute_model.get_node_by_uuid(
|
mig_source_node = self.compute_model.get_node_by_uuid(
|
||||||
node_to_release)
|
node_to_release)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
"""
|
"""
|
||||||
*Good Thermal Strategy*
|
*Good Thermal Strategy*:
|
||||||
|
|
||||||
Towards to software defined infrastructure, the power and thermal
|
Towards to software defined infrastructure, the power and thermal
|
||||||
intelligences is being adopted to optimize workload, which can help
|
intelligences is being adopted to optimize workload, which can help
|
||||||
@@ -26,10 +26,6 @@ improve efficiency, reduce power, as well as to improve datacenter PUE
|
|||||||
and lower down operation cost in data center.
|
and lower down operation cost in data center.
|
||||||
Outlet (Exhaust Air) Temperature is one of the important thermal
|
Outlet (Exhaust Air) Temperature is one of the important thermal
|
||||||
telemetries to measure thermal/workload status of server.
|
telemetries to measure thermal/workload status of server.
|
||||||
|
|
||||||
This strategy makes decisions to migrate workloads to the hosts with good
|
|
||||||
thermal condition (lowest outlet temperature) when the outlet temperature
|
|
||||||
of source hosts reach a configurable threshold.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@@ -216,11 +212,6 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
|
|||||||
mig_source_node)
|
mig_source_node)
|
||||||
for instance in instances_of_src:
|
for instance in instances_of_src:
|
||||||
try:
|
try:
|
||||||
# NOTE: skip exclude instance when migrating
|
|
||||||
if instance.watcher_exclude:
|
|
||||||
LOG.debug("Instance is excluded by scope, "
|
|
||||||
"skipped: %s", instance.uuid)
|
|
||||||
continue
|
|
||||||
# select the first active instance to migrate
|
# select the first active instance to migrate
|
||||||
if (instance.state !=
|
if (instance.state !=
|
||||||
element.InstanceState.ACTIVE.value):
|
element.InstanceState.ACTIVE.value):
|
||||||
|
|||||||
@@ -14,6 +14,24 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
*Workload balance using cinder volume migration*
|
||||||
|
|
||||||
|
*Description*
|
||||||
|
|
||||||
|
This strategy migrates volumes based on the workload of the
|
||||||
|
cinder pools.
|
||||||
|
It makes decision to migrate a volume whenever a pool's used
|
||||||
|
utilization % is higher than the specified threshold. The volume
|
||||||
|
to be moved should make the pool close to average workload of all
|
||||||
|
cinder pools.
|
||||||
|
|
||||||
|
*Requirements*
|
||||||
|
|
||||||
|
* You must have at least 2 cinder volume pools to run
|
||||||
|
this strategy.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|||||||
@@ -16,6 +16,31 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
[PoC]Uniform Airflow using live migration
|
||||||
|
|
||||||
|
*Description*
|
||||||
|
|
||||||
|
It is a migration strategy based on the airflow of physical
|
||||||
|
servers. It generates solutions to move VM whenever a server's
|
||||||
|
airflow is higher than the specified threshold.
|
||||||
|
|
||||||
|
*Requirements*
|
||||||
|
|
||||||
|
* Hardware: compute node with NodeManager 3.0 support
|
||||||
|
* Software: Ceilometer component ceilometer-agent-compute running
|
||||||
|
in each compute node, and Ceilometer API can report such telemetry
|
||||||
|
"airflow, system power, inlet temperature" successfully.
|
||||||
|
* You must have at least 2 physical compute nodes to run this strategy
|
||||||
|
|
||||||
|
*Limitations*
|
||||||
|
|
||||||
|
- This is a proof of concept that is not meant to be used in production.
|
||||||
|
- We cannot forecast how many servers should be migrated. This is the
|
||||||
|
reason why we only plan a single virtual machine migration at a time.
|
||||||
|
So it's better to use this algorithm with `CONTINUOUS` audits.
|
||||||
|
- It assumes that live migrations are possible.
|
||||||
|
"""
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
@@ -215,11 +240,6 @@ class UniformAirflow(base.BaseStrategy):
|
|||||||
else:
|
else:
|
||||||
# migrate the first active instance
|
# migrate the first active instance
|
||||||
for instance in source_instances:
|
for instance in source_instances:
|
||||||
# NOTE: skip exclude instance when migrating
|
|
||||||
if instance.watcher_exclude:
|
|
||||||
LOG.debug("Instance is excluded by scope, "
|
|
||||||
"skipped: %s", instance.uuid)
|
|
||||||
continue
|
|
||||||
if (instance.state !=
|
if (instance.state !=
|
||||||
element.InstanceState.ACTIVE.value):
|
element.InstanceState.ACTIVE.value):
|
||||||
LOG.info(
|
LOG.info(
|
||||||
|
|||||||
@@ -17,6 +17,41 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
*VM Workload Consolidation Strategy*
|
||||||
|
|
||||||
|
A load consolidation strategy based on heuristic first-fit
|
||||||
|
algorithm which focuses on measured CPU utilization and tries to
|
||||||
|
minimize hosts which have too much or too little load respecting
|
||||||
|
resource capacity constraints.
|
||||||
|
|
||||||
|
This strategy produces a solution resulting in more efficient
|
||||||
|
utilization of cluster resources using following four phases:
|
||||||
|
|
||||||
|
* Offload phase - handling over-utilized resources
|
||||||
|
* Consolidation phase - handling under-utilized resources
|
||||||
|
* Solution optimization - reducing number of migrations
|
||||||
|
* Disability of unused compute nodes
|
||||||
|
|
||||||
|
A capacity coefficients (cc) might be used to adjust optimization
|
||||||
|
thresholds. Different resources may require different coefficient
|
||||||
|
values as well as setting up different coefficient values in both
|
||||||
|
phases may lead to to more efficient consolidation in the end.
|
||||||
|
If the cc equals 1 the full resource capacity may be used, cc
|
||||||
|
values lower than 1 will lead to resource under utilization and
|
||||||
|
values higher than 1 will lead to resource overbooking.
|
||||||
|
e.g. If targeted utilization is 80 percent of a compute node capacity,
|
||||||
|
the coefficient in the consolidation phase will be 0.8, but
|
||||||
|
may any lower value in the offloading phase. The lower it gets
|
||||||
|
the cluster will appear more released (distributed) for the
|
||||||
|
following consolidation phase.
|
||||||
|
|
||||||
|
As this strategy leverages VM live migration to move the load
|
||||||
|
from one compute node to another, this feature needs to be set up
|
||||||
|
correctly on all compute nodes within the cluster.
|
||||||
|
This strategy assumes it is possible to live migrate any VM from
|
||||||
|
an active compute node to any other active compute node.
|
||||||
|
"""
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
@@ -31,40 +66,7 @@ LOG = log.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
|
class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
|
||||||
"""VM Workload Consolidation Strategy
|
"""VM Workload Consolidation Strategy"""
|
||||||
|
|
||||||
A load consolidation strategy based on heuristic first-fit
|
|
||||||
algorithm which focuses on measured CPU utilization and tries to
|
|
||||||
minimize hosts which have too much or too little load respecting
|
|
||||||
resource capacity constraints.
|
|
||||||
|
|
||||||
This strategy produces a solution resulting in more efficient
|
|
||||||
utilization of cluster resources using following four phases:
|
|
||||||
|
|
||||||
* Offload phase - handling over-utilized resources
|
|
||||||
* Consolidation phase - handling under-utilized resources
|
|
||||||
* Solution optimization - reducing number of migrations
|
|
||||||
* Disability of unused compute nodes
|
|
||||||
|
|
||||||
A capacity coefficients (cc) might be used to adjust optimization
|
|
||||||
thresholds. Different resources may require different coefficient
|
|
||||||
values as well as setting up different coefficient values in both
|
|
||||||
phases may lead to to more efficient consolidation in the end.
|
|
||||||
If the cc equals 1 the full resource capacity may be used, cc
|
|
||||||
values lower than 1 will lead to resource under utilization and
|
|
||||||
values higher than 1 will lead to resource overbooking.
|
|
||||||
e.g. If targeted utilization is 80 percent of a compute node capacity,
|
|
||||||
the coefficient in the consolidation phase will be 0.8, but
|
|
||||||
may any lower value in the offloading phase. The lower it gets
|
|
||||||
the cluster will appear more released (distributed) for the
|
|
||||||
following consolidation phase.
|
|
||||||
|
|
||||||
As this strategy leverages VM live migration to move the load
|
|
||||||
from one compute node to another, this feature needs to be set up
|
|
||||||
correctly on all compute nodes within the cluster.
|
|
||||||
This strategy assumes it is possible to live migrate any VM from
|
|
||||||
an active compute node to any other active compute node.
|
|
||||||
"""
|
|
||||||
|
|
||||||
HOST_CPU_USAGE_METRIC_NAME = 'compute.node.cpu.percent'
|
HOST_CPU_USAGE_METRIC_NAME = 'compute.node.cpu.percent'
|
||||||
INSTANCE_CPU_USAGE_METRIC_NAME = 'cpu_util'
|
INSTANCE_CPU_USAGE_METRIC_NAME = 'cpu_util'
|
||||||
@@ -502,11 +504,6 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
key=lambda x: self.get_instance_utilization(
|
key=lambda x: self.get_instance_utilization(
|
||||||
x)['cpu']
|
x)['cpu']
|
||||||
):
|
):
|
||||||
# skip exclude instance when migrating
|
|
||||||
if instance.watcher_exclude:
|
|
||||||
LOG.debug("Instance is excluded by scope, "
|
|
||||||
"skipped: %s", instance.uuid)
|
|
||||||
continue
|
|
||||||
for destination_node in reversed(sorted_nodes):
|
for destination_node in reversed(sorted_nodes):
|
||||||
if self.instance_fits(
|
if self.instance_fits(
|
||||||
instance, destination_node, cc):
|
instance, destination_node, cc):
|
||||||
@@ -539,11 +536,6 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
self.compute_model.get_node_instances(node),
|
self.compute_model.get_node_instances(node),
|
||||||
key=lambda x: self.get_instance_utilization(x)['cpu'])
|
key=lambda x: self.get_instance_utilization(x)['cpu'])
|
||||||
for instance in reversed(instances):
|
for instance in reversed(instances):
|
||||||
# skip exclude instance when migrating
|
|
||||||
if instance.watcher_exclude:
|
|
||||||
LOG.debug("Instance is excluded by scope, "
|
|
||||||
"skipped: %s", instance.uuid)
|
|
||||||
continue
|
|
||||||
dsc = len(sorted_nodes) - 1
|
dsc = len(sorted_nodes) - 1
|
||||||
for destination_node in reversed(sorted_nodes):
|
for destination_node in reversed(sorted_nodes):
|
||||||
if asc >= dsc:
|
if asc >= dsc:
|
||||||
|
|||||||
@@ -16,6 +16,35 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
*[PoC]Workload balance using live migration*
|
||||||
|
|
||||||
|
*Description*
|
||||||
|
|
||||||
|
This strategy migrates a VM based on the VM workload of the hosts.
|
||||||
|
It makes decision to migrate a workload whenever a host's CPU or RAM
|
||||||
|
utilization % is higher than the specified threshold. The VM to
|
||||||
|
be moved should make the host close to average workload of all
|
||||||
|
hosts nodes.
|
||||||
|
|
||||||
|
*Requirements*
|
||||||
|
|
||||||
|
* Hardware: compute node should use the same physical CPUs
|
||||||
|
* Software: Ceilometer component ceilometer-agent-compute
|
||||||
|
running in each compute node, and Ceilometer API can
|
||||||
|
report such telemetry "cpu_util" and "memory.resident" successfully.
|
||||||
|
* You must have at least 2 physical compute nodes to run
|
||||||
|
this strategy.
|
||||||
|
|
||||||
|
*Limitations*
|
||||||
|
|
||||||
|
- This is a proof of concept that is not meant to be used in
|
||||||
|
production.
|
||||||
|
- We cannot forecast how many servers should be migrated.
|
||||||
|
This is the reason why we only plan a single virtual
|
||||||
|
machine migration at a time. So it's better to use this
|
||||||
|
algorithm with `CONTINUOUS` audits.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
|
||||||
@@ -47,7 +76,7 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
|
|||||||
* Software: Ceilometer component ceilometer-agent-compute running
|
* Software: Ceilometer component ceilometer-agent-compute running
|
||||||
in each compute node, and Ceilometer API can report such telemetry
|
in each compute node, and Ceilometer API can report such telemetry
|
||||||
"cpu_util" and "memory.resident" successfully.
|
"cpu_util" and "memory.resident" successfully.
|
||||||
* You must have at least 2 physical compute nodes to run this strategy.
|
* You must have at least 2 physical compute nodes to run this strategy
|
||||||
|
|
||||||
*Limitations*
|
*Limitations*
|
||||||
|
|
||||||
@@ -179,11 +208,6 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
|
|||||||
instance_id = None
|
instance_id = None
|
||||||
for instance in source_instances:
|
for instance in source_instances:
|
||||||
try:
|
try:
|
||||||
# NOTE: skip exclude instance when migrating
|
|
||||||
if instance.watcher_exclude:
|
|
||||||
LOG.debug("Instance is excluded by scope, "
|
|
||||||
"skipped: %s", instance.uuid)
|
|
||||||
continue
|
|
||||||
# select the first active VM to migrate
|
# select the first active VM to migrate
|
||||||
if (instance.state !=
|
if (instance.state !=
|
||||||
element.InstanceState.ACTIVE.value):
|
element.InstanceState.ACTIVE.value):
|
||||||
|
|||||||
@@ -16,6 +16,16 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
*Workload Stabilization control using live migration*
|
||||||
|
|
||||||
|
This is workload stabilization strategy based on standard deviation
|
||||||
|
algorithm. The goal is to determine if there is an overload in a cluster
|
||||||
|
and respond to it by migrating VMs to stabilize the cluster.
|
||||||
|
|
||||||
|
It assumes that live migrations are possible in your cluster.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import itertools
|
import itertools
|
||||||
@@ -47,16 +57,7 @@ def _set_memoize(conf):
|
|||||||
|
|
||||||
|
|
||||||
class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
||||||
"""Workload Stabilization control using live migration
|
"""Workload Stabilization control using live migration"""
|
||||||
|
|
||||||
This is workload stabilization strategy based on standard deviation
|
|
||||||
algorithm. The goal is to determine if there is an overload in a cluster
|
|
||||||
and respond to it by migrating VMs to stabilize the cluster.
|
|
||||||
|
|
||||||
This strategy has been tested in a small (32 nodes) cluster.
|
|
||||||
|
|
||||||
It assumes that live migrations are possible in your cluster.
|
|
||||||
"""
|
|
||||||
|
|
||||||
MIGRATION = "migrate"
|
MIGRATION = "migrate"
|
||||||
MEMOIZE = _set_memoize(CONF)
|
MEMOIZE = _set_memoize(CONF)
|
||||||
@@ -79,7 +80,6 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
self.instance_metrics = None
|
self.instance_metrics = None
|
||||||
self.retry_count = None
|
self.retry_count = None
|
||||||
self.periods = None
|
self.periods = None
|
||||||
self.aggregation_method = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_name(cls):
|
def get_name(cls):
|
||||||
@@ -104,47 +104,19 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
"metrics": {
|
"metrics": {
|
||||||
"description": "Metrics used as rates of cluster loads.",
|
"description": "Metrics used as rates of cluster loads.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"default": ["cpu_util", "memory.resident"]
|
||||||
"type": "string",
|
|
||||||
"enum": ["cpu_util", "memory.resident"]
|
|
||||||
},
|
|
||||||
"default": ["cpu_util"]
|
|
||||||
},
|
},
|
||||||
"thresholds": {
|
"thresholds": {
|
||||||
"description": "Dict where key is a metric and value "
|
"description": "Dict where key is a metric and value "
|
||||||
"is a trigger value.",
|
"is a trigger value.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"default": {"cpu_util": 0.2, "memory.resident": 0.2}
|
||||||
"cpu_util": {
|
|
||||||
"type": "number",
|
|
||||||
"minimum": 0,
|
|
||||||
"maximum": 1
|
|
||||||
},
|
|
||||||
"memory.resident": {
|
|
||||||
"type": "number",
|
|
||||||
"minimum": 0,
|
|
||||||
"maximum": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {"cpu_util": 0.1, "memory.resident": 0.1}
|
|
||||||
},
|
},
|
||||||
"weights": {
|
"weights": {
|
||||||
"description": "These weights used to calculate "
|
"description": "These weights used to calculate "
|
||||||
"common standard deviation. Name of weight"
|
"common standard deviation. Name of weight"
|
||||||
" contains meter name and _weight suffix.",
|
" contains meter name and _weight suffix.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
|
||||||
"cpu_util_weight": {
|
|
||||||
"type": "number",
|
|
||||||
"minimum": 0,
|
|
||||||
"maximum": 1
|
|
||||||
},
|
|
||||||
"memory.resident_weight": {
|
|
||||||
"type": "number",
|
|
||||||
"minimum": 0,
|
|
||||||
"maximum": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {"cpu_util_weight": 1.0,
|
"default": {"cpu_util_weight": 1.0,
|
||||||
"memory.resident_weight": 1.0}
|
"memory.resident_weight": 1.0}
|
||||||
},
|
},
|
||||||
@@ -169,7 +141,6 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
"retry_count": {
|
"retry_count": {
|
||||||
"description": "Count of random returned hosts",
|
"description": "Count of random returned hosts",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"minimum": 1,
|
|
||||||
"default": 1
|
"default": 1
|
||||||
},
|
},
|
||||||
"periods": {
|
"periods": {
|
||||||
@@ -181,43 +152,12 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
"uses only the last period of all received"
|
"uses only the last period of all received"
|
||||||
" ones.",
|
" ones.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
|
||||||
"instance": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0
|
|
||||||
},
|
|
||||||
"node": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"default": {"instance": 720, "node": 600}
|
"default": {"instance": 720, "node": 600}
|
||||||
},
|
},
|
||||||
"aggregation_method": {
|
|
||||||
"description": "Function used to aggregate multiple "
|
|
||||||
"measures into an aggregate. For example, "
|
|
||||||
"the min aggregation method will aggregate "
|
|
||||||
"the values of different measures to the "
|
|
||||||
"minimum value of all the measures in the "
|
|
||||||
"time range.",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"instance": {
|
|
||||||
"type": "string",
|
|
||||||
"default": 'mean'
|
|
||||||
},
|
|
||||||
"node": {
|
|
||||||
"type": "string",
|
|
||||||
"default": 'mean'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"default": {"instance": 'mean', "node": 'mean'}
|
|
||||||
},
|
|
||||||
"granularity": {
|
"granularity": {
|
||||||
"description": "The time between two measures in an "
|
"description": "The time between two measures in an "
|
||||||
"aggregated timeseries of a metric.",
|
"aggregated timeseries of a metric.",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"minimum": 0,
|
|
||||||
"default": 300
|
"default": 300
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -259,8 +199,7 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
for meter in self.metrics:
|
for meter in self.metrics:
|
||||||
avg_meter = self.datasource_backend.statistic_aggregation(
|
avg_meter = self.datasource_backend.statistic_aggregation(
|
||||||
instance.uuid, meter, self.periods['instance'],
|
instance.uuid, meter, self.periods['instance'],
|
||||||
self.granularity,
|
self.granularity, aggregation='mean')
|
||||||
aggregation=self.aggregation_method['instance'])
|
|
||||||
if avg_meter is None:
|
if avg_meter is None:
|
||||||
LOG.warning(
|
LOG.warning(
|
||||||
"No values returned by %(resource_id)s "
|
"No values returned by %(resource_id)s "
|
||||||
@@ -303,8 +242,7 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
resource_id = node_id
|
resource_id = node_id
|
||||||
avg_meter = self.datasource_backend.statistic_aggregation(
|
avg_meter = self.datasource_backend.statistic_aggregation(
|
||||||
resource_id, self.instance_metrics[metric],
|
resource_id, self.instance_metrics[metric],
|
||||||
self.periods['node'], self.granularity,
|
self.periods['node'], self.granularity, aggregation='mean')
|
||||||
aggregation=self.aggregation_method['node'])
|
|
||||||
if avg_meter is None:
|
if avg_meter is None:
|
||||||
LOG.warning('No values returned by node %s for %s',
|
LOG.warning('No values returned by node %s for %s',
|
||||||
node_id, meter_name)
|
node_id, meter_name)
|
||||||
@@ -363,7 +301,7 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
s_host_vcpus = new_hosts[src_node.uuid]['vcpus']
|
s_host_vcpus = new_hosts[src_node.uuid]['vcpus']
|
||||||
d_host_vcpus = new_hosts[dst_node.uuid]['vcpus']
|
d_host_vcpus = new_hosts[dst_node.uuid]['vcpus']
|
||||||
for metric in self.metrics:
|
for metric in self.metrics:
|
||||||
if metric == 'cpu_util':
|
if metric is 'cpu_util':
|
||||||
new_hosts[src_node.uuid][metric] -= (
|
new_hosts[src_node.uuid][metric] -= (
|
||||||
self.transform_instance_cpu(instance_load, s_host_vcpus))
|
self.transform_instance_cpu(instance_load, s_host_vcpus))
|
||||||
new_hosts[dst_node.uuid][metric] += (
|
new_hosts[dst_node.uuid][metric] += (
|
||||||
@@ -409,15 +347,10 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
c_nodes.remove(src_host)
|
c_nodes.remove(src_host)
|
||||||
node_list = yield_nodes(c_nodes)
|
node_list = yield_nodes(c_nodes)
|
||||||
for instance in self.compute_model.get_node_instances(src_node):
|
for instance in self.compute_model.get_node_instances(src_node):
|
||||||
# NOTE: skip exclude instance when migrating
|
min_sd_case = {'value': current_weighted_sd}
|
||||||
if instance.watcher_exclude:
|
|
||||||
LOG.debug("Instance is excluded by scope, "
|
|
||||||
"skipped: %s", instance.uuid)
|
|
||||||
continue
|
|
||||||
if instance.state not in [element.InstanceState.ACTIVE.value,
|
if instance.state not in [element.InstanceState.ACTIVE.value,
|
||||||
element.InstanceState.PAUSED.value]:
|
element.InstanceState.PAUSED.value]:
|
||||||
continue
|
continue
|
||||||
min_sd_case = {'value': current_weighted_sd}
|
|
||||||
for dst_host in next(node_list):
|
for dst_host in next(node_list):
|
||||||
dst_node = self.compute_model.get_node_by_uuid(dst_host)
|
dst_node = self.compute_model.get_node_by_uuid(dst_host)
|
||||||
sd_case = self.calculate_migration_case(
|
sd_case = self.calculate_migration_case(
|
||||||
@@ -500,7 +433,6 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
|||||||
self.instance_metrics = self.input_parameters.instance_metrics
|
self.instance_metrics = self.input_parameters.instance_metrics
|
||||||
self.retry_count = self.input_parameters.retry_count
|
self.retry_count = self.input_parameters.retry_count
|
||||||
self.periods = self.input_parameters.periods
|
self.periods = self.input_parameters.periods
|
||||||
self.aggregation_method = self.input_parameters.aggregation_method
|
|
||||||
|
|
||||||
def do_execute(self):
|
def do_execute(self):
|
||||||
migration = self.check_threshold()
|
migration = self.check_threshold()
|
||||||
|
|||||||
@@ -11,6 +11,13 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
*Zone migration using instance and volume migration*
|
||||||
|
|
||||||
|
This is zone migration strategy to migrate many instances and volumes
|
||||||
|
efficiently with minimum downtime for hardware maintenance.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from dateutil.parser import parse
|
from dateutil.parser import parse
|
||||||
import six
|
import six
|
||||||
@@ -41,11 +48,7 @@ IN_USE = "in-use"
|
|||||||
|
|
||||||
|
|
||||||
class ZoneMigration(base.ZoneMigrationBaseStrategy):
|
class ZoneMigration(base.ZoneMigrationBaseStrategy):
|
||||||
"""Zone migration using instance and volume migration
|
"""Zone migration using instance and volume migration"""
|
||||||
|
|
||||||
This is zone migration strategy to migrate many instances and volumes
|
|
||||||
efficiently with minimum downtime for hardware maintenance.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config, osc=None):
|
def __init__(self, config, osc=None):
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
|
# 265
|
||||||
|
# Andreas Jaeger <jaegerandi@gmail.com>, 2018. #zanata
|
||||||
# Frank Kloeker <eumel@arcor.de>, 2018. #zanata
|
# Frank Kloeker <eumel@arcor.de>, 2018. #zanata
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: watcher VERSION\n"
|
"Project-Id-Version: watcher VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||||
"POT-Creation-Date: 2018-03-07 13:07+0000\n"
|
"POT-Creation-Date: 2018-02-12 10:48+0000\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"PO-Revision-Date: 2018-03-06 07:56+0000\n"
|
"PO-Revision-Date: 2018-03-02 09:00+0000\n"
|
||||||
"Last-Translator: Frank Kloeker <eumel@arcor.de>\n"
|
"Last-Translator: Andreas Jaeger <jaegerandi@gmail.com>\n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"X-Generator: Zanata 4.3.3\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||||
|
"X-Generator: Zanata 4.3.3\n"
|
||||||
|
|
||||||
msgid " (may include orphans)"
|
msgid " (may include orphans)"
|
||||||
msgstr "(kann Waisen einschließen)"
|
msgstr "(kann Waisen einschließen)"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: watcher VERSION\n"
|
"Project-Id-Version: watcher VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||||
"POT-Creation-Date: 2018-02-28 12:27+0000\n"
|
"POT-Creation-Date: 2018-02-12 10:48+0000\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import itertools
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@@ -268,67 +267,6 @@ class TestListAction(api_base.FunctionalTest):
|
|||||||
response = self.get_json(url, expect_errors=True)
|
response = self.get_json(url, expect_errors=True)
|
||||||
self.assertEqual(400, response.status_int)
|
self.assertEqual(400, response.status_int)
|
||||||
|
|
||||||
def test_many_with_sort_key_uuid(self):
|
|
||||||
action_plan = obj_utils.create_test_action_plan(
|
|
||||||
self.context,
|
|
||||||
uuid=utils.generate_uuid(),
|
|
||||||
audit_id=self.audit.id)
|
|
||||||
|
|
||||||
actions_list = []
|
|
||||||
for id_ in range(1, 3):
|
|
||||||
action = obj_utils.create_test_action(
|
|
||||||
self.context, id=id_,
|
|
||||||
action_plan_id=action_plan.id,
|
|
||||||
uuid=utils.generate_uuid())
|
|
||||||
actions_list.append(action)
|
|
||||||
|
|
||||||
response = self.get_json('/actions?sort_key=%s' % 'uuid')
|
|
||||||
names = [s['uuid'] for s in response['actions']]
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
sorted([a.uuid for a in actions_list]),
|
|
||||||
names)
|
|
||||||
|
|
||||||
def test_many_with_sort_key_action_plan_uuid(self):
|
|
||||||
action_plan_1 = obj_utils.create_test_action_plan(
|
|
||||||
self.context,
|
|
||||||
uuid=utils.generate_uuid(),
|
|
||||||
audit_id=self.audit.id)
|
|
||||||
|
|
||||||
action_plan_2 = obj_utils.create_test_action_plan(
|
|
||||||
self.context,
|
|
||||||
uuid=utils.generate_uuid(),
|
|
||||||
audit_id=self.audit.id)
|
|
||||||
|
|
||||||
action_plans_uuid_list = []
|
|
||||||
for id_, action_plan_id in enumerate(itertools.chain.from_iterable([
|
|
||||||
itertools.repeat(action_plan_1.id, 3),
|
|
||||||
itertools.repeat(action_plan_2.id, 2)]), 1):
|
|
||||||
action = obj_utils.create_test_action(
|
|
||||||
self.context, id=id_,
|
|
||||||
action_plan_id=action_plan_id,
|
|
||||||
uuid=utils.generate_uuid())
|
|
||||||
action_plans_uuid_list.append(action.action_plan.uuid)
|
|
||||||
|
|
||||||
for direction in ['asc', 'desc']:
|
|
||||||
response = self.get_json(
|
|
||||||
'/actions?sort_key={0}&sort_dir={1}'
|
|
||||||
.format('action_plan_uuid', direction))
|
|
||||||
|
|
||||||
action_plan_uuids = \
|
|
||||||
[s['action_plan_uuid'] for s in response['actions']]
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
sorted(action_plans_uuid_list, reverse=(direction == 'desc')),
|
|
||||||
action_plan_uuids,
|
|
||||||
message='Failed on %s direction' % direction)
|
|
||||||
|
|
||||||
def test_sort_key_validation(self):
|
|
||||||
response = self.get_json(
|
|
||||||
'/actions?sort_key=%s' % 'bad_name',
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
|
|
||||||
def test_many_with_soft_deleted_action_plan_uuid(self):
|
def test_many_with_soft_deleted_action_plan_uuid(self):
|
||||||
action_plan1 = obj_utils.create_test_action_plan(
|
action_plan1 = obj_utils.create_test_action_plan(
|
||||||
self.context,
|
self.context,
|
||||||
|
|||||||
@@ -256,12 +256,6 @@ class TestListActionPlan(api_base.FunctionalTest):
|
|||||||
uuids = [s['audit_uuid'] for s in response['action_plans']]
|
uuids = [s['audit_uuid'] for s in response['action_plans']]
|
||||||
self.assertEqual(sorted(audit_list), uuids)
|
self.assertEqual(sorted(audit_list), uuids)
|
||||||
|
|
||||||
def test_sort_key_validation(self):
|
|
||||||
response = self.get_json(
|
|
||||||
'/action_plans?sort_key=%s' % 'bad_name',
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
|
|
||||||
def test_links(self):
|
def test_links(self):
|
||||||
uuid = utils.generate_uuid()
|
uuid = utils.generate_uuid()
|
||||||
obj_utils.create_test_action_plan(self.context, id=1, uuid=uuid)
|
obj_utils.create_test_action_plan(self.context, id=1, uuid=uuid)
|
||||||
|
|||||||
@@ -294,50 +294,6 @@ class TestListAuditTemplate(FunctionalTestWithSetup):
|
|||||||
'/audit_templates?strategy=%s' % self.fake_strategy2.name)
|
'/audit_templates?strategy=%s' % self.fake_strategy2.name)
|
||||||
self.assertEqual(2, len(response['audit_templates']))
|
self.assertEqual(2, len(response['audit_templates']))
|
||||||
|
|
||||||
def test_many_with_sort_key_name(self):
|
|
||||||
audit_template_list = []
|
|
||||||
for id_ in range(1, 6):
|
|
||||||
audit_template = obj_utils.create_test_audit_template(
|
|
||||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
|
||||||
name='My Audit Template {0}'.format(id_))
|
|
||||||
audit_template_list.append(audit_template)
|
|
||||||
|
|
||||||
response = self.get_json('/audit_templates?sort_key=%s' % 'name')
|
|
||||||
|
|
||||||
names = [s['name'] for s in response['audit_templates']]
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
sorted([at.name for at in audit_template_list]),
|
|
||||||
names)
|
|
||||||
|
|
||||||
def test_many_with_sort_key_goal_name(self):
|
|
||||||
goal_names_list = []
|
|
||||||
for id_, goal_id in enumerate(itertools.chain.from_iterable([
|
|
||||||
itertools.repeat(self.fake_goal1.id, 3),
|
|
||||||
itertools.repeat(self.fake_goal2.id, 2)]), 1):
|
|
||||||
audit_template = obj_utils.create_test_audit_template(
|
|
||||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
|
||||||
name='My Audit Template {0}'.format(id_),
|
|
||||||
goal_id=goal_id)
|
|
||||||
goal_names_list.append(audit_template.goal.name)
|
|
||||||
|
|
||||||
for direction in ['asc', 'desc']:
|
|
||||||
response = self.get_json(
|
|
||||||
'/audit_templates?sort_key={0}&sort_dir={1}'
|
|
||||||
.format('goal_name', direction))
|
|
||||||
|
|
||||||
goal_names = [s['goal_name'] for s in response['audit_templates']]
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
sorted(goal_names_list, reverse=(direction == 'desc')),
|
|
||||||
goal_names)
|
|
||||||
|
|
||||||
def test_sort_key_validation(self):
|
|
||||||
response = self.get_json(
|
|
||||||
'/audit_templates?sort_key=%s' % 'goal_bad_name',
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
|
|
||||||
|
|
||||||
class TestPatch(FunctionalTestWithSetup):
|
class TestPatch(FunctionalTestWithSetup):
|
||||||
|
|
||||||
|
|||||||
@@ -217,12 +217,6 @@ class TestListAudit(api_base.FunctionalTest):
|
|||||||
uuids = [s['goal_uuid'] for s in response['audits']]
|
uuids = [s['goal_uuid'] for s in response['audits']]
|
||||||
self.assertEqual(sorted(goal_list), uuids)
|
self.assertEqual(sorted(goal_list), uuids)
|
||||||
|
|
||||||
def test_sort_key_validation(self):
|
|
||||||
response = self.get_json(
|
|
||||||
'/audits?sort_key=%s' % 'bad_name',
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
|
|
||||||
def test_links(self):
|
def test_links(self):
|
||||||
uuid = utils.generate_uuid()
|
uuid = utils.generate_uuid()
|
||||||
obj_utils.create_test_audit(
|
obj_utils.create_test_audit(
|
||||||
|
|||||||
@@ -120,27 +120,6 @@ class TestListGoal(api_base.FunctionalTest):
|
|||||||
response = self.get_json('/goals')
|
response = self.get_json('/goals')
|
||||||
self.assertEqual(3, len(response['goals']))
|
self.assertEqual(3, len(response['goals']))
|
||||||
|
|
||||||
def test_many_with_sort_key_uuid(self):
|
|
||||||
goal_list = []
|
|
||||||
for idx in range(1, 6):
|
|
||||||
goal = obj_utils.create_test_goal(
|
|
||||||
self.context, id=idx,
|
|
||||||
uuid=utils.generate_uuid(),
|
|
||||||
name='GOAL_{0}'.format(idx))
|
|
||||||
goal_list.append(goal.uuid)
|
|
||||||
|
|
||||||
response = self.get_json('/goals/?sort_key=uuid')
|
|
||||||
|
|
||||||
self.assertEqual(5, len(response['goals']))
|
|
||||||
uuids = [s['uuid'] for s in response['goals']]
|
|
||||||
self.assertEqual(sorted(goal_list), uuids)
|
|
||||||
|
|
||||||
def test_sort_key_validation(self):
|
|
||||||
response = self.get_json(
|
|
||||||
'/goals?sort_key=%s' % 'bad_name',
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
|
|
||||||
|
|
||||||
class TestGoalPolicyEnforcement(api_base.FunctionalTest):
|
class TestGoalPolicyEnforcement(api_base.FunctionalTest):
|
||||||
|
|
||||||
|
|||||||
@@ -113,26 +113,6 @@ class TestListScoringEngine(api_base.FunctionalTest):
|
|||||||
response = self.get_json('/scoring_engines')
|
response = self.get_json('/scoring_engines')
|
||||||
self.assertEqual(3, len(response['scoring_engines']))
|
self.assertEqual(3, len(response['scoring_engines']))
|
||||||
|
|
||||||
def test_many_with_sort_key_uuid(self):
|
|
||||||
scoring_engine_list = []
|
|
||||||
for idx in range(1, 6):
|
|
||||||
scoring_engine = obj_utils.create_test_scoring_engine(
|
|
||||||
self.context, id=idx, uuid=utils.generate_uuid(),
|
|
||||||
name=str(idx), description='SE_{0}'.format(idx))
|
|
||||||
scoring_engine_list.append(scoring_engine.uuid)
|
|
||||||
|
|
||||||
response = self.get_json('/scoring_engines/?sort_key=uuid')
|
|
||||||
|
|
||||||
self.assertEqual(5, len(response['scoring_engines']))
|
|
||||||
uuids = [s['uuid'] for s in response['scoring_engines']]
|
|
||||||
self.assertEqual(sorted(scoring_engine_list), uuids)
|
|
||||||
|
|
||||||
def test_sort_key_validation(self):
|
|
||||||
response = self.get_json(
|
|
||||||
'/goals?sort_key=%s' % 'bad_name',
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
|
|
||||||
|
|
||||||
class TestScoringEnginePolicyEnforcement(api_base.FunctionalTest):
|
class TestScoringEnginePolicyEnforcement(api_base.FunctionalTest):
|
||||||
|
|
||||||
|
|||||||
@@ -131,26 +131,6 @@ class TestListService(api_base.FunctionalTest):
|
|||||||
response = self.get_json('/services')
|
response = self.get_json('/services')
|
||||||
self.assertEqual(3, len(response['services']))
|
self.assertEqual(3, len(response['services']))
|
||||||
|
|
||||||
def test_many_with_sort_key_name(self):
|
|
||||||
service_list = []
|
|
||||||
for id_ in range(1, 4):
|
|
||||||
service = obj_utils.create_test_service(
|
|
||||||
self.context, id=id_, host='CONTROLLER',
|
|
||||||
name='SERVICE_{0}'.format(id_))
|
|
||||||
service_list.append(service.name)
|
|
||||||
|
|
||||||
response = self.get_json('/services/?sort_key=name')
|
|
||||||
|
|
||||||
self.assertEqual(3, len(response['services']))
|
|
||||||
names = [s['name'] for s in response['services']]
|
|
||||||
self.assertEqual(sorted(service_list), names)
|
|
||||||
|
|
||||||
def test_sort_key_validation(self):
|
|
||||||
response = self.get_json(
|
|
||||||
'/services?sort_key=%s' % 'bad_name',
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
|
|
||||||
|
|
||||||
class TestServicePolicyEnforcement(api_base.FunctionalTest):
|
class TestServicePolicyEnforcement(api_base.FunctionalTest):
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user