Compare commits

..

41 Commits
1.5.0 ... 1.7.0

Author SHA1 Message Date
Zuul
673905fead Merge "Update links in README" 2018-04-02 08:09:51 +00:00
Zuul
d886672fb5 Merge "add lower-constraints job" 2018-03-29 10:01:43 +00:00
Alexander Chadin
83ccfbb177 [WiP] functional jobs fix
Change-Id: If92660ad48b19fce47dd7a88ed33796427f70a80
2018-03-28 09:11:18 +00:00
Zuul
4c47c7364c Merge "ZuulV3 support for watcherclient" 2018-03-27 16:48:01 +00:00
Alexander Chadin
8acf01859c ZuulV3 support for watcherclient
Change-Id: I0efc302dae72d18f86dbdae1d22a6ea798cfb8f5
2018-03-27 18:38:55 +03:00
Zuul
d4af8d8834 Merge "Delete the unnecessary '-'" 2018-03-26 09:30:30 +00:00
Doug Hellmann
40d8df21c8 add lower-constraints job
Create a tox environment for running the unit tests against the lower
bounds of the dependencies.

Create a lower-constraints.txt to be used to enforce the lower bounds
in those tests.

Add openstack-tox-lower-constraints job to the zuul configuration.

See http://lists.openstack.org/pipermail/openstack-dev/2018-March/128352.html
for more details.

Change-Id: I11c8526b413e6dfc6e2457026ccc2f35e38d1699
Depends-On: https://review.openstack.org/555034
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
2018-03-25 11:32:15 -04:00
Zuul
b87399cb2c Merge "Add tempest plugin" 2018-03-23 12:56:05 +00:00
Alexander Chadin
9bfefbf9ab Add tempest plugin
This patch set adds tempest plugin to register existing tempest
tests of watcherclient. It is also required to let devstack-tempest
job to launch watcherclient functional tests.

Change-Id: I4d906fd597048bb37803a2bb42f8357502cf1b76
2018-03-23 14:36:38 +03:00
huang.zhiping
b84039d7d4 Delete the unnecessary '-'
Change-Id: I6f36a4114026368d854fac37dcd003d3f4236455
2018-03-23 11:29:03 +08:00
OpenStack Proposal Bot
f8ead12481 Updated from global requirements
Change-Id: I35f49cea5915272e21ac1351dfb190c2b55be335
2018-03-15 08:06:52 +00:00
OpenStack Proposal Bot
85e4e364b7 Updated from global requirements
Change-Id: I46b763854d736e74523106240e1faf06150a3139
2018-03-13 07:30:46 +00:00
melissaml
ed9ce5b952 Update links in README
Change the outdated links to the latest links in README

Change-Id: I0a0781d4a86bb860bb8c85923e7be8b803f91ae2
2018-03-11 03:01:04 +08:00
Zuul
731e019bc1 Merge "Fix global efficacy format" 2018-02-23 09:09:47 +00:00
OpenStack Proposal Bot
a22dab5f88 Updated from global requirements
Change-Id: Ib45b0cc6fefd94b3c3a8181bd10b0d85f8044800
2018-02-17 10:20:48 +00:00
Alexander Chadin
0d7233055d Fix global efficacy format
Closes-Bug: #1749463
Change-Id: I0496f0d82bbf72dbbc8b808feb3a529ad6d49402
2018-02-14 17:06:15 +03:00
Zuul
2777a9bba8 Merge "Zuul: Remove project name" 2018-02-07 08:23:00 +00:00
James E. Blair
e359488cde Zuul: Remove project name
Zuul no longer requires the project-name for in-repo configuration.
Omitting it makes forking or renaming projects easier.

Change-Id: I884a00879ac30b504833951303578de42174ba09
2018-02-02 10:11:34 -08:00
Zuul
a1b6eed54c Merge "Add strategy state command" 2018-01-25 20:34:01 +00:00
Alexander Chadin
a907c2d0cd Add strategy state command
This patch set adds command "strategy state"
that allows to request strategy requirements to run.

Partially-Implements: blueprint check-strategy-requirements
Change-Id: I327cc4f5a887f62af700bd646576caa036faaf75
2018-01-24 14:55:01 +00:00
Zuul
d38bb255ed Merge "Updated from global requirements" 2018-01-24 14:03:31 +00:00
Zuul
30e2aad126 Merge "Audit Template Help Message" 2018-01-24 12:37:57 +00:00
OpenStack Proposal Bot
bc0fb4f051 Updated from global requirements
Change-Id: I420d547482d84c9cf8e39e2e2069c46f6d86e993
2018-01-24 01:34:14 +00:00
Zuul
ef533eb151 Merge "Updated from global requirements" 2018-01-22 11:24:34 +00:00
OpenStack Proposal Bot
3c160af376 Updated from global requirements
Change-Id: I65f25febf490e24d2bea4182890400191ce3a16a
I08b68c15c3906885ce3ca6fb410d227c3e585978
2018-01-19 13:50:22 +00:00
OpenStack Proposal Bot
186029ac87 Updated from global requirements
Change-Id: I08b68c15c3906885ce3ca6fb410d227c3e585978
I65f25febf490e24d2bea4182890400191ce3a16a
2018-01-19 12:01:23 +00:00
aditi
b9112a1199 Audit Template Help Message
This patch updates help message of "watcher audittemplate create"
It updates help about storage scoper.

Change-Id: I46be98c2819c2dfb2a7ef3fe1aa98caa90ececfd
Implements: blueprint audit-scoper-for-storage-data-model
2018-01-19 11:16:44 +00:00
Alexander Chadin
9d301fa14a Fix test_action_plan functional tests
Change-Id: Ie5b5bd330c68b696dce48e2475baf77a81f2e8c4
Closes-Bug: #1744262
2018-01-19 13:36:19 +03:00
Zuul
15d7d78b8b Merge "Fix watcher actionplan list command" 2018-01-05 09:25:24 +00:00
OpenStack Proposal Bot
81b84bcff5 Updated from global requirements
Change-Id: I8dcbe4616e7618c783097baecd62f67ef6501e30
2017-12-21 00:47:51 +00:00
Alexander Chadin
79880c2dff Fix watcher actionplan list command
This patch set replaces itervalues() method with values()
to get py3.* compatibility.

Closes-Bug: #1738772
Change-Id: Ie13d15d6f407b08ba087168ac647669c0df2c30a
2017-12-18 15:45:33 +03:00
Zuul
32259fe287 Merge "Fix unnecessary retries during conflict" 2017-12-13 14:24:13 +00:00
Zuul
d36ba79e77 Merge "marker when retrive audit" 2017-12-13 11:43:17 +00:00
Zuul
4f5487b5e1 Merge "Update audit_template create help message" 2017-12-13 10:06:17 +00:00
Zuul
7bf192a981 Merge "marker when retrive action" 2017-12-13 08:36:22 +00:00
Zuul
f8b341c0f3 Merge "marker when retrive audit template" 2017-12-13 05:39:09 +00:00
Yumeng Bao
1a6703266f Update audit_template create help message
'id' should be an integer, not string.
Closes-Bug: #1734056
Change-Id: I4275127e7f76ba6ad05f478278204c3de535cf5c
2017-11-28 10:04:16 +08:00
suzhengwei
fada471714 marker when retrive audit
Change-Id: I72078850152e0ccf2abc0d35b06a5142df35c312
2017-11-27 06:56:01 +00:00
suzhengwei
1cd8c38d95 marker when retrive action
Change-Id: Ic733c3e79a31c7cd99226e6e49dfbbc8c3b58902
2017-11-27 06:54:42 +00:00
suzhengwei
0257aa9116 marker when retrive audit template
Change-Id: Ia2cd0598770359c2727ec569dd7f2531f7b5f40a
2017-11-22 13:34:18 +00:00
aditi
5378102cd6 Fix unnecessary retries during conflict
This patch removes retries from python-watcherclient in case
watcher-api responded with conflict case.

Change-Id: I830abe0a3cd67484e165a0a42d5a57b75ed4dac6
Closes-Bug: #1725088
2017-10-20 02:27:13 +00:00
36 changed files with 619 additions and 138 deletions

3
.stestr.conf Normal file
View File

@@ -0,0 +1,3 @@
[DEFAULT]
test_path=${OS_TEST_PATH:-./watcherclient/tests/functional}
top_dir=./

View File

@@ -1,8 +1,9 @@
- project:
name: openstack/python-watcherclient
check:
jobs:
- watcherclient-tempest-functional
- openstack-tox-lower-constraints
gate:
jobs:
- watcherclient-tempest-functional
- openstack-tox-lower-constraints

View File

@@ -2,8 +2,8 @@
Team and repository tags
========================
.. image:: https://governance.openstack.org/badges/python-watcherclient.svg
:target: https://governance.openstack.org/reference/tags/index.html
.. image:: https://governance.openstack.org/tc/badges/python-watcherclient.svg
:target: https://governance.openstack.org/tc/reference/tags/index.html
.. Change things from this point on
@@ -20,7 +20,7 @@ metrics receiver, complex event processor and profiler, optimization processor
and an action plan applier. This provides a robust framework to realize a wide
range of cloud optimization goals, including the reduction of data center
operating costs, increased system performance via intelligent virtual machine
migration, increased energy efficiency-and more!
migration, increased energy efficiency and more!
* Free software: Apache license
* Wiki: https://wiki.openstack.org/wiki/Watcher

92
lower-constraints.txt Normal file
View File

@@ -0,0 +1,92 @@
alabaster==0.7.10
appdirs==1.4.3
asn1crypto==0.23.0
Babel==2.5.3
certifi==2018.1.18
cffi==1.7.0
chardet==3.0.4
cliff==2.11.0
cmd2==0.8.2
coverage==4.0
cryptography==2.1
debtcollector==1.19.0
decorator==4.2.1
deprecation==2.0
docutils==0.11
dogpile.cache==0.6.5
dulwich==0.15.0
extras==1.0.0
fasteners==0.7.0
fixtures==3.0.0
flake8==2.5.5
future==0.16.0
hacking==0.12.0
idna==2.6
imagesize==0.7.1
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
linecache2==1.0.0
MarkupSafe==1.0
mccabe==0.2.1
mock==2.0.0
monotonic==1.4
mox3==0.20.0
msgpack-python==0.4.0
munch==2.2.0
netaddr==0.7.19
netifaces==0.10.6
openstackdocstheme==1.18.1
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.concurrency==3.25.0
oslo.config==5.2.0
oslo.context==2.19.2
oslo.i18n==3.20.0
oslo.log==3.36.0
oslo.serialization==2.18.0
oslo.utils==3.36.0
oslotest==3.2.0
packaging==17.1
paramiko==2.0.0
pbr==3.1.1
pep8==1.5.7
prettytable==0.7.2
pyasn1==0.1.8
pycparser==2.18
pyflakes==0.8.1
Pygments==2.2.0
pyinotify==0.9.6
pyparsing==2.2.0
pyperclip==1.6.0
python-dateutil==2.5.3
python-mimeparse==1.6.0
python-subunit==1.0.0
-e git://git.openstack.org/openstack/python-watcherclient@104894958882a4877dad6f469361d2adb41d0b59#egg=python_watcherclient
pytz==2018.3
PyYAML==3.12
requests==2.18.4
requestsexceptions==1.4.0
rfc3986==0.3.1
simplejson==3.13.2
six==1.11.0
snowballstemmer==1.2.1
Sphinx==1.6.5
sphinxcontrib-websupport==1.0.1
stestr==1.0.0
stevedore==1.28.0
tempest==17.1.0
testrepository==0.0.18
testscenarios==0.4
testtools==2.2.0
traceback2==1.4.0
unittest2==1.1.0
urllib3==1.22
wrapt==1.10.11

View File

@@ -4,11 +4,11 @@
Babel!=2.4.0,>=2.3.4 # BSD
cliff!=2.9.0,>=2.8.0 # Apache-2.0
osc-lib>=1.7.0 # Apache-2.0
osc-lib>=1.8.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
oslo.utils>=3.31.0 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0
pbr!=2.1.0,>=2.0.0 # Apache-2.0
PrettyTable<0.8,>=0.7.1 # BSD
keystoneauth1>=3.3.0 # Apache-2.0
keystoneauth1>=3.4.0 # Apache-2.0
six>=1.10.0 # MIT
PyYAML>=3.10 # MIT
PyYAML>=3.12 # MIT

View File

@@ -25,6 +25,8 @@ packages =
[entry_points]
console_scripts =
watcher = watcherclient.shell:main
tempest.test_plugins =
watcherclient_tests = watcherclient.plugin:WatcherClientTempestPlugin
openstack.cli.extension =
infra_optim = watcherclient.osc.plugin
@@ -36,6 +38,7 @@ openstack.infra_optim.v1 =
optimize_strategy_show = watcherclient.v1.strategy_shell:ShowStrategy
optimize_strategy_list = watcherclient.v1.strategy_shell:ListStrategy
optimize_strategy_state = watcherclient.v1.strategy_shell:StateStrategy
optimize_audittemplate_show = watcherclient.v1.audit_template_shell:ShowAuditTemplate
optimize_audittemplate_list = watcherclient.v1.audit_template_shell:ListAuditTemplate
@@ -72,6 +75,7 @@ watcherclient.v1 =
strategy_show = watcherclient.v1.strategy_shell:ShowStrategy
strategy_list = watcherclient.v1.strategy_shell:ListStrategy
strategy_state = watcherclient.v1.strategy_shell:StateStrategy
audittemplate_show = watcherclient.v1.audit_template_shell:ShowAuditTemplate
audittemplate_list = watcherclient.v1.audit_template_shell:ListAuditTemplate

View File

@@ -6,10 +6,10 @@ coverage!=4.4,>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
mock>=2.0.0 # BSD
openstackdocstheme>=1.17.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
openstackdocstheme>=1.18.1 # Apache-2.0
oslotest>=3.2.0 # Apache-2.0
python-subunit>=1.0.0 # Apache-2.0/BSD
sphinx>=1.6.2 # BSD
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=2.2.0 # MIT

View File

@@ -60,3 +60,11 @@ commands = python setup.py bdist_wheel
[hacking]
import_exceptions = watcherclient._i18n
[testenv:lower-constraints]
basepython = python3
install_command = pip install -U {opts} {packages}
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt

View File

@@ -165,8 +165,7 @@ class VersionNegotiationMixin(object):
raise NotImplementedError()
_RETRY_EXCEPTIONS = (exceptions.Conflict,
exceptions.ServiceUnavailable,
_RETRY_EXCEPTIONS = (exceptions.ServiceUnavailable,
exceptions.ConnectionRefused,
kexceptions.RetriableConnectionFailure)

23
watcherclient/config.py Normal file
View File

@@ -0,0 +1,23 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2018 Servionica
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from oslo_config import cfg
service_option = cfg.BoolOpt("watcher_client",
default=True,
help="Whether or not watcher_client is expected"
"to be available")

34
watcherclient/plugin.py Normal file
View File

@@ -0,0 +1,34 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from tempest.test_discover import plugins
from watcherclient import config as watcher_config
class WatcherClientTempestPlugin(plugins.TempestPlugin):
def load_tests(self):
base_path = os.path.split(os.path.dirname(
os.path.abspath(__file__)))[0]
test_dir = "watcherclient/tests/functional/v1"
full_test_dir = os.path.join(base_path, test_dir)
return full_test_dir, base_path
def register_opts(self, conf):
conf.register_opt(watcher_config.service_option,
group='service_available')
def get_opt_lists(self):
return [('service_available', [watcher_config.service_option])]

View File

@@ -1,53 +0,0 @@
#!/bin/bash -xe
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# This script is executed inside post_test_hook function in devstack gate.
# Default gate uses /opt/stack/new... but some of us may install differently
STACK_DIR=$BASE/new/devstack
function generate_testr_results {
if [ -f .testrepository/0 ]; then
sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit
sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit
sudo /usr/os-testr-env/bin/subunit2html $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html
sudo gzip -9 $BASE/logs/testrepository.subunit
sudo gzip -9 $BASE/logs/testr_results.html
sudo chown $USER:$USER $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
fi
}
export WATCHERCLIENT_DIR="$BASE/new/python-watcherclient"
sudo chown -R $USER:stack $WATCHERCLIENT_DIR
# Get admin credentials
cd $STACK_DIR
source openrc admin admin
# Go to the watcherclient dir
cd $WATCHERCLIENT_DIR
# Run tests
echo "Running watcherclient functional test suite"
set +e
# Preserve env for OS_ credentials
sudo -E -H -u $USER tox -efunctional
EXIT_CODE=$?
set -e
# Collect and parse result
generate_testr_results
exit $EXIT_CODE

View File

@@ -18,22 +18,31 @@ import subprocess
import testtools
import six
from tempest import clients
from tempest.common import credentials_factory as creds_factory
from tempest.lib.cli import output_parser
from tempest.lib import exceptions
def credentials():
creds = {
'--os-username': os.environ.get('OS_USERNAME', 'admin'),
'--os-password': os.environ.get('OS_PASSWORD', 'secretadmin'),
'--os-project-name': os.environ.get('OS_PROJECT_NAME', 'admin'),
# You can get credentials either from tempest.conf file or
# from OS environment.
tempest_creds = clients.get_auth_provider(
creds_factory.get_configured_admin_credentials())
creds = tempest_creds.credentials
creds_dict = {
'--os-username': os.environ.get('OS_USERNAME', creds.username),
'--os-password': os.environ.get('OS_PASSWORD', creds.password),
'--os-project-name': os.environ.get('OS_PROJECT_NAME',
creds.project_name),
'--os-auth-url': os.environ.get('OS_AUTH_URL',
'http://10.0.1.94/identity'),
'--os-project-domain-id': os.environ.get('OS_PROJECT_DOMAIN_ID',
'default'),
'--os-user-domain-id': os.environ.get('OS_USER_DOMAIN_ID', 'default'),
tempest_creds.auth_url),
'--os-project-domain-name': os.environ.get('OS_PROJECT_DOMAIN_ID',
creds.project_domain_name),
'--os-user-domain-name': os.environ.get('OS_USER_DOMAIN_ID',
creds.user_domain_name),
}
return [x for sub in creds.items() for x in sub]
return [x for sub in creds_dict.items() for x in sub]
def execute(cmd, fail_ok=False, merge_stderr=False):
@@ -123,3 +132,13 @@ class TestCase(testtools.TestCase):
def parse_listing(self, raw_output):
"""Return list of dicts with basic item parsed from cli output."""
return output_parser.listing(raw_output)
def has_actionplan_succeeded(self, ap_uuid):
return self.parse_show_as_object(
self.watcher('actionplan show %s' % ap_uuid)
)['State'] == 'SUCCEEDED'
@classmethod
def has_audit_created(cls, audit_uuid):
return cls.parse_show_as_object(
cls.watcher('audit show %s' % audit_uuid))['State'] == 'SUCCEEDED'

View File

@@ -15,6 +15,10 @@
from oslo_utils import uuidutils
import functools
from tempest.lib.common.utils import test_utils
from watcherclient.tests.functional.v1 import base
@@ -33,17 +37,22 @@ class ActionTests(base.TestCase):
template_raw_output = cls.watcher(
'audittemplate create %s dummy -s dummy' % cls.audit_template_name)
template_output = cls.parse_show_as_object(template_raw_output)
audit_raw_output = cls.watcher(
'audit create -a %s' % template_output['Name'])
audit_output = cls.parse_show_as_object(audit_raw_output)
audit_output = cls.parse_show_as_object(cls.watcher(
'audit create -a %s' % template_output['Name']))
cls.audit_uuid = audit_output['UUID']
audit_created = test_utils.call_until_true(
func=functools.partial(cls.has_audit_created, cls.audit_uuid),
duration=600,
sleep_for=2)
if not audit_created:
raise Exception('Audit has not been succeeded')
@classmethod
def tearDownClass(cls):
# Delete Action Plan and all related actions.
output = cls.parse_show(
cls.watcher('actionplan list --audit %s' % cls.audit_uuid))
action_plan_uuid = output[0].keys()[0]
action_plan_uuid = list(output[0])[0]
raw_output = cls.watcher('actionplan delete %s' % action_plan_uuid)
cls.assertOutput('', raw_output)
# Delete audit
@@ -63,9 +72,10 @@ class ActionTests(base.TestCase):
self.assert_table_structure([raw_output], self.detailed_list_fields)
def test_action_show(self):
action_list = self.parse_show(self.watcher('action list'))
action_uuid = action_list[0].keys()[0]
action = self.watcher('action show ' + action_uuid)
action_list = self.parse_show(self.watcher('action list --audit %s'
% self.audit_uuid))
action_uuid = list(action_list[0])[0]
action = self.watcher('action show %s' % action_uuid)
self.assertIn(action_uuid, action)
self.assert_table_structure([action],
self.detailed_list_fields)

View File

@@ -15,6 +15,10 @@
from oslo_utils import uuidutils
import functools
from tempest.lib.common.utils import test_utils
from watcherclient.tests.functional.v1 import base
@@ -37,13 +41,19 @@ class ActionPlanTests(base.TestCase):
% template_output['Name'])
audit_output = cls.parse_show_as_object(audit_raw_output)
cls.audit_uuid = audit_output['UUID']
audit_created = test_utils.call_until_true(
func=functools.partial(cls.has_audit_created, cls.audit_uuid),
duration=600,
sleep_for=2)
if not audit_created:
raise Exception('Audit has not been succeeded')
@classmethod
def tearDownClass(cls):
# Delete action plan
output = cls.parse_show(
cls.watcher('actionplan list --audit %s' % cls.audit_uuid))
action_plan_uuid = output[0].keys()[0]
action_plan_uuid = list(output[0])[0]
raw_output = cls.watcher('actionplan delete %s' % action_plan_uuid)
cls.assertOutput('', raw_output)
# Delete audit
@@ -64,7 +74,7 @@ class ActionPlanTests(base.TestCase):
def test_action_plan_show(self):
action_plan_list = self.parse_show(self.watcher('actionplan list'))
action_plan_uuid = action_plan_list[0].keys()[0]
action_plan_uuid = list(action_plan_list[0])[0]
actionplan = self.watcher('actionplan show %s' % action_plan_uuid)
self.assertIn(action_plan_uuid, actionplan)
self.assert_table_structure([actionplan],
@@ -73,11 +83,18 @@ class ActionPlanTests(base.TestCase):
def test_action_plan_start(self):
output = self.parse_show(self.watcher('actionplan list --audit %s'
% self.audit_uuid))
action_plan_uuid = output[0].keys()[0]
action_plan_uuid = list(output[0])[0]
self.watcher('actionplan start %s' % action_plan_uuid)
raw_output = self.watcher('actionplan show %s' % action_plan_uuid)
self.assert_table_structure([raw_output], self.detailed_list_fields)
self.assertTrue(test_utils.call_until_true(
func=functools.partial(
self.has_actionplan_succeeded, action_plan_uuid),
duration=600,
sleep_for=2
))
class ActionPlanActiveTests(base.TestCase):
@@ -89,9 +106,15 @@ class ActionPlanActiveTests(base.TestCase):
'Strategy', 'Efficacy indicators']
def _delete_action_plan(self):
self.assertTrue(test_utils.call_until_true(
func=functools.partial(
self.has_audit_created, self.audit_uuid),
duration=600,
sleep_for=2
))
output = self.parse_show(
self.watcher('actionplan list --audit %s' % self.audit_uuid))
action_plan_uuid = output[0].keys()[0]
action_plan_uuid = list(output[0])[0]
raw_output = self.watcher('actionplan delete %s' % action_plan_uuid)
self.assertOutput('', raw_output)

View File

@@ -15,6 +15,10 @@
from oslo_utils import uuidutils
import functools
from tempest.lib.common.utils import test_utils
from watcherclient.tests.functional.v1 import base
@@ -39,12 +43,18 @@ class AuditTests(base.TestCase):
'audit create -a %s' % template_output['Name'])
audit_output = cls.parse_show_as_object(audit_raw_output)
cls.audit_uuid = audit_output['UUID']
audit_created = test_utils.call_until_true(
func=functools.partial(cls.has_audit_created, cls.audit_uuid),
duration=600,
sleep_for=2)
if not audit_created:
raise Exception('Audit has not been succeeded')
@classmethod
def tearDownClass(cls):
output = cls.parse_show(
cls.watcher('actionplan list --audit %s' % cls.audit_uuid))
action_plan_uuid = output[0].keys()[0]
action_plan_uuid = list(output[0])[0]
cls.watcher('actionplan delete %s' % action_plan_uuid)
cls.watcher('audit delete %s' % cls.audit_uuid)
cls.watcher('audittemplate delete %s' % cls.audit_template_name)
@@ -76,39 +86,51 @@ class AuditActiveTests(base.TestCase):
'Deleted At', 'Parameters',
'Interval', 'Audit Scope']
audit_template_name = 'a' + uuidutils.generate_uuid()
audit_uuid = None
@classmethod
def setUpClass(cls):
cls.watcher('audittemplate create %s dummy -s dummy'
% cls.audit_template_name)
@classmethod
def tearDownClass(cls):
cls.watcher('audittemplate delete %s' % cls.audit_template_name)
def _create_audit(self):
raw_output = self.watcher('audittemplate create %s dummy -s dummy'
% self.audit_template_name)
template_output = self.parse_show_as_object(raw_output)
self.audit_uuid = self.parse_show_as_object(
return self.parse_show_as_object(
self.watcher('audit create -a %s'
% template_output['Name']))['UUID']
% self.audit_template_name))['UUID']
def _delete_audit(self):
def _delete_audit(self, audit_uuid):
self.assertTrue(test_utils.call_until_true(
func=functools.partial(
self.has_audit_created, audit_uuid),
duration=600,
sleep_for=2
))
output = self.parse_show(
self.watcher('actionplan list --audit %s' % self.audit_uuid))
action_plan_uuid = output[0].keys()[0]
self.watcher('actionplan list --audit %s' % audit_uuid))
action_plan_uuid = list(output[0])[0]
self.watcher('actionplan delete %s' % action_plan_uuid)
self.watcher('audit delete %s' % self.audit_uuid)
self.watcher('audittemplate delete %s' % self.audit_template_name)
self.watcher('audit delete %s' % audit_uuid)
def test_create_audit(self):
raw_output = self.watcher('audittemplate create %s dummy -s dummy'
% self.audit_template_name)
template_output = self.parse_show_as_object(raw_output)
audit = self.watcher('audit create -a %s' % template_output['Name'])
self.audit_uuid = self.parse_show_as_object(audit)['UUID']
audit = self.watcher('audit create -a %s' % self.audit_template_name)
audit_uuid = self.parse_show_as_object(audit)['UUID']
self.assert_table_structure([audit], self.detailed_list_fields)
self._delete_audit()
self._delete_audit(audit_uuid)
def test_delete_audit(self):
self._create_audit()
raw_output = self.watcher('audit delete %s' % self.audit_uuid)
audit_uuid = self._create_audit()
self.assertTrue(test_utils.call_until_true(
func=functools.partial(
self.has_audit_created, audit_uuid),
duration=600,
sleep_for=2
))
raw_output = self.watcher('audit delete %s' % audit_uuid)
self.assertOutput('', raw_output)
output = self.parse_show(
self.watcher('actionplan list --audit %s' % self.audit_uuid))
action_plan_uuid = output[0].keys()[0]
self.watcher('actionplan list --audit %s' % audit_uuid))
action_plan_uuid = list(output[0])[0]
self.watcher('actionplan delete %s' % action_plan_uuid)
self.watcher('audittemplate delete %s' % self.audit_template_name)

View File

@@ -37,6 +37,8 @@ class ServiceTests(base.TestCase):
self.list_fields + ['Last seen up'])
def test_service_show(self):
# TODO(alexchadin): this method should be refactored since Watcher will
# get HA support soon.
raw_output = self.watcher('service show %s'
% self.decision_engine_name)
self.assertIn(self.decision_engine_name, raw_output)

View File

@@ -20,7 +20,9 @@ class StrategyTests(base.TestCase):
"""Functional tests for strategy."""
dummy_name = 'dummy'
basic_strategy = 'basic'
list_fields = ['UUID', 'Name', 'Display name', 'Goal']
state_fields = ['Datasource', 'Metrics', 'CDM', 'Name']
def test_strategy_list(self):
raw_output = self.watcher('strategy list')
@@ -39,3 +41,8 @@ class StrategyTests(base.TestCase):
self.assert_table_structure([raw_output],
self.list_fields + ['Parameters spec'])
self.assertNotIn('basic', raw_output)
def test_strategy_state(self):
raw_output = self.watcher('strategy state %s' % self.basic_strategy)
self.assertIn(self.basic_strategy, raw_output)
self.assert_table_structure([raw_output], self.state_fields)

View File

@@ -144,6 +144,16 @@ fake_responses_sorting = {
},
}
fake_responses_marker = {
'/v1/actions/?marker=770ef053-ecb3-48b0-85b5-d55a2dbc6588':
{
'GET': (
{},
{"actions": [ACTION2, ACTION3]}
),
},
}
class ActionManagerTest(testtools.TestCase):
@@ -231,6 +241,20 @@ class ActionManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(3, len(actions))
def test_actions_list_marker(self):
self.api = utils.FakeAPI(fake_responses_marker)
self.mgr = watcherclient.v1.action.ActionManager(self.api)
actions = self.mgr.list(
marker='770ef053-ecb3-48b0-85b5-d55a2dbc6588')
expect = [
('GET',
'/v1/actions/?marker=770ef053-ecb3-48b0-85b5-d55a2dbc6588',
{},
None)
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(actions))
def test_actions_show(self):
action = self.mgr.get(ACTION1['uuid'])
expect = [

21
watcherclient/tests/unit/v1/test_action_shell.py Executable file → Normal file
View File

@@ -132,6 +132,27 @@ class ActionShellTest(base.CommandTestCase):
self.m_action_mgr.list.assert_called_once_with(detail=True)
def test_do_action_list_marker(self):
action2 = resource.Action(mock.Mock(), ACTION_2)
action3 = resource.Action(mock.Mock(), ACTION_3)
self.m_action_mgr.list.return_value = [
action2, action3]
exit_code, results = self.run_cmd(
'action list --marker 770ef053-ecb3-48b0-85b5-d55a2dbc6588')
self.assertEqual(0, exit_code)
self.assertEqual(
[self.resource_as_dict(action2, self.SHORT_LIST_FIELDS,
self.SHORT_LIST_FIELD_LABELS),
self.resource_as_dict(action3, self.SHORT_LIST_FIELDS,
self.SHORT_LIST_FIELD_LABELS)],
results)
self.m_action_mgr.list.assert_called_once_with(
detail=False,
marker='770ef053-ecb3-48b0-85b5-d55a2dbc6588')
def test_do_action_show_by_uuid(self):
action = resource.Action(mock.Mock(), ACTION_1)
self.m_action_mgr.get.return_value = action

View File

@@ -145,6 +145,16 @@ fake_responses_strategy = {
},
}
fake_responses_marker = {
'/v1/audits/?marker=5869da81-4876-4687-a1ed-12cd64cf53d9':
{
'GET': (
{},
{"audits": [AUDIT2]}
),
},
}
class AuditManagerTest(testtools.TestCase):
def setUp(self):
@@ -229,6 +239,17 @@ class AuditManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(audits))
def test_audits_list_marker(self):
self.api = utils.FakeAPI(fake_responses_marker)
self.mgr = watcherclient.v1.audit.AuditManager(self.api)
audits = self.mgr.list(marker=AUDIT1['uuid'])
expect = [
('GET', '/v1/audits/?marker=5869da81-4876-4687-a1ed-12cd64cf53d9',
{}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(audits))
def test_audits_show(self):
audit = self.mgr.get(AUDIT1['uuid'])
expect = [

View File

@@ -172,6 +172,23 @@ class AuditShellTest(base.CommandTestCase):
self.m_audit_mgr.list.assert_called_once_with(detail=False)
def test_do_audit_list_marker(self):
audit2 = resource.Audit(mock.Mock(), AUDIT_2)
self.m_audit_mgr.list.return_value = [audit2]
exit_code, results = self.run_cmd(
'audit list --marker 5869da81-4876-4687-a1ed-12cd64cf53d9')
self.assertEqual(0, exit_code)
self.assertEqual(
[self.resource_as_dict(audit2, self.SHORT_LIST_FIELDS,
self.SHORT_LIST_FIELD_LABELS)],
results)
self.m_audit_mgr.list.assert_called_once_with(
detail=False,
marker='5869da81-4876-4687-a1ed-12cd64cf53d9')
def test_do_audit_list_detail(self):
audit1 = resource.Audit(mock.Mock(), AUDIT_1)
audit2 = resource.Audit(mock.Mock(), AUDIT_2)

View File

@@ -178,6 +178,16 @@ fake_responses_sorting = {
},
}
fake_responses_marker = {
'/v1/audit_templates/?marker=f8e47706-efcf-49a4-a5c4-af604eb492f2':
{
'GET': (
{},
{"audit_templates": [AUDIT_TMPL2, AUDIT_TMPL3]}
),
},
}
fake_responses_filter_by_goal_uuid = {
'/v1/audit_templates/?goal=e75ee410-b32b-465f-88b5-4397705f9473':
{
@@ -389,6 +399,18 @@ class AuditTemplateManagerTest(utils.BaseTestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(3, len(audit_templates))
def test_audit_templates_list_marker(self):
self.api = utils.FakeAPI(fake_responses_marker)
self.mgr = watcherclient.v1.audit_template.AuditTemplateManager(
self.api)
audit_templates = self.mgr.list(marker=AUDIT_TMPL1['uuid'])
expect_url = '/v1/audit_templates/?marker=%s' % AUDIT_TMPL1['uuid']
expect = [
('GET', expect_url, {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(audit_templates))
def test_audit_templates_show(self):
audit_template = self.mgr.get(AUDIT_TMPL1['uuid'])
expect = [

View File

@@ -128,6 +128,24 @@ class AuditTemplateShellTest(base.CommandTestCase):
self.m_audit_template_mgr.list.assert_called_once_with(detail=False)
def test_do_audit_template_list_marker(self):
audit_template2 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_2)
self.m_audit_template_mgr.list.return_value = [audit_template2]
exit_code, results = self.run_cmd(
'audittemplate list --marker '
'f8e47706-efcf-49a4-a5c4-af604eb492f2')
self.assertEqual(0, exit_code)
self.assertEqual(
[self.resource_as_dict(audit_template2, self.SHORT_LIST_FIELDS,
self.SHORT_LIST_FIELD_LABELS)],
results)
self.m_audit_template_mgr.list.assert_called_once_with(
detail=False,
marker='f8e47706-efcf-49a4-a5c4-af604eb492f2')
def test_do_audit_template_list_detail(self):
audit_template1 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
audit_template2 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_2)

View File

@@ -63,6 +63,13 @@ fake_responses = {
STRATEGY1,
),
},
'/v1/strategies/%s/state' % STRATEGY1['name']:
{
'GET': (
{},
STRATEGY1,
),
},
}
fake_responses_pagination = {
@@ -180,3 +187,10 @@ class StrategyManagerTest(testtools.TestCase):
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(STRATEGY1['name'], strategy.name)
def test_strategies_state(self):
self.mgr.state(STRATEGY1['name'])
expect = [
('GET', '/v1/strategies/%s/state' % STRATEGY1['name'], {}, None),
]
self.assertEqual(expect, self.api.calls)

View File

@@ -17,6 +17,8 @@ import datetime
import mock
import six
from oslo_serialization import jsonutils
from watcherclient import shell
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
@@ -54,6 +56,8 @@ class StrategyShellTest(base.CommandTestCase):
resource_fields.STRATEGY_SHORT_LIST_FIELD_LABELS)
FIELDS = resource_fields.STRATEGY_FIELDS
FIELD_LABELS = resource_fields.STRATEGY_FIELD_LABELS
STATE_FIELDS = resource_fields.STRATEGY_STATE_FIELDS
STATE_FIELD_LABELS = resource_fields.STRATEGY_STATE_FIELD_LABELS
def setUp(self):
super(self.__class__, self).setUp()
@@ -155,3 +159,33 @@ class StrategyShellTest(base.CommandTestCase):
result)
self.m_strategy_mgr.get.assert_called_once_with(
'f8e47706-efcf-49a4-a5c4-af604eb492f2')
def test_do_strategy_state(self):
strategy1 = resource.Strategy(mock.Mock(), STRATEGY_1)
strategy_req = [
{'type': 'Datasource', 'mandatory': True,
'comment': '', 'state': 'gnocchi: True'},
{'type': 'Metrics', 'mandatory': False,
'comment': '', 'state': jsonutils.dumps([
{'compute.node.cpu.percent': 'available'},
{'cpu_util': 'available'},
{'memory.resident': 'available'},
{'hardware.memory.used': 'not available'}])},
{'type': 'CDM', 'mandatory': True,
'comment': '',
'state': jsonutils.dumps([{'compute_model': 'available'},
{'storage_model': 'not available'}])},
{'type': 'Name', 'mandatory': '', 'comment': '',
'state': strategy1.name}]
requirements = [resource.Strategy(mock.Mock(), req)
for req in strategy_req]
self.m_strategy_mgr.state.return_value = requirements
exit_code, results = self.run_cmd('strategy state basic')
self.assertEqual(0, exit_code)
self.assertEqual(
[self.resource_as_dict(req, self.STATE_FIELDS,
self.STATE_FIELD_LABELS)
for req in requirements],
results)

View File

@@ -30,7 +30,7 @@ class ActionManager(base.Manager):
return '/v1/actions/%s' % id if id else '/v1/actions'
def list(self, action_plan=None, audit=None, limit=None, sort_key=None,
sort_dir=None, detail=False):
sort_dir=None, detail=False, marker=None):
"""Retrieve a list of action.
:param action_plan: UUID of the action plan
@@ -52,13 +52,15 @@ class ActionManager(base.Manager):
:param detail: Optional, boolean whether to return detailed information
about actions.
:param marker: Optional, UUID of the last action in the previous page.
:returns: A list of actions.
"""
if limit is not None:
limit = int(limit)
filters = utils.common_filters(limit, sort_key, sort_dir)
filters = utils.common_filters(limit, sort_key, sort_dir, marker)
if action_plan is not None:
filters.append('action_plan_uuid=%s' % action_plan)
if audit is not None:

View File

@@ -28,8 +28,8 @@ from watcherclient.v1 import resource_fields as res_fields
def format_global_efficacy(global_efficacy):
formatted_global_eff = {}
for eff in global_efficacy:
eff_name = eff.get('name')
if (eff.get('value') is not None and eff.get('unit')):
eff_name = eff.get('name')
formatted_global_eff[eff_name] = "%(value).2f %(unit)s" % dict(
unit=eff.get('unit'),
value=eff.get('value'))
@@ -162,7 +162,7 @@ class ListActionPlan(command.Lister):
yaml_format.YAMLFormatter().emit_one(
column_names=list(resource.capitalize()
for resource in formatted_global_efficacy),
data=[value for value in formatted_global_efficacy.itervalues()],
data=[value for value in formatted_global_efficacy.values()],
stdout=out,
parsed_args=parsed_args,
)

View File

@@ -83,6 +83,13 @@ class ListAction(command.Lister):
metavar='<direction>',
choices=['asc', 'desc'],
help=_('Sort direction: "asc" (the default) or "desc".'))
parser.add_argument(
'--marker',
dest='marker',
metavar='<marker>',
default=None,
help=_('UUID of the last action in the previous page; '
'displays list of actions after "marker".'))
return parser

View File

@@ -36,7 +36,8 @@ class AuditManager(base.Manager):
return '/v1/audits/%s' % id if id else '/v1/audits'
def list(self, audit_template=None, limit=None, sort_key=None,
sort_dir=None, detail=False, goal=None, strategy=None):
sort_dir=None, detail=False, goal=None, strategy=None,
marker=None):
"""Retrieve a list of audit.
:param audit_template: Name of the audit template
@@ -57,13 +58,15 @@ class AuditManager(base.Manager):
:param detail: Optional, boolean whether to return detailed information
about audits.
:param marker: Optional, UUID of the last audit in the previous page.
:returns: A list of audits.
"""
if limit is not None:
limit = int(limit)
filters = utils.common_filters(limit, sort_key, sort_dir)
filters = utils.common_filters(limit, sort_key, sort_dir, marker)
if audit_template is not None:
filters.append('audit_template=%s' % audit_template)
if goal is not None:

View File

@@ -88,6 +88,13 @@ class ListAudit(command.Lister):
metavar='<direction>',
choices=['asc', 'desc'],
help=_('Sort direction: "asc" (the default) or "desc".'))
parser.add_argument(
'--marker',
dest='marker',
metavar='<marker>',
default=None,
help=_('UUID of the last audit in the previous page; '
'displays list of audits after "marker".'))
return parser

View File

@@ -33,7 +33,7 @@ class AuditTemplateManager(base.Manager):
return '/v1/audit_templates/%s' % id_ if id_ else '/v1/audit_templates'
def list(self, name=None, goal=None, strategy=None, limit=None,
sort_key=None, sort_dir=None, detail=False):
sort_key=None, sort_dir=None, detail=False, marker=None):
"""Retrieve a list of audit template.
:param name: Name of the audit template
@@ -54,13 +54,16 @@ class AuditTemplateManager(base.Manager):
:param detail: Optional, boolean whether to return detailed information
about audit_templates.
:param marker: Optional, UUID of the last audit template of
the previous page.
:returns: A list of audit templates.
"""
if limit is not None:
limit = int(limit)
filters = utils.common_filters(limit, sort_key, sort_dir)
filters = utils.common_filters(limit, sort_key, sort_dir, marker)
if name is not None:
filters.append('name=%s' % name)
if goal is not None:

View File

@@ -91,6 +91,13 @@ class ListAuditTemplate(command.Lister):
metavar='<direction>',
choices=['asc', 'desc'],
help=_('Sort direction: "asc" (the default) or "desc".'))
parser.add_argument(
'--marker',
dest='marker',
metavar='<marker>',
default=None,
help=_('UUID of the last audit template of the previous page; '
'displays list of audit templates after "marker".'))
return parser
@@ -176,26 +183,67 @@ class CreateAuditTemplate(command.ShowOne):
" - uuid: UUID2\n"
" - compute_nodes:\n"
" - name: compute1\n"
" - storage: \n"
" - availability_zones:\n"
" - name: AZ1\n"
" - name: AZ2\n"
" - volume_types:\n"
" - name: lvm1\n"
" - name: lvm2\n"
" - exclude:\n"
" - storage_pools:\n"
" - name: host0@backend0#pool0\n"
" - name: host1@backend1#pool1\n"
" - volumes:\n"
" - uuid: UUID1\n"
" - uuid: UUID2\n"
" - projects:\n"
" - uuid: UUID1\n"
" - uuid: UUID2\n"
"\n"
"JSON example:\n"
"[{\"compute\":\n"
" [{\"host_aggregates\": [\n"
" {\"id\": \"1\"},\n"
" {\"id\": \"2\"},\n"
" {\"id\": \"3\"}]},\n"
" {\"availability_zones\": [\n"
" {\"name\": \"AZ1\"},\n"
" {\"name\": \"AZ2\"}]},\n"
" {\"exclude\": [\n"
" {\"instances\": [\n"
" {\"uuid\": \"UUID1\"},\n"
" {\"uuid\": \"UUID2\"}\n"
" ]},\n"
" {\"compute_nodes\": [\n"
" {\"name\": \"compute1\"}\n"
" ]}\n"
" ]}]\n"
"}]\n"
"[\n"
" {\"compute\":\n"
" [{\"host_aggregates\": [\n"
" {\"id\": 1},\n"
" {\"id\": 2},\n"
" {\"id\": 3}]},\n"
" {\"availability_zones\": [\n"
" {\"name\": \"AZ1\"},\n"
" {\"name\": \"AZ2\"}]},\n"
" {\"exclude\": [\n"
" {\"instances\": [\n"
" {\"uuid\": \"UUID1\"},\n"
" {\"uuid\": \"UUID2\"}\n"
" ]},\n"
" {\"compute_nodes\": [\n"
" {\"name\": \"compute1\"}\n"
" ]}\n"
" ]}]\n"
" },\n"
" {\"storage\":\n"
" [{\"availability_zones\": [\n"
" {\"name\": \"AZ1\"},\n"
" {\"name\": \"AZ2\"}]},\n"
" {\"volume_types\": [\n"
" {\"name\": \"lvm1\"},\n"
" {\"name\": \"lvm2\"}]},\n"
" {\"exclude\": [\n"
" {\"storage_pools\": [\n"
" {\"name\": \"host0@backend0#pool0\"},\n"
" {\"name\": \"host1@backend1#pool1\"}\n"
" ]},\n"
" {\"volumes\": [\n"
" {\"uuid\": \"UUID1\"},\n"
" {\"uuid\": \"UUID2\"}\n"
" ]},\n"
" {\"projects\": [\n"
" {\"uuid\": \"UUID1\"},\n"
" {\"uuid\": \"UUID2\"}\n"
" ]},\n"
" ]}]\n"
" }\n"
" ]\n"
)
)

View File

@@ -99,6 +99,10 @@ STRATEGY_SHORT_LIST_FIELDS = ['uuid', 'name', 'display_name', 'goal_name']
STRATEGY_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Display name', 'Goal']
STRATEGY_STATE_FIELDS = ['type', 'state', 'mandatory', 'comment']
STRATEGY_STATE_FIELD_LABELS = ['Type', 'State', 'Mandatory', 'Comment']
# Metric Collector
METRIC_COLLECTOR_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at',
'endpoint', 'category']

View File

@@ -28,9 +28,14 @@ class StrategyManager(base.Manager):
resource_class = Strategy
@staticmethod
def _path(strategy=None):
return ('/v1/strategies/%s' % strategy
if strategy else '/v1/strategies')
def _path(strategy=None, state=False):
if strategy:
path = '/v1/strategies/%s' % strategy
if state:
path = '/v1/strategies/%s/state' % strategy
else:
path = '/v1/strategies'
return path
def list(self, goal=None, limit=None, sort_key=None,
sort_dir=None, detail=False):
@@ -82,3 +87,6 @@ class StrategyManager(base.Manager):
return self._list(self._path(strategy))[0]
except IndexError:
return None
def state(self, strategy):
return self._list(self._path(strategy, state=True))

View File

@@ -57,6 +57,40 @@ class ShowStrategy(command.ShowOne):
return column_headers, utils.get_item_properties(strategy, columns)
class StateStrategy(command.Lister):
"""Retrieve information about strategy requirements."""
def get_parser(self, prog_name):
parser = super(StateStrategy, self).get_parser(prog_name)
parser.add_argument(
'strategy',
metavar='<strategy>',
help=_('Name of the strategy'),
)
return parser
def _format_spec(self, requirements):
for req in requirements:
if type(req.state) == list:
req.state = jsonutils.dumps(req.state, indent=2)
return requirements
def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim")
try:
requirements = client.strategy.state(parsed_args.strategy)
except exceptions.HTTPNotFound as exc:
raise exceptions.CommandError(str(exc))
requirements = self._format_spec(requirements)
columns = res_fields.STRATEGY_STATE_FIELDS
column_headers = res_fields.STRATEGY_STATE_FIELD_LABELS
return (column_headers,
(utils.get_item_properties(item, columns)
for item in requirements))
class ListStrategy(command.Lister):
"""List information on retrieved strategies."""