Compare commits

..

62 Commits

Author SHA1 Message Date
Jenkins
cec8ee4f15 Merge "Add functional tests to watcherclient" 2017-01-19 14:54:54 +00:00
Jenkins
4713a69a18 Merge "Support parents field along with planner changes" 2017-01-19 14:19:12 +00:00
Jenkins
22bc9a97c9 Merge "Updated from global requirements" 2017-01-17 16:12:11 +00:00
licanwei
11e3568d66 update '--detail' help in goal list
help=_("Show detailed information about metric collectors."))
==>
help=_("Show detailed information about each goal."))

Change-Id: If6bf750a6a7941524dd144430abcc48b9f44a9d3
2017-01-17 16:49:19 +08:00
OpenStack Proposal Bot
96ef016b81 Updated from global requirements
Change-Id: I6239c242d4ab23ec885556daddc45d0f97b8234c
2017-01-16 17:28:36 +00:00
Alexander Chadin
c0f6ea54f6 Add functional tests to watcherclient
This Patch Set implements functional tests for
watcherclient to cover all use cases of watcher objects.

Implements : blueprint tempest-cli-test
Change-Id: I920d5b1ae25c62a72d3474538f866a5415c2e119
2017-01-16 17:08:27 +00:00
Alexander Chadin
b8666d511e Support parents field along with planner changes
This patch set allows python-watcherclient to show
parents field instead of next_uuid field.

Partially Implements: blueprint planner-storage-action-plan
Change-Id: I00cefd2ba6dad5daa6470c851c2fdfb7c22d7a63
Depends-On: Ide2c8fc521488e486eac8f9f89d3f808ccf4b4d7
2017-01-16 20:05:10 +03:00
Jenkins
4a093e168d Merge "Enable coverage report in console output" 2017-01-16 11:09:11 +00:00
Jenkins
1fa93eeb8f Merge "Add auto_trigger support" 2017-01-16 10:18:55 +00:00
Jenkins
d36f2de07e Merge "Add param 'goal' and 'strategy' in list()" 2017-01-13 16:38:00 +00:00
Jenkins
d17bc25fef Merge "Use keystoneauth instead of keystoneclient" 2017-01-13 15:06:21 +00:00
Lucky samadhiya
075b32efe2 changes to make consistent with other openstack component
Change-Id: I4a530ff7912f129823783c742f906d769965872e
2017-01-13 10:10:01 +00:00
Jeremy Liu
a2cbb8d4b2 Use keystoneauth instead of keystoneclient
keystoneauth was extracted from keystoneclient, this CR replaces usage
of keystoneclient in favor of keystoneauth.

Implements: blueprint use-keystoneauth-instead-of-keystoneclient
Change-Id: Iada0f8b45ae30a89908a4ae9e5f55a33e3744e17
2017-01-13 16:14:33 +08:00
Jeremy Liu
ecfa35ecc2 Remove unused files
`MANIFEST.in` was used to handle git files but now pbr can do it instead.
`openstack-common.conf` was used to copy files from oslo-incubator, but
oslo-incubator is no longer maintained. So delete the two files from repo.

Change-Id: I976434e8283c588191d7bfb9321f46cea724406a
2017-01-13 14:39:04 +08:00
Jeremy Liu
546d03c3aa Enable coverage report in console output
Change-Id: I861dbe49eb888e9c51a0c649dffde4479f19ee67
2017-01-13 10:51:12 +08:00
licanwei
3a04f9fb9a Add param 'goal' and 'strategy' in list()
watcher  audit list --goal dummy
list() got an unexpected keyword argument 'goal'

watcher  audit list --strategy dummy
list() got an unexpected keyword argument 'strategy'

Change-Id: Iba4b89da1669f07002718c885107ee3813ca676a
Closes-Bug: #1654962
2017-01-12 11:54:56 +08:00
DeepaJon
17a144e2f8 Removes unnecessary utf-8 encoding
This patches removes unnecessary utf-8 encoding in the
watcherclient/*
doc/source/conf.py

Change-Id: Ic74c9c57f52f7bf021dfa25d4de069b57e5cb474
2017-01-11 14:23:40 +05:30
licanwei
658fd038f3 update audit create '--interval' description
update audit create '--interval' description

Change-Id: I03779a8fe446c051397f02cfb2d73bda804706fe
2017-01-10 18:02:55 +01:00
Jenkins
d2aede7151 Merge "Fix a typo in audittemplate help" 2017-01-10 16:29:41 +00:00
Jenkins
de54d5b8ba Merge "Add unit for continuous audit's interval." 2017-01-10 16:27:10 +00:00
Jenkins
ae003b7e76 Merge "use 'auto' instead of None" 2017-01-10 16:27:01 +00:00
ericxiett
d08c0e3ef1 Add unit for continuous audit's interval.
End user does not get the unit of 'interval' when use command
``watcher audit create -h``. This patch adds unit for the option
'interval'. Aslo use single quotes.

Change-Id: I9e3df031822f2f2704a3c661c6a5953d7e0caae5
Closes-Bug: #1653322
2017-01-03 08:07:10 +08:00
licanwei
c68120edcd Fix a typo in audittemplate help
'Descrition'->'Description'

Change-Id: Ie1f06f14a06e27c52b41b1d8c7da1f160212288d
2016-12-26 10:29:02 +08:00
licanwei
bf7085fabd use 'auto' instead of None
In cli cmds:watcher audit create,
the Strategy is None if no --strategy
It's better to use default instead of None
watcher audit create -g thermal_optimization
+-------------+--------------------------------------+
| Field       | Value                                |
+-------------+--------------------------------------+
| UUID        | 2bc3001d-683a-48ee-b2f2-766cb8d6329e |
| Created At  | 2016-12-22T08:41:21.173601+00:00     |
| Updated At  | None                                 |
| Deleted At  | None                                 |
| State       | PENDING                              |
| Audit Type  | ONESHOT                              |
| Parameters  | {}                                   |
| Interval    | None                                 |
| Goal        | thermal_optimization                 |
| Strategy    | None                                 |
| Audit Scope | []                                   |
+-------------+--------------------------------------+

Change-Id: I5798df81303c425ed0e26e401589e6680b945bf6
2016-12-22 17:52:29 +08:00
Jenkins
ef93a57b46 Merge "too many digits after the decimal point" 2016-12-19 11:03:17 +00:00
Alexander Chadin
6e291f0f13 Add auto_trigger support
This patch set adds support of auto-triggering for action plans.
You can use it by adding new attribute '--auto-trigger' to
watcher audit create command.

Change-Id: I2e4ee53f9f639cb3248c0e4dcc7b9716ff1fc352
Partially-Implements: blueprint automatic-triggering-audit
Depends-On: I36b7dff8eab5f6ebb18f6f4e752cf4b263456293
2016-12-16 17:01:46 +00:00
Jenkins
73dcf25b63 Merge "Replace six.iteritems() with .items()" 2016-12-16 16:34:15 +00:00
zhangguoqing
0ecafcd6a0 Fix TOKENID format which should without dashed
[1] is wrong amendment which had change the TOKENID's format.
Since [2] had been merged, this patch fix the format without dashed.

[1] https://review.openstack.org/#/c/399359/1/watcherclient/tests/keystone_client_fixtures.py
[2] https://review.openstack.org/#/c/398788

Change-Id: I80b953cbf31e2b09160f0b55ffe48eea845c2dd4
2016-12-15 14:40:21 +00:00
licanwei
529f406075 too many digits after the decimal point
'global_efficacy' field print too many digits
 after the decimal point.
 for example 14.2857142857 %
 two digits are enough
 for example 14.29%

Closes-Bug: #1650159

Change-Id: I4af3ef5b250d7e0b921269cc91ec46978d090c10
2016-12-15 09:08:34 +00:00
Antoine Cabot
b66834506a Remove readme reference because of sphinx error
Change-Id: Ide4b84131fd516ae4784dab9673354c5bab0968a
Closes-Bug: #1650173
2016-12-15 09:42:46 +01:00
gengchc2
5b8e3d4d6c Replace six.iteritems() with .items()
1.As mentioned in [1], we should avoid using
six.iteritems to achieve iterators. We can
use dict.items instead, as it will return
iterators in PY3 as well. And dict.items/keys
will more readable. 2.In py2, the performance
about list should be negligible, see the link [2].
[1] https://wiki.openstack.org/wiki/Python3
[2] http://lists.openstack.org/pipermail/openstack-dev/2015-June/066391.html

Change-Id: Ic6af69d5b4b36f3dd02aab0f9c446aa54970f388
2016-12-09 11:31:24 +08:00
OpenStack Proposal Bot
fce4e7e10c Updated from global requirements
Change-Id: Ifc867f3be4b569c6ec9a5fce1149d949bce09685
2016-12-02 17:18:38 +00:00
Flavio Percoco
8004b4ec0b Show team and repo badges on README
This patch adds the team's and repository's badges to the README file.
The motivation behind this is to communicate the project status and
features at first glance.

For more information about this effort, please read this email thread:

http://lists.openstack.org/pipermail/openstack-dev/2016-October/105562.html

To see an example of how this would look like check:

https://gist.github.com/f8cdc5cce16393ddd9885f99ad05a610

Change-Id: I58f89eb319919a824fea21961b4338e2d1132f6e
2016-11-25 17:23:24 +01:00
OpenStack Proposal Bot
0584350663 Updated from global requirements
Change-Id: I98b97ab69a726f1634f6c96274d02cde60b064c2
2016-11-18 21:33:33 +00:00
qinchunhua
ccef7be62c Use uuidutils instead of uuid.uuid4()
Openstack common has a wrapper for generating uuids.
We should only use that function when generating
uuids for consistency.

Change-Id: Ic36426c6ab4228826b66a926e74e3331e9a9abdc
Closes-Bug: #1082248
2016-11-18 11:27:48 +08:00
Jenkins
d960977c96 Merge "Remove unnecessary ')'" 2016-11-16 21:15:25 +00:00
OpenStack Proposal Bot
51b295c21c Updated from global requirements
Change-Id: Ib24775ef8844c56b64d1b8b2fea30eb69f252135
2016-11-14 15:13:17 +00:00
Jenkins
e3478de41d Merge "Fix a typo error in a help message in the client" 2016-11-14 13:54:30 +00:00
David.T
c8bf47d72d Remove obsolete object attributes
As we are about to version the Watcher objects, we need to make sure
that upcoming model/object modifications are additive in order to
avoid having to bump the major version of the API. Therefore,
this changeset removes 2 unused DB fields: extra in Audit Template
object and deadline in Audit object.

Change-Id: Ib9750d2dee8b565bd837341f826999b64c4e2cc3
Partially-Implements: blueprint watcher-versioned-objects
2016-11-14 10:03:37 +00:00
Tin Lam
c83499e85f Fix a typo error in a help message in the client
In the class UpdateAuditTemplate, there is a help message with an extra
')'.  This patch set removes that extra ')'.

Change-Id: I1463144e38ebb5db59f49a94406ff884f0e2450b
Closes-Bug: #1639681
2016-11-10 12:45:04 -06:00
Jenkins
28acfe1535 Merge "Updated from global requirements" 2016-11-08 12:55:57 +00:00
OpenStack Proposal Bot
9edd6afa28 Updated from global requirements
Change-Id: I330f1fe0daa0431d47719268cf2c4c92e89387e1
2016-11-06 02:07:40 +00:00
caihui
9923b51944 Remove unnecessary ')'
Change-Id: Ia16cfe517cbd4511fb29b32e4ce9f2cc42151dc4
2016-11-04 14:27:03 +08:00
Tony Xu
7dfcdba21e Add Python 3.5 classifier and venv
Now that there is a passing gate job, we can claim
support for Python 3.5 in the classifier.
This patch also adds the convenience py35 venv.

Change-Id: I2ed2d7c62c32df6593ae7b1e00da92fb56d391c6
2016-10-26 10:36:54 +08:00
gecong1973
dfdee212cc Add __ne__ built-in function
In Python 3 __ne__ by default delegates to __eq__ and inverts the
result, but in Python 2 they urge you to define __ne__ when you
define __eq__ for it to work properly [1].There are no implied
relationships among the comparison operators. The truth of x==y
does not imply that x!=y is false. Accordingly, when defining __eq__(),
one should also define __ne__() so that the operators will behave as
expected.
[1]https://docs.python.org/2/reference/datamodel.html#object.__ne__

Change-Id: I6b43b9ac33b4f6e6331b390366e68f2a5bbb997c
2016-10-17 09:02:05 +08:00
Jenkins
50af3b6bb8 Merge "Add support for Audit Scope" 2016-10-14 14:59:55 +00:00
OpenStack Proposal Bot
494b2ecc88 Updated from global requirements
Change-Id: I13491acd6fe8a7c2c9dcf4211769daaf58190ff3
2016-10-13 05:28:46 +00:00
Jenkins
d109e9d9a0 Merge "Add service support" 2016-10-12 12:48:46 +00:00
Alexander Chadin
c185f32ebb Add support for Audit Scope
This patch set adds audit scope support in python-watcherclient.

Change-Id: I2a94320bec0fd7051a38bee1c872148e39f69d0c
Partially-Implements: blueprint define-the-audit-scope
2016-10-07 12:30:44 +03:00
Jenkins
11056302b4 Merge "Add parameters in Audit creation attributes list" 2016-10-06 09:07:34 +00:00
David.T
466131e788 Add parameters in Audit creation attributes list
Change-Id: Ib4371223af76d20de2f6d019b73164058f1b4e89
Closes-Bug: #1617320
2016-10-05 17:18:20 +02:00
OpenStack Proposal Bot
c88551435d Updated from global requirements
Change-Id: I5e9d875b0049ae3d9fc0d2dde6943db0c2d84730
2016-10-04 14:02:54 +00:00
Jenkins
77d282e452 Merge "Added support for Client creation from KS session" 2016-10-04 08:08:06 +00:00
Vincent Francoise
940a7093e6 Added support for Client creation from KS session
In this changeset, I refactored some of the code to now support
the creation of a watcher client using a Keystone session as it was
already possible in most other OS projects.

Change-Id: I3462daeaac9103138366a0fcaa2fc46729bbd8ce
Related-Bug: #1625643
Closes-Bug: #1623070
2016-09-28 17:54:29 +02:00
OpenStack Proposal Bot
0c0f6cb079 Updated from global requirements
Change-Id: I5c16b623d05241e2744d89160322c5e4c3d08c2d
2016-09-27 10:08:00 +00:00
Jenkins
9c3440df44 Merge "Add constraint target to tox.ini and remove 1 dep" 2016-09-26 09:39:54 +00:00
Tony Xu
f50df0dc74 Add fixtures to test-requirements
Closes-Bug: #1626372
Change-Id: I671a925f36e3d177f55036766e6792bc87ab58a0
2016-09-22 17:38:14 +08:00
David.T
abf3257e1c Add constraint target to tox.ini and remove 1 dep
This adds a pip install command to tox.ini that is only used when the
tox env is passed with the 'constraints' factor appended onto it.
As such this will not effect developer workflows or current unit tests.

The initial use of this will be in a non-voting job, to verify that the
constrained checks with tox are stable.  DevStack is already running
constrained jobs, as such problems are no expected.

To run a tox with pip using constraints on a developer system a
developer should run the desired tox environment with -constraints.
For example: $(tox -epy27-constraints)
Pip will pull the current version of the upper-constraints.txt file down
from the git.openstack.org, however this method can be overriden to use
a local file setting the environment variable "UPPER_CONSTRAINTS_FILE"
to the local path or a different URL, it is passed directly to pip.

This is currently not enabled in the default tox run, however it is
possible to enable it as a default by adding it to 'envlist' in tox.ini

This also removes requirements.txt from tox.ini deps
This is redundant, per lifeless email:
http://lists.openstack.org/pipermail/openstack-dev/2015-July/069663.html

Change-Id: Id953a99cf2f83533a38a03f9934f4ec818bec03f
2016-09-21 12:15:05 +02:00
Alexander Chadin
4e86151f81 Add service support
This patch set adds service support to watcherclient.
There is two new commands:
1. service list
2. service show

Change-Id: I669fd69ca7e854f8f576a017447ef003c6cf3d3b
Partially-Implements: blueprint watcher-service-list
2016-09-21 10:31:15 +03:00
David.T
a48a9213f0 python-openstackclient ClientManager interface changed
I update the watcher client plugin to correctly used ClientManager
auth parameters

Change-Id: Ie2e4db3b63ea1671147a407480ce0d2569d5b057
Closes-bug: #1625643
2016-09-20 16:17:49 +02:00
avnish
ad4ee3fcc7 Update home page link in cfg file
Change-Id: I4630862bc2e7cd64f158173fdf05af5a054afd92
2016-09-20 08:30:18 +05:30
Cao Xuan Hoang
4df180fc1f Clean imports in code
This patch set modifies lines which are importing objects
instead of modules. As per openstack import guide lines, user should
import modules in a file not objects.

http://docs.openstack.org/developer/hacking/#imports

Change-Id: I17e692108c058ba393eb2a81b8814f879956a991
2016-09-06 10:58:04 +07:00
88 changed files with 3120 additions and 1928 deletions

View File

@@ -2,4 +2,3 @@
host=review.openstack.org
port=29418
project=openstack/python-watcherclient.git
defaultbranch=stable/newton

View File

@@ -1,6 +0,0 @@
include AUTHORS
include ChangeLog
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

View File

@@ -1,3 +1,12 @@
========================
Team and repository tags
========================
.. image:: http://governance.openstack.org/badges/python-watcherclient.svg
:target: http://governance.openstack.org/reference/tags/index.html
.. Change things from this point on
====================
python-watcherclient
====================

View File

@@ -1,51 +0,0 @@
.. toctree::
:maxdepth: 1
watcherclient._i18n.rst
watcherclient.client.rst
watcherclient.common.apiclient.base.rst
watcherclient.common.apiclient.exceptions.rst
watcherclient.common.base.rst
watcherclient.common.cliutils.rst
watcherclient.common.command.rst
watcherclient.common.http.rst
watcherclient.common.utils.rst
watcherclient.exceptions.rst
watcherclient.plugin.rst
watcherclient.shell.rst
watcherclient.tests.keystone_client_fixtures.rst
watcherclient.tests.test_client.rst
watcherclient.tests.test_http.rst
watcherclient.tests.test_import.rst
watcherclient.tests.test_utils.rst
watcherclient.tests.utils.rst
watcherclient.tests.v1.base.rst
watcherclient.tests.v1.test_action.rst
watcherclient.tests.v1.test_action_plan.rst
watcherclient.tests.v1.test_action_plan_shell.rst
watcherclient.tests.v1.test_action_shell.rst
watcherclient.tests.v1.test_audit.rst
watcherclient.tests.v1.test_audit_shell.rst
watcherclient.tests.v1.test_audit_template.rst
watcherclient.tests.v1.test_audit_template_shell.rst
watcherclient.tests.v1.test_goal.rst
watcherclient.tests.v1.test_goal_shell.rst
watcherclient.tests.v1.test_metric_collector.rst
watcherclient.tests.v1.test_strategy.rst
watcherclient.tests.v1.test_strategy_shell.rst
watcherclient.v1.action.rst
watcherclient.v1.action_plan.rst
watcherclient.v1.action_plan_shell.rst
watcherclient.v1.action_shell.rst
watcherclient.v1.audit.rst
watcherclient.v1.audit_shell.rst
watcherclient.v1.audit_template.rst
watcherclient.v1.audit_template_shell.rst
watcherclient.v1.client.rst
watcherclient.v1.goal.rst
watcherclient.v1.goal_shell.rst
watcherclient.v1.metric_collector.rst
watcherclient.v1.resource_fields.rst
watcherclient.v1.strategy.rst
watcherclient.v1.strategy_shell.rst
watcherclient.version.rst

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

View File

@@ -14,7 +14,6 @@ Contents:
.. toctree::
:maxdepth: 1
readme
installation
api_v1
cli

View File

@@ -1 +0,0 @@
.. include:: ../../README.rst

View File

@@ -1,10 +0,0 @@
[DEFAULT]
# The list of modules to copy from oslo-incubator.git
module=apiclient
module=cliutils
module=_i18n
# The base module to hold the copy of openstack.common
base=watcherclient

View File

@@ -3,11 +3,12 @@
# process, which may cause wedges in the gate later.
Babel>=2.3.4 # BSD
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
osc-lib>=1.0.2 # Apache-2.0
cliff>=2.3.0 # Apache-2.0
osc-lib>=1.2.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.utils>=3.16.0 # Apache-2.0
pbr>=1.6 # Apache-2.0
PrettyTable<0.8,>=0.7 # BSD
python-keystoneclient!=2.1.0,>=2.0.0 # Apache-2.0
oslo.utils>=3.18.0 # Apache-2.0
pbr>=1.8 # Apache-2.0
PrettyTable<0.8,>=0.7.1 # BSD
keystoneauth1>=2.17.0 # Apache-2.0
six>=1.9.0 # MIT
PyYAML>=3.10.0 # MIT

View File

@@ -17,6 +17,7 @@ classifier =
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
[files]
packages =
@@ -27,7 +28,7 @@ console_scripts =
watcher = watcherclient.shell:main
openstack.cli.extension =
infra_optim = watcherclient.plugin
infra_optim = watcherclient.osc.plugin
# Entry points for the 'openstack' command
openstack.infra_optim.v1 =
@@ -61,6 +62,9 @@ openstack.infra_optim.v1 =
optimize_scoringengine_show = watcherclient.v1.scoring_engine_shell:ShowScoringEngine
optimize_scoringengine_list = watcherclient.v1.scoring_engine_shell:ListScoringEngine
optimize_service_show = watcherclient.v1.service_shell:ShowService
optimize_service_list = watcherclient.v1.service_shell:ListService
# The same as above but used by the 'watcher' command
watcherclient.v1 =
goal_show = watcherclient.v1.goal_shell:ShowGoal
@@ -94,6 +98,9 @@ watcherclient.v1 =
scoringengine_show = watcherclient.v1.scoring_engine_shell:ShowScoringEngine
scoringengine_list = watcherclient.v1.scoring_engine_shell:ListScoringEngine
service_show = watcherclient.v1.service_shell:ShowService
service_list = watcherclient.v1.service_shell:ListService
[pbr]
autodoc_index_modules = True

View File

@@ -2,13 +2,14 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
coverage>=3.6 # Apache-2.0
coverage>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
hacking<0.11,>=0.10.2
mock>=2.0 # BSD
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
oslosphinx>=4.7.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
python-subunit>=0.0.18 # Apache-2.0/BSD
sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT

View File

@@ -1,15 +1,16 @@
[tox]
minversion = 1.8
envlist = py34,py27,pep8
envlist = py35,py34,py27,pep8
skipsdist = True
[testenv]
usedevelop = True
install_command =
constraints: pip install -U --force-reinstall -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/newton} {opts} {packages}
constraints: pip install -U --force-reinstall -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
pip install -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
OS_TEST_PATH=./watcherclient/tests/unit
deps = -r{toxinidir}/test-requirements.txt
commands = python setup.py testr --slowest --testr-args='{posargs}'
@@ -20,7 +21,9 @@ commands = flake8
commands = {posargs}
[testenv:cover]
commands = python setup.py testr --coverage --testr-args='{posargs}'
commands =
python setup.py testr --coverage --testr-args='{posargs}'
coverage report
[testenv:docs]
commands = python setup.py build_sphinx

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -12,112 +10,176 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient.v2_0 import client as ksclient
import oslo_i18n
from keystoneauth1 import loading as kaloading
from oslo_utils import importutils
from watcherclient._i18n import _
from watcherclient.common import utils
from watcherclient import exceptions as exc
oslo_i18n.install('watcherclient')
from watcherclient.common import api_versioning
from watcherclient import exceptions
def _get_ksclient(**kwargs):
"""Get an endpoint and auth token from Keystone.
:param kwargs: keyword args containing credentials:
* username: name of user
* password: user's password
* auth_url: endpoint to authenticate against
* insecure: allow insecure SSL (no cert verification)
* tenant_{name|id}: name or ID of tenant
"""
return ksclient.Client(username=kwargs.get('username'),
password=kwargs.get('password'),
tenant_id=kwargs.get('tenant_id'),
tenant_name=kwargs.get('tenant_name'),
auth_url=kwargs.get('auth_url'),
insecure=kwargs.get('insecure'))
def _get_endpoint(client, **kwargs):
"""Get an endpoint using the provided keystone client."""
attr = None
filter_value = None
if kwargs.get('region_name'):
attr = 'region'
filter_value = kwargs.get('region_name')
return client.service_catalog.url_for(
service_type=kwargs.get('service_type') or 'infra-optim',
attr=attr,
filter_value=filter_value,
endpoint_type=kwargs.get('endpoint_type') or 'publicURL')
def get_client(api_version, **kwargs):
"""Get an authenticated client, based on the credentials in args.
def get_client(api_version, os_auth_token=None, watcher_url=None,
os_username=None, os_password=None, os_auth_url=None,
os_project_id=None, os_project_name=None, os_tenant_id=None,
os_tenant_name=None, os_region_name=None,
os_user_domain_id=None, os_user_domain_name=None,
os_project_domain_id=None, os_project_domain_name=None,
os_service_type=None, os_endpoint_type=None,
insecure=None, timeout=None, os_cacert=None, ca_file=None,
os_cert=None, cert_file=None, os_key=None, key_file=None,
os_watcher_api_version=None, max_retries=None,
retry_interval=None, session=None, os_endpoint_override=None,
**ignored_kwargs):
"""Get an authenticated client, based on the credentials.
:param api_version: the API version to use. Valid value: '1'.
:param kwargs: keyword args containing credentials, either:
* os_auth_token: pre-existing token to re-use
* watcher_url: watcher API endpoint
or:
* os_username: name of user
* os_password: user's password
* os_auth_url: endpoint to authenticate against
* insecure: allow insecure SSL (no cert verification)
* os_tenant_{name|id}: name or ID of tenant
:param os_auth_token: pre-existing token to re-use
:param watcher_url: watcher API endpoint
:param os_username: name of a user
:param os_password: user's password
:param os_auth_url: endpoint to authenticate against
:param os_project_id: ID of a project
:param os_project_name: name of a project
:param os_tenant_id: ID of a tenant (deprecated in favour of
os_project_id)
:param os_tenant_name: name of a tenant (deprecated in favour of
os_project_name)
:param os_region_name: name of a keystone region
:param os_user_domain_id: ID of a domain the user belongs to
:param os_user_domain_name: name of a domain the user belongs to
:param os_project_domain_id: ID of a domain the project belongs to
:param os_project_domain_name: name of a domain the project belongs to
:param os_service_type: the type of service to lookup the endpoint for
:param os_endpoint_type: the type (exposure) of the endpoint
:param insecure: allow insecure SSL (no cert verification)
:param timeout: allows customization of the timeout for client HTTP
requests
:param os_cacert: path to cacert file
:param ca_file: path to cacert file, deprecated in favour of os_cacert
:param os_cert: path to cert file
:param cert_file: path to cert file, deprecated in favour of os_cert
:param os_key: path to key file
:param key_file: path to key file, deprecated in favour of os_key
:param os_watcher_api_version: watcher API version to use
:param max_retries: Maximum number of retries in case of conflict error
:param retry_interval: Amount of time (in seconds) between retries in case
of conflict error
:param session: Keystone session to use
:param os_endpoint_override: watcher API endpoint
:param ignored_kwargs: all the other params that are passed. Left for
backwards compatibility. They are ignored.
"""
if kwargs.get('os_auth_token') and kwargs.get('watcher_url'):
token = kwargs.get('os_auth_token')
endpoint = kwargs.get('watcher_url')
auth_ref = None
elif (kwargs.get('os_username') and
kwargs.get('os_password') and
kwargs.get('os_auth_url') and
(kwargs.get('os_tenant_id') or kwargs.get('os_tenant_name'))):
ks_kwargs = {
'username': kwargs.get('os_username'),
'password': kwargs.get('os_password'),
'tenant_id': kwargs.get('os_tenant_id'),
'tenant_name': kwargs.get('os_tenant_name'),
'auth_url': kwargs.get('os_auth_url'),
'service_type': kwargs.get('os_service_type'),
'endpoint_type': kwargs.get('os_endpoint_type'),
'insecure': kwargs.get('insecure'),
}
_ksclient = _get_ksclient(**ks_kwargs)
token = (kwargs.get('os_auth_token')
if kwargs.get('os_auth_token')
else _ksclient.auth_token)
ks_kwargs['region_name'] = kwargs.get('os_region_name')
endpoint = (kwargs.get('watcher_url') or
_get_endpoint(_ksclient, **ks_kwargs))
auth_ref = _ksclient.auth_ref
else:
e = (_('Must provide Keystone credentials or user-defined endpoint '
'and token'))
raise exc.AmbiguousAuthSystem(e)
cli_kwargs = {
'token': token,
'insecure': kwargs.get('insecure'),
'timeout': kwargs.get('timeout'),
'ca_file': kwargs.get('ca_file'),
'cert_file': kwargs.get('cert_file'),
'key_file': kwargs.get('key_file'),
'auth_ref': auth_ref,
os_service_type = os_service_type or 'infra-optim'
os_endpoint_type = os_endpoint_type or 'publicURL'
project_id = (os_project_id or os_tenant_id)
project_name = (os_project_name or os_tenant_name)
kwargs = {
'os_watcher_api_version': os_watcher_api_version,
'max_retries': max_retries,
'retry_interval': retry_interval,
}
endpoint = watcher_url or os_endpoint_override
cacert = os_cacert or ca_file
cert = os_cert or cert_file
key = os_key or key_file
if os_auth_token and endpoint:
kwargs.update({
'token': os_auth_token,
'insecure': insecure,
'ca_file': cacert,
'cert_file': cert,
'key_file': key,
'timeout': timeout,
})
elif os_auth_url:
auth_type = 'password'
auth_kwargs = {
'auth_url': os_auth_url,
'project_id': project_id,
'project_name': project_name,
'user_domain_id': os_user_domain_id,
'user_domain_name': os_user_domain_name,
'project_domain_id': os_project_domain_id,
'project_domain_name': os_project_domain_name,
}
if os_username and os_password:
auth_kwargs.update({
'username': os_username,
'password': os_password,
})
elif os_auth_token:
auth_type = 'token'
auth_kwargs.update({
'token': os_auth_token,
})
# Create new session only if it was not passed in
if not session:
loader = kaloading.get_plugin_loader(auth_type)
auth_plugin = loader.load_from_options(**auth_kwargs)
# Let keystoneauth do the necessary parameter conversions
session = kaloading.session.Session().load_from_options(
auth=auth_plugin, insecure=insecure, cacert=cacert,
cert=cert, key=key, timeout=timeout,
)
return Client(api_version, endpoint, **cli_kwargs)
exception_msg = _('Must provide Keystone credentials or user-defined '
'endpoint and token')
if not endpoint:
if session:
try:
# Pass the endpoint, it will be used to get hostname
# and port that will be used for API version caching. It will
# be also set as endpoint_override.
endpoint = session.get_endpoint(
service_type=os_service_type,
interface=os_endpoint_type,
region_name=os_region_name
)
except Exception as e:
raise exceptions.AmbiguousAuthSystem(
exception_msg + _(', error was: %s') % e)
else:
# Neither session, nor valid auth parameters provided
raise exceptions.AmbiguousAuthSystem(exception_msg)
# Always pass the session
kwargs['session'] = session
return Client(api_version, endpoint, **kwargs)
def _get_client_class_and_version(version):
if not isinstance(version, api_versioning.APIVersion):
version = api_versioning.get_api_version(version)
else:
api_versioning.check_major_version(version)
if version.is_latest():
raise exceptions.UnsupportedVersion(
_("The version should be explicit, not latest."))
return version, importutils.import_class(
"watcherclient.v%s.client.Client" % version.ver_major)
def Client(version, *args, **kwargs):
module = utils.import_versioned_module(version, 'client')
client_class = getattr(module, 'Client')
"""Initialize client object based on given version.
HOW-TO:
The simplest way to create a client instance is initialization with your
credentials::
>>> from watcherclient import client
>>> watcher = client.Client(VERSION, USERNAME, PASSWORD,
... PROJECT_ID, AUTH_URL)
Here ``VERSION`` can be a string or
``watcherclient.api_versions.APIVersion`` obj. If you prefer string value,
you can use ``1`` or ``1.X`` (where X is a microversion).
Alternatively, you can create a client instance using the keystoneauth
session API. See "The watcherclient Python API" page at
python-watcherclient's doc.
"""
api_version, client_class = _get_client_class_and_version(version)
return client_class(*args, **kwargs)

View File

@@ -0,0 +1,215 @@
#
# 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 logging
import os
import pkgutil
import re
from oslo_utils import strutils
from watcherclient._i18n import _, _LW
from watcherclient import exceptions
LOG = logging.getLogger(__name__)
if not LOG.handlers:
LOG.addHandler(logging.StreamHandler())
HEADER_NAME = "OpenStack-API-Version"
SERVICE_TYPE = "infra-optim"
# key is a deprecated version and value is an alternative version.
DEPRECATED_VERSIONS = {}
_type_error_msg = _("'%(other)s' should be an instance of '%(cls)s'")
class APIVersion(object):
"""This class represents an API Version Request.
This class provides convenience methods for manipulation
and comparison of version numbers that we need to do to
implement microversions.
"""
def __init__(self, version_str=None):
"""Create an API version object.
:param version_str: String representation of APIVersionRequest.
Correct format is 'X.Y', where 'X' and 'Y'
are int values. None value should be used
to create Null APIVersionRequest, which is
equal to 0.0
"""
self.ver_major = 0
self.ver_minor = 0
if version_str is not None:
match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0|latest)$", version_str)
if match:
self.ver_major = int(match.group(1))
if match.group(2) == "latest":
# NOTE(andreykurilin): Infinity allows to easily determine
# latest version and doesn't require any additional checks
# in comparison methods.
self.ver_minor = float("inf")
else:
self.ver_minor = int(match.group(2))
else:
msg = _("Invalid format of client version '%s'. "
"Expected format 'X.Y', where X is a major part and Y "
"is a minor part of version.") % version_str
raise exceptions.UnsupportedVersion(msg)
def __str__(self):
"""Debug/Logging representation of object."""
if self.is_latest():
return "Latest API Version Major: %s" % self.ver_major
return ("API Version Major: %s, Minor: %s"
% (self.ver_major, self.ver_minor))
def __repr__(self):
if self.is_null():
return "<APIVersion: null>"
else:
return "<APIVersion: %s>" % self.get_string()
def is_null(self):
return self.ver_major == 0 and self.ver_minor == 0
def is_latest(self):
return self.ver_minor == float("inf")
def __lt__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(_type_error_msg % {"other": other,
"cls": self.__class__})
return ((self.ver_major, self.ver_minor) <
(other.ver_major, other.ver_minor))
def __eq__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(_type_error_msg % {"other": other,
"cls": self.__class__})
return ((self.ver_major, self.ver_minor) ==
(other.ver_major, other.ver_minor))
def __gt__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(_type_error_msg % {"other": other,
"cls": self.__class__})
return ((self.ver_major, self.ver_minor) >
(other.ver_major, other.ver_minor))
def __le__(self, other):
return self < other or self == other
def __ne__(self, other):
return not self.__eq__(other)
def __ge__(self, other):
return self > other or self == other
def matches(self, min_version, max_version):
"""Matches the version object.
Returns whether the version object represents a version
greater than or equal to the minimum version and less than
or equal to the maximum version.
:param min_version: Minimum acceptable version.
:param max_version: Maximum acceptable version.
:returns: boolean
If min_version is null then there is no minimum limit.
If max_version is null then there is no maximum limit.
If self is null then raise ValueError
"""
if self.is_null():
raise ValueError(_("Null APIVersion doesn't support 'matches'."))
if max_version.is_null() and min_version.is_null():
return True
elif max_version.is_null():
return min_version <= self
elif min_version.is_null():
return self <= max_version
else:
return min_version <= self <= max_version
def get_string(self):
"""Version string representation.
Converts object to string representation which if used to create
an APIVersion object results in the same version.
"""
if self.is_null():
raise ValueError(
_("Null APIVersion cannot be converted to string."))
elif self.is_latest():
return "%s.%s" % (self.ver_major, "latest")
return "%s.%s" % (self.ver_major, self.ver_minor)
def get_available_major_versions():
# NOTE(andreykurilin): available clients version should not be
# hardcoded, so let's discover them.
matcher = re.compile(r"v[0-9]*$")
submodules = pkgutil.iter_modules(
[os.path.dirname(os.path.dirname(__file__))])
available_versions = [name[1:] for loader, name, ispkg in submodules
if matcher.search(name)]
return available_versions
def check_major_version(api_version):
"""Checks major part of ``APIVersion`` obj is supported.
:raises watcherclient.exceptions.UnsupportedVersion: if major part is not
supported
"""
available_versions = get_available_major_versions()
if (not api_version.is_null() and
str(api_version.ver_major) not in available_versions):
if len(available_versions) == 1:
msg = _("Invalid client version '%(version)s'. "
"Major part should be '%(major)s'") % {
"version": api_version.get_string(),
"major": available_versions[0]}
else:
msg = _("Invalid client version '%(version)s'. "
"Major part must be one of: '%(major)s'") % {
"version": api_version.get_string(),
"major": ", ".join(available_versions)}
raise exceptions.UnsupportedVersion(msg)
def get_api_version(version_string):
"""Returns checked APIVersion object"""
version_string = str(version_string)
if version_string in DEPRECATED_VERSIONS:
LOG.warning(
_LW("Version %(deprecated_version)s is deprecated, using "
"alternative version %(alternative)s instead."),
{"deprecated_version": version_string,
"alternative": DEPRECATED_VERSIONS[version_string]})
version_string = DEPRECATED_VERSIONS[version_string]
if strutils.is_int_like(version_string):
version_string = "%s.0" % version_string
api_version = APIVersion(version_string)
check_major_version(api_version)
return api_version

View File

@@ -317,7 +317,7 @@ class CrudManager(BaseManager):
def _filter_kwargs(self, kwargs):
"""Drop null values and handle ids."""
for key, ref in six.iteritems(kwargs.copy()):
for key, ref in kwargs.copy().items():
if ref is None:
kwargs.pop(key)
else:
@@ -475,7 +475,7 @@ class Resource(object):
return None
def _add_details(self, info):
for (k, v) in six.iteritems(info):
for (k, v) in info.items():
try:
setattr(self, k, v)
self._info[k] = v
@@ -497,7 +497,7 @@ class Resource(object):
def get(self):
"""Support for lazy loading details.
Some clients, such as novaclient have the option to lazy load the
Some clients, such as watcherclient have the option to lazy load the
details, details which can be loaded with this function.
"""
# set_loaded() first ... so if we have to bail, we know we tried.
@@ -521,6 +521,9 @@ class Resource(object):
return self.id == other.id
return self._info == other._info
def __ne__(self, other):
return not self.__eq__(other)
def is_loaded(self):
return self._loaded

View File

@@ -421,7 +421,7 @@ class HttpVersionNotSupported(HttpServerError):
# _code_map contains all the classes that have http_status attribute.
_code_map = dict(
(getattr(obj, 'http_status', None), obj)
for name, obj in six.iteritems(vars(sys.modules[__name__]))
for name, obj in vars(sys.modules[__name__]).items()
if inspect.isclass(obj) and getattr(obj, 'http_status', False)
)
@@ -433,11 +433,7 @@ def from_response(response, method, url):
:param method: HTTP method used for request
:param url: URL used for request
"""
req_id = response.headers.get("x-openstack-request-id")
# NOTE(hdd) true for older versions of nova and cinder
if not req_id:
req_id = response.headers.get("x-compute-request-id")
kwargs = {
"http_status": response.status_code,
"response": response,

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.

View File

@@ -198,7 +198,7 @@ def print_dict(dct, dict_property="Property", wrap=0):
"""
pt = prettytable.PrettyTable([dict_property, 'Value'])
pt.align = 'l'
for k, v in six.iteritems(dct):
for k, v in dct.items():
# convert dict to str to check length
if isinstance(v, dict):
v = six.text_type(v)

View File

@@ -43,4 +43,8 @@ class Lister(Command, lister.Lister):
class ShowOne(Command, show.ShowOne):
pass
def get_parser(self, prog_name, formatter_class=None):
parser = super(ShowOne, self).get_parser(prog_name)
if formatter_class:
parser.formatter_class = formatter_class
return parser

View File

@@ -1,388 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
# 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 copy
import json
import logging
import os
import socket
import ssl
from keystoneclient import adapter
import six
import six.moves.urllib.parse as urlparse
from watcherclient._i18n import _, _LW, _LE
from watcherclient import exceptions as exc
LOG = logging.getLogger(__name__)
USER_AGENT = 'python-watcherclient'
CHUNKSIZE = 1024 * 64 # 64kB
API_VERSION = '/v1'
def _trim_endpoint_api_version(url):
"""Trim API version and trailing slash from endpoint."""
return url.rstrip('/').rstrip(API_VERSION)
def _extract_error_json(body):
"""Return error_message from the HTTP response body."""
error_json = {}
if six.PY3 and not isinstance(body, six.string_types):
body = body.decode("utf-8")
try:
body_json = json.loads(body)
if 'error_message' in body_json:
raw_msg = body_json['error_message']
error_json = json.loads(raw_msg)
except ValueError:
pass
return error_json
class HTTPClient(object):
def __init__(self, endpoint, **kwargs):
self.endpoint = endpoint
self.endpoint_trimmed = _trim_endpoint_api_version(endpoint)
self.auth_token = kwargs.get('token')
self.auth_ref = kwargs.get('auth_ref')
self.connection_params = self.get_connection_params(endpoint, **kwargs)
@staticmethod
def get_connection_params(endpoint, **kwargs):
parts = urlparse.urlparse(endpoint)
path = _trim_endpoint_api_version(parts.path)
_args = (parts.hostname, parts.port, path)
_kwargs = {'timeout': (float(kwargs.get('timeout'))
if kwargs.get('timeout') else 600)}
if parts.scheme == 'https':
_class = VerifiedHTTPSConnection
_kwargs['ca_file'] = kwargs.get('ca_file', None)
_kwargs['cert_file'] = kwargs.get('cert_file', None)
_kwargs['key_file'] = kwargs.get('key_file', None)
_kwargs['insecure'] = kwargs.get('insecure', False)
elif parts.scheme == 'http':
_class = six.moves.http_client.HTTPConnection
else:
raise exc.EndpointException(
_('Unsupported scheme: %s'), parts.scheme)
return (_class, _args, _kwargs)
def get_connection(self):
_class = self.connection_params[0]
try:
return _class(*self.connection_params[1][0:2],
**self.connection_params[2])
except six.moves.http_client.InvalidURL:
raise exc.EndpointException()
def log_curl_request(self, method, url, kwargs):
curl = ['curl -i -X %s' % method]
for (key, value) in kwargs['headers'].items():
header = '-H \'%s: %s\'' % (key, value)
curl.append(header)
conn_params_fmt = [
('key_file', '--key %s'),
('cert_file', '--cert %s'),
('ca_file', '--cacert %s'),
]
for (key, fmt) in conn_params_fmt:
value = self.connection_params[2].get(key)
if value:
curl.append(fmt % value)
if self.connection_params[2].get('insecure'):
curl.append('-k')
if 'body' in kwargs:
curl.append('-d \'%s\'' % kwargs['body'])
curl.append(urlparse.urljoin(self.endpoint_trimmed, url))
LOG.debug(' '.join(curl))
@staticmethod
def log_http_response(resp, body=None):
status = (resp.version / 10.0, resp.status, resp.reason)
dump = ['\nHTTP/%.1f %s %s' % status]
dump.extend(['%s: %s' % (k, v) for k, v in resp.getheaders()])
dump.append('')
if body:
dump.extend([body, ''])
LOG.debug('\n'.join(dump))
def _make_connection_url(self, url):
(_class, _args, _kwargs) = self.connection_params
base_url = _args[2]
return '%s/%s' % (base_url, url.lstrip('/'))
def _http_request(self, url, method, **kwargs):
"""Send an http request with the specified characteristics.
Wrapper around httplib.HTTP(S)Connection.request to handle tasks such
as setting headers and error handling.
"""
# Copy the kwargs so we can reuse the original in case of redirects
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
kwargs['headers'].setdefault('User-Agent', USER_AGENT)
if self.auth_token:
kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
self.log_curl_request(method, url, kwargs)
conn = self.get_connection()
try:
conn_url = self._make_connection_url(url)
conn.request(method, conn_url, **kwargs)
resp = conn.getresponse()
except socket.gaierror as e:
raise exc.EndpointNotFound(
_("Error finding address for %(url)s: %(e)s"),
url=url, e=e)
except (socket.error, socket.timeout) as e:
endpoint = self.endpoint
raise exc.CommunicationError(
_("Error communicating with %(endpoint)s %(e)s"),
endpoint=endpoint, e=e)
body_iter = ResponseBodyIterator(resp)
# Read body into string if it isn't obviously image data
body_str = None
if resp.getheader('content-type', None) != 'application/octet-stream':
body_str = ''.join([chunk for chunk in body_iter])
self.log_http_response(resp, body_str)
body_iter = six.StringIO(body_str)
else:
self.log_http_response(resp)
if 400 <= resp.status < 600:
LOG.warning(_LW("Request returned failure status."))
error_json = _extract_error_json(body_str)
raise exc.from_response(
resp, error_json.get('faultstring'),
error_json.get('debuginfo'), method, url)
elif resp.status in (301, 302, 305):
# Redirected. Reissue the request to the new location.
return self._http_request(resp['location'], method, **kwargs)
elif resp.status == 300:
raise exc.from_response(resp, method=method, url=url)
return resp, body_iter
def json_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type', 'application/json')
kwargs['headers'].setdefault('Accept', 'application/json')
if 'body' in kwargs:
kwargs['body'] = json.dumps(kwargs['body'])
resp, body_iter = self._http_request(url, method, **kwargs)
content_type = resp.getheader('content-type', None)
if resp.status == 204 or resp.status == 205 or content_type is None:
return resp, list()
if 'application/json' in content_type:
body = ''.join([chunk for chunk in body_iter])
try:
body = json.loads(body)
except ValueError:
LOG.error(_LE('Could not decode response body as JSON'))
else:
body = None
return resp, body
def raw_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type',
'application/octet-stream')
return self._http_request(url, method, **kwargs)
class VerifiedHTTPSConnection(six.moves.http_client.HTTPSConnection):
"""httplib-compatibile connection using client-side SSL authentication
:see http://code.activestate.com/recipes/
577548-https-httplib-client-connection-with-certificate-v/
"""
def __init__(self, host, port, key_file=None, cert_file=None,
ca_file=None, timeout=None, insecure=False):
six.moves.http_client.HTTPSConnection.__init__(
self, host, port,
key_file=key_file,
cert_file=cert_file)
self.key_file = key_file
self.cert_file = cert_file
if ca_file is not None:
self.ca_file = ca_file
else:
self.ca_file = self.get_system_ca_file()
self.timeout = timeout
self.insecure = insecure
def connect(self):
"""Connect to a host on a given (SSL) port.
If ca_file is pointing somewhere, use it to check Server Certificate.
Redefined/copied and extended from httplib.py:1105 (Python 2.6.x).
This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to
ssl.wrap_socket(), which forces SSL to check server certificate against
our client certificate.
"""
sock = socket.create_connection((self.host, self.port), self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
if self.insecure is True:
kwargs = {'cert_reqs': ssl.CERT_NONE}
else:
kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.ca_file}
if self.cert_file:
kwargs['certfile'] = self.cert_file
if self.key_file:
kwargs['keyfile'] = self.key_file
self.sock = ssl.wrap_socket(sock, **kwargs)
@staticmethod
def get_system_ca_file():
"""Return path to system default CA file."""
# Standard CA file locations for Debian/Ubuntu, RedHat/Fedora,
# Suse, FreeBSD/OpenBSD
ca_path = ['/etc/ssl/certs/ca-certificates.crt',
'/etc/pki/tls/certs/ca-bundle.crt',
'/etc/ssl/ca-bundle.pem',
'/etc/ssl/cert.pem']
for ca in ca_path:
if os.path.exists(ca):
return ca
return None
class SessionClient(adapter.LegacyJsonAdapter):
"""HTTP client based on Keystone client session."""
def _http_request(self, url, method, **kwargs):
kwargs.setdefault('user_agent', USER_AGENT)
kwargs.setdefault('auth', self.auth)
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
endpoint_filter.setdefault('interface', self.interface)
endpoint_filter.setdefault('service_type', self.service_type)
endpoint_filter.setdefault('region_name', self.region_name)
resp = self.session.request(url, method,
raise_exc=False, **kwargs)
if 400 <= resp.status_code < 600:
error_json = _extract_error_json(resp.content)
raise exc.from_response(resp, error_json.get(
'faultstring'),
error_json.get('debuginfo'), method, url)
elif resp.status_code in (301, 302, 305):
# Redirected. Reissue the request to the new location.
location = resp.headers.get('location')
resp = self._http_request(location, method, **kwargs)
elif resp.status_code == 300:
raise exc.from_response(resp, method=method, url=url)
return resp
def json_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type', 'application/json')
kwargs['headers'].setdefault('Accept', 'application/json')
if 'body' in kwargs:
kwargs['data'] = json.dumps(kwargs.pop('body'))
resp = self._http_request(url, method, **kwargs)
body = resp.content
content_type = resp.headers.get('content-type', None)
status = resp.status_code
if status == 204 or status == 205 or content_type is None:
return resp, list()
if 'application/json' in content_type:
try:
body = resp.json()
except ValueError:
LOG.error(_LE('Could not decode response body as JSON'))
else:
body = None
return resp, body
def raw_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type',
'application/octet-stream')
return self._http_request(url, method, **kwargs)
class ResponseBodyIterator(object):
"""A class that acts as an iterator over an HTTP response."""
def __init__(self, resp):
self.resp = resp
def __iter__(self):
while True:
yield self.next()
def next(self):
chunk = self.resp.read(CHUNKSIZE)
if chunk:
if six.PY3 and not isinstance(chunk, six.string_types):
chunk = chunk.decode("utf-8")
return chunk
else:
raise StopIteration()
def _construct_http_client(*args, **kwargs):
session = kwargs.pop('session', None)
auth = kwargs.pop('auth', None)
if session:
service_type = kwargs.pop('service_type', 'infra-optim')
interface = kwargs.pop('endpoint_type', None)
region_name = kwargs.pop('region_name', None)
return SessionClient(session=session,
auth=auth,
interface=interface,
service_type=service_type,
region_name=region_name,
service_name=None,
user_agent='python-watcherclient')
else:
return HTTPClient(*args, **kwargs)

View File

@@ -0,0 +1,632 @@
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
# 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 copy
from distutils import version
import functools
import hashlib
import json
import logging
import os
import socket
import ssl
import textwrap
import time
from keystoneauth1 import adapter
from keystoneauth1 import exceptions as kexceptions
from oslo_utils import strutils
import requests
import six
from six.moves import http_client
import six.moves.urllib.parse as urlparse
from watcherclient._i18n import _, _LE, _LW
from watcherclient import exceptions
# NOTE(deva): Record the latest version that this client was tested with.
# We still have a lot of work to do in the client to implement
# microversion support in the client properly! See
# http://specs.openstack.org/openstack/watcher-specs/specs/kilo/api-microversions.html # noqa
# for full details.
DEFAULT_VER = '1.0'
LOG = logging.getLogger(__name__)
USER_AGENT = 'python-watcherclient'
CHUNKSIZE = 1024 * 64 # 64kB
API_VERSION = '/v1'
API_VERSION_SELECTED_STATES = ('user', 'negotiated', 'cached', 'default')
DEFAULT_MAX_RETRIES = 5
DEFAULT_RETRY_INTERVAL = 2
SENSITIVE_HEADERS = ('X-Auth-Token',)
SUPPORTED_ENDPOINT_SCHEME = ('http', 'https')
def _trim_endpoint_api_version(url):
"""Trim API version and trailing slash from endpoint."""
return url.rstrip('/').rstrip(API_VERSION)
def _extract_error_json(body):
"""Return error_message from the HTTP response body."""
error_json = {}
try:
body_json = json.loads(body)
if 'error_message' in body_json:
raw_msg = body_json['error_message']
error_json = json.loads(raw_msg)
except ValueError:
pass
return error_json
def get_server(endpoint):
"""Extract and return the server & port that we're connecting to."""
if endpoint is None:
return None, None
parts = urlparse.urlparse(endpoint)
return parts.hostname, str(parts.port)
class VersionNegotiationMixin(object):
def negotiate_version(self, conn, resp):
"""Negotiate the server version
Assumption: Called after receiving a 406 error when doing a request.
param conn: A connection object
param resp: The response object from http request
"""
if self.api_version_select_state not in API_VERSION_SELECTED_STATES:
raise RuntimeError(
_('Error: self.api_version_select_state should be one of the '
'values in: "%(valid)s" but had the value: "%(value)s"') %
{'valid': ', '.join(API_VERSION_SELECTED_STATES),
'value': self.api_version_select_state})
min_ver, max_ver = self._parse_version_headers(resp)
# NOTE: servers before commit 32fb6e99 did not return version headers
# on error, so we need to perform a GET to determine
# the supported version range
if not max_ver:
LOG.debug('No version header in response, requesting from server')
if self.os_watcher_api_version:
base_version = ("/v%s" %
str(self.os_watcher_api_version).split('.')[0])
else:
base_version = API_VERSION
resp = self._make_simple_request(conn, 'GET', base_version)
min_ver, max_ver = self._parse_version_headers(resp)
# If the user requested an explicit version or we have negotiated a
# version and still failing then error now. The server could
# support the version requested but the requested operation may not
# be supported by the requested version.
if self.api_version_select_state == 'user':
raise exceptions.UnsupportedVersion(textwrap.fill(
_("Requested API version %(req)s is not supported by the "
"server or the requested operation is not supported by the "
"requested version. Supported version range is %(min)s to "
"%(max)s")
% {'req': self.os_watcher_api_version,
'min': min_ver, 'max': max_ver}))
if self.api_version_select_state == 'negotiated':
raise exceptions.UnsupportedVersion(textwrap.fill(
_("No API version was specified and the requested operation "
"was not supported by the client's negotiated API version "
"%(req)s. Supported version range is: %(min)s to %(max)s")
% {'req': self.os_watcher_api_version,
'min': min_ver, 'max': max_ver}))
negotiated_ver = str(
min(version.StrictVersion(self.os_watcher_api_version),
version.StrictVersion(max_ver)))
if negotiated_ver < min_ver:
negotiated_ver = min_ver
# server handles microversions, but doesn't support
# the requested version, so try a negotiated version
self.api_version_select_state = 'negotiated'
self.os_watcher_api_version = negotiated_ver
LOG.debug('Negotiated API version is %s', negotiated_ver)
return negotiated_ver
def _generic_parse_version_headers(self, accessor_func):
min_ver = accessor_func('X-OpenStack-Watcher-API-Minimum-Version',
None)
max_ver = accessor_func('X-OpenStack-Watcher-API-Maximum-Version',
None)
return min_ver, max_ver
def _parse_version_headers(self, accessor_func):
# NOTE(jlvillal): Declared for unit testing purposes
raise NotImplementedError()
def _make_simple_request(self, conn, method, url):
# NOTE(jlvillal): Declared for unit testing purposes
raise NotImplementedError()
_RETRY_EXCEPTIONS = (exceptions.Conflict,
exceptions.ServiceUnavailable,
exceptions.ConnectionRefused,
kexceptions.RetriableConnectionFailure)
def with_retries(func):
"""Wrapper for _http_request adding support for retries."""
@functools.wraps(func)
def wrapper(self, url, method, **kwargs):
if self.conflict_max_retries is None:
self.conflict_max_retries = DEFAULT_MAX_RETRIES
if self.conflict_retry_interval is None:
self.conflict_retry_interval = DEFAULT_RETRY_INTERVAL
num_attempts = self.conflict_max_retries + 1
for attempt in range(1, num_attempts + 1):
try:
return func(self, url, method, **kwargs)
except _RETRY_EXCEPTIONS as error:
msg = (_LE("Error contacting Watcher server: %(error)s. "
"Attempt %(attempt)d of %(total)d") %
{'attempt': attempt,
'total': num_attempts,
'error': error})
if attempt == num_attempts:
LOG.error(msg)
raise
else:
LOG.debug(msg)
time.sleep(self.conflict_retry_interval)
return wrapper
class HTTPClient(VersionNegotiationMixin):
def __init__(self, endpoint, **kwargs):
self.endpoint = endpoint
self.endpoint_trimmed = _trim_endpoint_api_version(endpoint)
self.auth_token = kwargs.get('token')
self.auth_ref = kwargs.get('auth_ref')
self.os_watcher_api_version = kwargs.get('os_watcher_api_version',
DEFAULT_VER)
self.api_version_select_state = kwargs.get(
'api_version_select_state', 'default')
self.conflict_max_retries = kwargs.pop('max_retries',
DEFAULT_MAX_RETRIES)
self.conflict_retry_interval = kwargs.pop('retry_interval',
DEFAULT_RETRY_INTERVAL)
self.session = requests.Session()
parts = urlparse.urlparse(endpoint)
if parts.scheme not in SUPPORTED_ENDPOINT_SCHEME:
msg = _('Unsupported scheme: %s') % parts.scheme
raise exceptions.EndpointException(msg)
if parts.scheme == 'https':
if kwargs.get('insecure') is True:
self.session.verify = False
elif kwargs.get('ca_file'):
self.session.verify = kwargs['ca_file']
self.session.cert = (kwargs.get('cert_file'),
kwargs.get('key_file'))
def _process_header(self, name, value):
"""Redacts any sensitive header
Redact a header that contains sensitive information, by returning an
updated header with the sha1 hash of that value. The redacted value is
prefixed by '{SHA1}' because that's the convention used within
OpenStack.
:returns: A tuple of (name, value)
name: the safe encoding format of name
value: the redacted value if name is x-auth-token,
or the safe encoding format of name
"""
if name in SENSITIVE_HEADERS:
v = value.encode('utf-8')
h = hashlib.sha1(v)
d = h.hexdigest()
return (name, "{SHA1}%s" % d)
else:
return (name, value)
def log_curl_request(self, method, url, kwargs):
curl = ['curl -i -X %s' % method]
for (key, value) in kwargs['headers'].items():
header = '-H \'%s: %s\'' % self._process_header(key, value)
curl.append(header)
if not self.session.verify:
curl.append('-k')
elif isinstance(self.session.verify, six.string_types):
curl.append('--cacert %s' % self.session.verify)
if self.session.cert:
curl.append('--cert %s' % self.session.cert[0])
curl.append('--key %s' % self.session.cert[1])
if 'body' in kwargs:
body = strutils.mask_password(kwargs['body'])
curl.append('-d \'%s\'' % body)
curl.append(urlparse.urljoin(self.endpoint_trimmed, url))
LOG.debug(' '.join(curl))
@staticmethod
def log_http_response(resp, body=None):
# NOTE(aarefiev): resp.raw is urllib3 response object, it's used
# only to get 'version', response from request with 'stream = True'
# should be used for raw reading.
status = (resp.raw.version / 10.0, resp.status_code, resp.reason)
dump = ['\nHTTP/%.1f %s %s' % status]
dump.extend(['%s: %s' % (k, v) for k, v in resp.headers.items()])
dump.append('')
if body:
body = strutils.mask_password(body)
dump.extend([body, ''])
LOG.debug('\n'.join(dump))
def _make_connection_url(self, url):
return urlparse.urljoin(self.endpoint_trimmed, url)
def _parse_version_headers(self, resp):
return self._generic_parse_version_headers(resp.headers.get)
def _make_simple_request(self, conn, method, url):
return conn.request(method, self._make_connection_url(url))
@with_retries
def _http_request(self, url, method, **kwargs):
"""Send an http request with the specified characteristics.
Wrapper around request.Session.request to handle tasks such
as setting headers and error handling.
"""
# Copy the kwargs so we can reuse the original in case of redirects
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
kwargs['headers'].setdefault('User-Agent', USER_AGENT)
if self.os_watcher_api_version:
kwargs['headers'].setdefault('X-OpenStack-Watcher-API-Version',
self.os_watcher_api_version)
if self.auth_token:
kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
self.log_curl_request(method, url, kwargs)
# NOTE(aarefiev): This is for backwards compatibility, request
# expected body in 'data' field, previously we used httplib,
# which expected 'body' field.
body = kwargs.pop('body', None)
if body:
kwargs['data'] = body
conn_url = self._make_connection_url(url)
try:
resp = self.session.request(method,
conn_url,
**kwargs)
# TODO(deva): implement graceful client downgrade when connecting
# to servers that did not support microversions. Details here:
# http://specs.openstack.org/openstack/watcher-specs/specs/kilo/api-microversions.html#use-case-3b-new-client-communicating-with-a-old-watcher-user-specified # noqa
if resp.status_code == http_client.NOT_ACCEPTABLE:
negotiated_ver = self.negotiate_version(self.session, resp)
kwargs['headers']['X-OpenStack-Watcher-API-Version'] = (
negotiated_ver)
return self._http_request(url, method, **kwargs)
except requests.exceptions.RequestException as e:
message = (_("Error has occurred while handling "
"request for %(url)s: %(e)s") %
dict(url=conn_url, e=e))
# NOTE(aarefiev): not valid request(invalid url, missing schema,
# and so on), retrying is not needed.
if isinstance(e, ValueError):
raise exceptions.ValidationError(message)
raise exceptions.ConnectionRefused(message)
body_iter = resp.iter_content(chunk_size=CHUNKSIZE)
# Read body into string if it isn't obviously image data
body_str = None
if resp.headers.get('Content-Type') != 'application/octet-stream':
body_str = ''.join([chunk for chunk in body_iter])
self.log_http_response(resp, body_str)
body_iter = six.StringIO(body_str)
else:
self.log_http_response(resp)
if resp.status_code >= http_client.BAD_REQUEST:
error_json = _extract_error_json(body_str)
raise exceptions.from_response(
resp, error_json.get('faultstring'),
error_json.get('debuginfo'), method, url)
elif resp.status_code in (http_client.MOVED_PERMANENTLY,
http_client.FOUND,
http_client.USE_PROXY):
# Redirected. Reissue the request to the new location.
return self._http_request(resp['location'], method, **kwargs)
elif resp.status_code == http_client.MULTIPLE_CHOICES:
raise exceptions.from_response(resp, method=method, url=url)
return resp, body_iter
def json_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type', 'application/json')
kwargs['headers'].setdefault('Accept', 'application/json')
if 'body' in kwargs:
kwargs['body'] = json.dumps(kwargs['body'])
resp, body_iter = self._http_request(url, method, **kwargs)
content_type = resp.headers.get('Content-Type')
if (resp.status_code in (http_client.NO_CONTENT,
http_client.RESET_CONTENT)
or content_type is None):
return resp, list()
if 'application/json' in content_type:
body = ''.join([chunk for chunk in body_iter])
try:
body = json.loads(body)
except ValueError:
LOG.error(_LE('Could not decode response body as JSON'))
else:
body = None
return resp, body
def raw_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type',
'application/octet-stream')
return self._http_request(url, method, **kwargs)
class VerifiedHTTPSConnection(six.moves.http_client.HTTPSConnection):
"""httplib-compatible connection using client-side SSL authentication
:see http://code.activestate.com/recipes/
577548-https-httplib-client-connection-with-certificate-v/
"""
def __init__(self, host, port, key_file=None, cert_file=None,
ca_file=None, timeout=None, insecure=False):
six.moves.http_client.HTTPSConnection.__init__(self, host, port,
key_file=key_file,
cert_file=cert_file)
self.key_file = key_file
self.cert_file = cert_file
if ca_file is not None:
self.ca_file = ca_file
else:
self.ca_file = self.get_system_ca_file()
self.timeout = timeout
self.insecure = insecure
def connect(self):
"""Connect to a host on a given (SSL) port.
If ca_file is pointing somewhere, use it to check Server Certificate.
Redefined/copied and extended from httplib.py:1105 (Python 2.6.x).
This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to
ssl.wrap_socket(), which forces SSL to check server certificate against
our client certificate.
"""
sock = socket.create_connection((self.host, self.port), self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
if self.insecure is True:
kwargs = {'cert_reqs': ssl.CERT_NONE}
else:
kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.ca_file}
if self.cert_file:
kwargs['certfile'] = self.cert_file
if self.key_file:
kwargs['keyfile'] = self.key_file
self.sock = ssl.wrap_socket(sock, **kwargs)
@staticmethod
def get_system_ca_file():
"""Return path to system default CA file."""
# Standard CA file locations for Debian/Ubuntu, RedHat/Fedora,
# Suse, FreeBSD/OpenBSD
ca_path = ['/etc/ssl/certs/ca-certificates.crt',
'/etc/pki/tls/certs/ca-bundle.crt',
'/etc/ssl/ca-bundle.pem',
'/etc/ssl/cert.pem']
for ca in ca_path:
if os.path.exists(ca):
return ca
return None
class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter):
"""HTTP client based on Keystone client session."""
def __init__(self,
os_watcher_api_version,
api_version_select_state,
max_retries,
retry_interval,
endpoint,
**kwargs):
self.os_watcher_api_version = os_watcher_api_version
self.api_version_select_state = api_version_select_state
self.conflict_max_retries = max_retries
self.conflict_retry_interval = retry_interval
self.endpoint = endpoint
super(SessionClient, self).__init__(**kwargs)
def _parse_version_headers(self, resp):
return self._generic_parse_version_headers(resp.headers.get)
def _make_simple_request(self, conn, method, url):
# NOTE: conn is self.session for this class
return conn.request(url, method, raise_exc=False)
@with_retries
def _http_request(self, url, method, **kwargs):
kwargs.setdefault('user_agent', USER_AGENT)
kwargs.setdefault('auth', self.auth)
if isinstance(self.endpoint_override, six.string_types):
kwargs.setdefault(
'endpoint_override',
_trim_endpoint_api_version(self.endpoint_override)
)
if getattr(self, 'os_watcher_api_version', None):
kwargs['headers'].setdefault('X-OpenStack-Watcher-API-Version',
self.os_watcher_api_version)
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
endpoint_filter.setdefault('interface', self.interface)
endpoint_filter.setdefault('service_type', self.service_type)
endpoint_filter.setdefault('region_name', self.region_name)
resp = self.session.request(url, method,
raise_exc=False, **kwargs)
if resp.status_code == http_client.NOT_ACCEPTABLE:
negotiated_ver = self.negotiate_version(self.session, resp)
kwargs['headers']['X-OpenStack-Watcher-API-Version'] = (
negotiated_ver)
return self._http_request(url, method, **kwargs)
if resp.status_code >= http_client.BAD_REQUEST:
error_json = _extract_error_json(resp.content)
raise exceptions.from_response(
resp, error_json.get('faultstring'),
error_json.get('debuginfo'), method, url)
elif resp.status_code in (http_client.MOVED_PERMANENTLY,
http_client.FOUND, http_client.USE_PROXY):
# Redirected. Reissue the request to the new location.
location = resp.headers.get('location')
resp = self._http_request(location, method, **kwargs)
elif resp.status_code == http_client.MULTIPLE_CHOICES:
raise exceptions.from_response(resp, method=method, url=url)
return resp
def json_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type', 'application/json')
kwargs['headers'].setdefault('Accept', 'application/json')
if 'body' in kwargs:
kwargs['data'] = json.dumps(kwargs.pop('body'))
resp = self._http_request(url, method, **kwargs)
body = resp.content
content_type = resp.headers.get('content-type', None)
status = resp.status_code
if (status in (http_client.NO_CONTENT, http_client.RESET_CONTENT) or
content_type is None):
return resp, list()
if 'application/json' in content_type:
try:
body = resp.json()
except ValueError:
LOG.error(_LE('Could not decode response body as JSON'))
else:
body = None
return resp, body
def raw_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type',
'application/octet-stream')
return self._http_request(url, method, **kwargs)
def _construct_http_client(endpoint=None,
session=None,
token=None,
auth_ref=None,
os_watcher_api_version=DEFAULT_VER,
api_version_select_state='default',
max_retries=DEFAULT_MAX_RETRIES,
retry_interval=DEFAULT_RETRY_INTERVAL,
timeout=600,
ca_file=None,
cert_file=None,
key_file=None,
insecure=None,
**kwargs):
if session:
kwargs.setdefault('service_type', 'infra-optim')
kwargs.setdefault('user_agent', 'python-watcherclient')
kwargs.setdefault('interface', kwargs.pop('endpoint_type', None))
kwargs.setdefault('endpoint_override', endpoint)
ignored = {'token': token,
'auth_ref': auth_ref,
'timeout': timeout != 600,
'ca_file': ca_file,
'cert_file': cert_file,
'key_file': key_file,
'insecure': insecure}
dvars = [k for k, v in ignored.items() if v]
if dvars:
LOG.warning(_LW('The following arguments are ignored when using '
'the session to construct a client: %s'),
', '.join(dvars))
return SessionClient(session=session,
os_watcher_api_version=os_watcher_api_version,
api_version_select_state=api_version_select_state,
max_retries=max_retries,
retry_interval=retry_interval,
endpoint=endpoint,
**kwargs)
else:
if kwargs:
LOG.warning(_LW('The following arguments are being ignored when '
'constructing the client: %s'), ', '.join(kwargs))
return HTTPClient(endpoint=endpoint,
token=token,
auth_ref=auth_ref,
os_watcher_api_version=os_watcher_api_version,
api_version_select_state=api_version_select_state,
max_retries=max_retries,
retry_interval=retry_interval,
timeout=timeout,
ca_file=ca_file,
cert_file=cert_file,
key_file=key_file,
insecure=insecure)

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
@@ -19,7 +18,9 @@ from __future__ import print_function
import argparse
import json
import os
import uuid
import yaml
from oslo_utils import importutils
@@ -192,3 +193,10 @@ def is_uuid_like(val):
return str(uuid.UUID(val)) == val
except (TypeError, ValueError, AttributeError):
return False
def serialize_file_to_dict(filename):
filename = os.path.expanduser(filename)
with open(filename, "rb") as stream:
scope = yaml.safe_load(stream.read())
return scope

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -53,6 +52,20 @@ A generic error message, given when no more specific message is suitable.
An alias of :py:exc:`watcherclient.common.apiclient.ValidationError`
"""
Conflict = exceptions.Conflict
ConnectionRefused = exceptions.ConnectionRefused
EndpointException = exceptions.EndpointException
EndpointNotFound = exceptions.EndpointNotFound
ServiceUnavailable = exceptions.ServiceUnavailable
class UnsupportedVersion(Exception):
"""Unsupported API Version
Indicates that the user is trying to use an unsupported version of the API.
"""
pass
class AmbiguousAuthSystem(exceptions.ClientException):
"""Could not obtain token and endpoint using provided credentials."""
@@ -86,11 +99,10 @@ def from_response(response, message=None, traceback=None, method=None,
'Content-Type': response.getheader('content-type', "")}
if hasattr(response, 'status_code'):
# NOTE(jiangfei): These modifications allow SessionClient
# to handle faultstring.
# NOTE(hongbin): This allows SessionClient to handle faultstring.
response.json = lambda: {'error': error_body}
if (response.headers['Content-Type'].startswith('text/') and
if (response.headers.get('Content-Type', '').startswith('text/') and
not hasattr(response, 'text')):
# NOTE(clif_h): There seems to be a case in the
# common.apiclient.exceptions module where if the
@@ -100,4 +112,4 @@ def from_response(response, message=None, traceback=None, method=None,
# This is to work around that problem.
response.text = ''
return exceptions.from_response(response, message, url)
return exceptions.from_response(response, method, url)

View File

@@ -26,32 +26,16 @@ API_VERSIONS = {
def make_client(instance):
"""Returns an infra-optim service client"""
watcher_client = utils.get_client_class(
"""Returns an infra-optim service client."""
infraoptim_client_class = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],
API_VERSIONS)
LOG.debug('Instantiating infra-optim client: %s', watcher_client)
LOG.debug('Instantiating infraoptim client: %s', infraoptim_client_class)
endpoint = instance.get_endpoint_for_service_type(
API_NAME,
region_name=instance._region_name,
interface=instance._interface,
)
auth_url = instance._auth_url \
if hasattr(instance, '_auth_url') else instance.auth.auth_url
username = instance._username \
if hasattr(instance, '_username') else instance.auth._username
password = instance._password \
if hasattr(instance, '_password') else instance.auth._password
client = watcher_client(
endpoint=endpoint,
client = infraoptim_client_class(
os_watcher_api_version=instance._api_version[API_NAME],
session=instance.session,
auth_url=auth_url,
username=username,
password=password,
region_name=instance._region_name,
)

View File

@@ -25,17 +25,11 @@ from cliff import command
from cliff import commandmanager
from cliff import complete
from cliff import help as cli_help
from keystoneclient.auth.identity import v2
from keystoneclient.auth.identity import v3
from keystoneclient import discover
from keystoneclient import exceptions as ks_exc
from keystoneclient import session
from keystoneauth1 import loading
from osc_lib import logs
from osc_lib import utils
import six.moves.urllib.parse as urlparse
from watcherclient._i18n import _
from watcherclient import exceptions as exc
from watcherclient import client as watcherclient
from watcherclient import version
LOG = logging.getLogger(__name__)
@@ -75,145 +69,9 @@ class WatcherShell(app.App):
)
def create_client(self, args):
service_type = 'infra-optim'
project_id = args.os_project_id or args.os_tenant_id
project_name = args.os_project_name or args.os_tenant_name
keystone_session = session.Session.load_from_cli_options(args)
kwargs = {
'username': args.os_username,
'user_domain_id': args.os_user_domain_id,
'user_domain_name': args.os_user_domain_name,
'password': args.os_password,
'auth_token': args.os_auth_token,
'project_id': project_id,
'project_name': project_name,
'project_domain_id': args.os_project_domain_id,
'project_domain_name': args.os_project_domain_name,
}
keystone_auth = self._get_keystone_auth(keystone_session,
args.os_auth_url,
**kwargs)
region_name = args.os_region_name
endpoint = keystone_auth.get_endpoint(keystone_session,
service_type=service_type,
region_name=region_name)
endpoint_type = args.os_endpoint_type or 'publicURL'
kwargs = {
'auth_url': args.os_auth_url,
'session': keystone_session,
'auth': keystone_auth,
'service_type': service_type,
'endpoint_type': endpoint_type,
'region_name': args.os_region_name,
'username': args.os_username,
'password': args.os_password,
}
watcher_client = utils.get_client_class(
API_NAME,
args.watcher_api_version or 1,
API_VERSIONS)
LOG.debug('Instantiating infra-optim client: %s', watcher_client)
client = watcher_client(args.watcher_api_version, endpoint, **kwargs)
client = watcherclient.get_client('1', **args.__dict__)
return client
def _discover_auth_versions(self, session, auth_url):
# discover the API versions the server is supporting base on the
# given URL
v2_auth_url = None
v3_auth_url = None
try:
ks_discover = discover.Discover(session=session, auth_url=auth_url)
v2_auth_url = ks_discover.url_for('2.0')
v3_auth_url = ks_discover.url_for('3.0')
except ks_exc.ClientException:
# Identity service may not support discover API version.
# Let's try to figure out the API version from the original URL.
url_parts = urlparse.urlparse(auth_url)
(scheme, netloc, path, params, query, fragment) = url_parts
path = path.lower()
if path.startswith('/v3'):
v3_auth_url = auth_url
elif path.startswith('/v2'):
v2_auth_url = auth_url
else:
# not enough information to determine the auth version
msg = _('Unable to determine the Keystone version '
'to authenticate with using the given '
'auth_url. Identity service may not support API '
'version discovery. Please provide a versioned '
'auth_url instead. %s') % auth_url
raise exc.CommandError(msg)
return (v2_auth_url, v3_auth_url)
def _get_keystone_v3_auth(self, v3_auth_url, **kwargs):
auth_token = kwargs.pop('auth_token', None)
if auth_token:
return v3.Token(v3_auth_url, auth_token)
else:
return v3.Password(v3_auth_url, **kwargs)
def _get_keystone_v2_auth(self, v2_auth_url, **kwargs):
auth_token = kwargs.pop('auth_token', None)
if auth_token:
return v2.Token(
v2_auth_url,
auth_token,
tenant_id=kwargs.pop('project_id', None),
tenant_name=kwargs.pop('project_name', None))
else:
return v2.Password(
v2_auth_url,
username=kwargs.pop('username', None),
password=kwargs.pop('password', None),
tenant_id=kwargs.pop('project_id', None),
tenant_name=kwargs.pop('project_name', None))
def _get_keystone_auth(self, session, auth_url, **kwargs):
# FIXME(dhu): this code should come from keystoneclient
# discover the supported keystone versions using the given url
(v2_auth_url, v3_auth_url) = self._discover_auth_versions(
session=session,
auth_url=auth_url)
# Determine which authentication plugin to use. First inspect the
# auth_url to see the supported version. If both v3 and v2 are
# supported, then use the highest version if possible.
auth = None
if v3_auth_url and v2_auth_url:
user_domain_name = kwargs.get('user_domain_name', None)
user_domain_id = kwargs.get('user_domain_id', None)
project_domain_name = kwargs.get('project_domain_name', None)
project_domain_id = kwargs.get('project_domain_id', None)
# support both v2 and v3 auth. Use v3 if domain information is
# provided.
if (user_domain_name or user_domain_id or project_domain_name or
project_domain_id):
auth = self._get_keystone_v3_auth(v3_auth_url, **kwargs)
else:
auth = self._get_keystone_v2_auth(v2_auth_url, **kwargs)
elif v3_auth_url:
# support only v3
auth = self._get_keystone_v3_auth(v3_auth_url, **kwargs)
elif v2_auth_url:
# support only v2
auth = self._get_keystone_v2_auth(v2_auth_url, **kwargs)
else:
raise exc.CommandError(
_('Unable to determine the Keystone version '
'to authenticate with using the given '
'auth_url.'))
return auth
def build_option_parser(self, description, version, argparse_kwargs=None):
"""Introduces global arguments for the application.
@@ -291,17 +149,23 @@ class WatcherShell(app.App):
metavar='<auth-token>',
default=utils.env('OS_AUTH_TOKEN'),
help='Defaults to env[OS_AUTH_TOKEN].')
parser.add_argument('--watcher-api-version',
metavar='<watcher-api-version>',
default=utils.env('WATCHER_API_VERSION'),
help='Defaults to env[WATCHER_API_VERSION].')
parser.add_argument('--os-watcher-api-version',
metavar='<os-watcher-api-version>',
default=utils.env('OS_WATCHER_API_VERSION',
default='1'),
help='Defaults to env[OS_WATCHER_API_VERSION].')
parser.add_argument('--os-endpoint-type',
default=utils.env('OS_ENDPOINT_TYPE'),
help='Defaults to env[OS_ENDPOINT_TYPE] or '
'"publicURL"')
parser.add_argument('--os-endpoint-override',
metavar='<endpoint-override>',
default=utils.env('OS_ENDPOINT_OVERRIDE'),
help="Use this API endpoint instead of the "
"Service Catalog.")
parser.epilog = ('See "watcher help COMMAND" for help '
'on a specific command.')
session.Session.register_cli_options(parser)
loading.register_session_argparse_arguments(parser)
return parser
def configure_logging(self):

View File

@@ -0,0 +1,61 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
=======
Testing
=======
.. _functional_tests:
Functional tests
================
The following procedure gets you started with Tempest testing but you can also
refer to the `Tempest documentation`_ for more details.
.. _Tempest documentation: http://docs.openstack.org/developer/tempest/
Tempest installation
--------------------
You need to install virtualenv, create a virtual environment and activate it::
$ pip install virtualenv
$ virtualenv watcher-env
$ source watcher-env/bin/activate
Then, to install Tempest you can issue the following commands::
$ git clone https://github.com/openstack/tempest/
$ pip install tempest/
There should be set environment variables using the OpenStack RC file. If
you don't have RC file yet, create ``admin-openrc`` file and fill it using
the following example::
export OS_PROJECT_DOMAIN_NAME=default
export OS_USER_DOMAIN_NAME=default
export OS_PROJECT_NAME=admin
export OS_USERNAME=admin
export OS_PASSWORD=admin
export OS_AUTH_URL=http://controller:35357/v3
export OS_IDENTITY_API_VERSION=3
export OS_IMAGE_API_VERSION=2
Then, save file and execute ``source admin-openrc`` to set environment
variables.
To run functional tests you need to go to python-watcherclient folder, install
all requirements and execute ``tempest run`` command::
$ pip install -r requirements.txt test-requirements.txt
$ pip install .
$ tempest run --regex watcherclient.tests.functional
You can run specified test(s) by using regular expression::
$ tempest run --regex watcherclient.tests.functional.v1.test_action.ActionTests.test_action_list

View File

@@ -0,0 +1,108 @@
# 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 re
import shlex
import subprocess
import testtools
import six
from tempest.lib.cli import output_parser
from tempest.lib import exceptions
def execute(cmd, fail_ok=False, merge_stderr=False):
"""Executes specified command for the given action."""
cmdlist = shlex.split(cmd)
stdout = subprocess.PIPE
stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr)
result, result_err = proc.communicate()
result = result.decode('utf-8')
if not fail_ok and proc.returncode != 0:
raise exceptions.CommandFailed(proc.returncode, cmd, result,
result_err)
return result
class TestCase(testtools.TestCase):
delimiter_line = re.compile('^\+\-[\+\-]+\-\+$')
@classmethod
def watcher(cls, cmd, fail_ok=False):
"""Executes watcherclient command for the given action."""
return execute('watcher {0}'.format(cmd), fail_ok=fail_ok)
@classmethod
def get_opts(cls, fields, format='value'):
return ' -f {0} {1}'.format(format,
' '.join(['-c ' + it for it in fields]))
@classmethod
def assertOutput(cls, expected, actual):
if expected != actual:
raise Exception('{0} != {1}'.format(expected, actual))
@classmethod
def assertInOutput(cls, expected, actual):
if expected not in actual:
raise Exception('{0} not in {1}'.format(expected, actual))
def assert_table_structure(self, items, field_names):
"""Verify that all items have keys listed in field_names."""
for item in items:
for field in field_names:
self.assertIn(field, item)
def assert_show_fields(self, items, field_names):
"""Verify that all items have keys listed in field_names."""
for item in items:
for key in six.iterkeys(item):
self.assertIn(key, field_names)
def assert_show_structure(self, items, field_names):
"""Verify that all field_names listed in keys of all items."""
if isinstance(items, list):
o = {}
for d in items:
o.update(d)
else:
o = items
item_keys = o.keys()
for field in field_names:
self.assertIn(field, item_keys)
@staticmethod
def parse_show_as_object(raw_output):
"""Return a dict with values parsed from cli output."""
items = TestCase.parse_show(raw_output)
o = {}
for item in items:
o.update(item)
return o
@staticmethod
def parse_show(raw_output):
"""Return list of dicts with item values parsed from cli output."""
items = []
table_ = output_parser.table(raw_output)
for row in table_['values']:
item = {}
item[row[0]] = row[1]
items.append(item)
return items
def parse_listing(self, raw_output):
"""Return list of dicts with basic item parsed from cli output."""
return output_parser.listing(raw_output)

View File

@@ -0,0 +1,72 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 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_utils import uuidutils
from watcherclient.tests.functional.v1 import base
class ActionTests(base.TestCase):
"""Functional tests for action."""
dummy_name = 'dummy'
list_fields = ['UUID', 'Parents', 'State', 'Action Plan', 'Action']
detailed_list_fields = list_fields + ['Created At', 'Updated At',
'Deleted At', 'Parameters']
audit_template_name = 'a' + uuidutils.generate_uuid()
audit_uuid = None
@classmethod
def setUpClass(cls):
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)
cls.audit_uuid = audit_output['UUID']
@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]
raw_output = cls.watcher('actionplan delete %s' % action_plan_uuid)
cls.assertOutput('', raw_output)
# Delete audit
raw_output = cls.watcher('audit delete %s' % cls.audit_uuid)
cls.assertOutput('', raw_output)
# Delete Template
raw_output = cls.watcher(
'audittemplate delete %s' % cls.audit_template_name)
cls.assertOutput('', raw_output)
def test_action_list(self):
raw_output = self.watcher('action list')
self.assert_table_structure([raw_output], self.list_fields)
def test_action_detailed_list(self):
raw_output = self.watcher('action list --detail')
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)
self.assertIn(action_uuid, action)
self.assert_table_structure([action],
self.detailed_list_fields)

View File

@@ -0,0 +1,123 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 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_utils import uuidutils
from watcherclient.tests.functional.v1 import base
class ActionPlanTests(base.TestCase):
"""Functional tests for action plan."""
dummy_name = 'dummy'
list_fields = ['UUID', 'Audit', 'State', 'Updated At', 'Global efficacy']
detailed_list_fields = list_fields + ['Created At', 'Deleted At',
'Strategy', 'Efficacy indicators']
audit_template_name = 'a' + uuidutils.generate_uuid()
audit_uuid = None
@classmethod
def setUpClass(cls):
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)
cls.audit_uuid = audit_output['UUID']
@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]
raw_output = cls.watcher('actionplan delete %s' % action_plan_uuid)
cls.assertOutput('', raw_output)
# Delete audit
raw_output = cls.watcher('audit delete %s' % cls.audit_uuid)
cls.assertOutput('', raw_output)
# Delete Template
raw_output = cls.watcher(
'audittemplate delete %s' % cls.audit_template_name)
cls.assertOutput('', raw_output)
def test_action_plan_list(self):
raw_output = self.watcher('actionplan list')
self.assert_table_structure([raw_output], self.list_fields)
def test_action_plan_detailed_list(self):
raw_output = self.watcher('actionplan list --detail')
self.assert_table_structure([raw_output], self.detailed_list_fields)
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]
actionplan = self.watcher('actionplan show %s' % action_plan_uuid)
self.assertIn(action_plan_uuid, actionplan)
self.assert_table_structure([actionplan],
self.detailed_list_fields)
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]
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)
class ActionPlanActiveTests(base.TestCase):
audit_uuid = None
audit_template_name = 'b' + uuidutils.generate_uuid()
list_fields = ['UUID', 'Audit', 'State', 'Updated At', 'Global efficacy']
detailed_list_fields = list_fields + ['Created At', 'Deleted At',
'Strategy', 'Efficacy indicators']
def _delete_action_plan(self):
output = self.parse_show(
self.watcher('actionplan list --audit %s' % self.audit_uuid))
action_plan_uuid = output[0].keys()[0]
raw_output = self.watcher('actionplan delete %s' % action_plan_uuid)
self.assertOutput('', raw_output)
def _delete_audit(self):
raw_output = self.watcher('audit delete %s' % self.audit_uuid)
self.assertOutput('', raw_output)
def _delete_audit_template(self):
raw_output = self.watcher(
'audittemplate delete %s' % self.audit_template_name)
self.assertOutput('', raw_output)
def _create_audit_template(self):
template_raw_output = self.watcher(
'audittemplate create %s dummy -s dummy'
% self.audit_template_name)
template_output = self.parse_show_as_object(template_raw_output)
return template_output
def test_action_plan_create(self):
template_output = self._create_audit_template()
action_plan = self.watcher(
'actionplan create -a %s' % template_output['Name'])
self.audit_uuid = self.parse_show_as_object(action_plan)['UUID']
self.assert_table_structure([action_plan], self.detailed_list_fields)
self._delete_action_plan()
self._delete_audit()
self._delete_audit_template()

View File

@@ -0,0 +1,114 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 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_utils import uuidutils
from watcherclient.tests.functional.v1 import base
class AuditTests(base.TestCase):
"""Functional tests for audit."""
dummy_name = 'dummy'
list_fields = ['UUID', 'Audit Type', 'State', 'Goal', 'Strategy']
detailed_list_fields = list_fields + ['Created At', 'Updated At',
'Deleted At', 'Parameters',
'Interval', 'Audit Scope']
audit_template_name = 'a' + uuidutils.generate_uuid()
audit_uuid = None
@classmethod
def setUpClass(cls):
raw_output = cls.watcher('audittemplate create %s dummy -s dummy'
% cls.audit_template_name)
template_output = cls.parse_show_as_object(raw_output)
audit_raw_output = cls.watcher(
'audit create -a %s' % template_output['Name'])
audit_output = cls.parse_show_as_object(audit_raw_output)
cls.audit_uuid = audit_output['UUID']
@classmethod
def tearDownClass(cls):
output = cls.parse_show(
cls.watcher('actionplan list --audit %s' % cls.audit_uuid))
action_plan_uuid = output[0].keys()[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)
def test_audit_list(self):
raw_output = self.watcher('audit list')
self.assert_table_structure([raw_output], self.list_fields)
def test_audit_detailed_list(self):
raw_output = self.watcher('audit list --detail')
self.assert_table_structure([raw_output], self.detailed_list_fields)
def test_audit_show(self):
audit = self.watcher('audit show ' + self.audit_uuid)
self.assertIn(self.audit_uuid, audit)
self.assert_table_structure([audit], self.detailed_list_fields)
def test_audit_update(self):
audit_raw_output = self.watcher('audit update %s add interval=2'
% self.audit_uuid)
audit_output = self.parse_show_as_object(audit_raw_output)
assert int(audit_output['Interval']) == 2
class AuditActiveTests(base.TestCase):
list_fields = ['UUID', 'Audit Type', 'State', 'Goal', 'Strategy']
detailed_list_fields = list_fields + ['Created At', 'Updated At',
'Deleted At', 'Parameters',
'Interval', 'Audit Scope']
audit_template_name = 'a' + uuidutils.generate_uuid()
audit_uuid = None
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(
self.watcher('audit create -a %s'
% template_output['Name']))['UUID']
def _delete_audit(self):
output = self.parse_show(
self.watcher('actionplan list --audit %s' % self.audit_uuid))
action_plan_uuid = output[0].keys()[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)
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']
self.assert_table_structure([audit], self.detailed_list_fields)
self._delete_audit()
def test_delete_audit(self):
self._create_audit()
raw_output = self.watcher('audit delete %s' % self.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 delete %s' % action_plan_uuid)
self.watcher('audittemplate delete %s' % self.audit_template_name)

View File

@@ -0,0 +1,90 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 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_utils import uuidutils
from watcherclient.tests.functional.v1 import base
class AuditTemplateTests(base.TestCase):
"""Functional tests for audit template."""
dummy_name = 'dummy'
list_fields = ['UUID', 'Name', 'Goal', 'Strategy']
detailed_list_fields = list_fields + ['Created At', 'Updated At',
'Deleted At', 'Description',
'Audit Scope']
audit_template_name = 'a' + uuidutils.generate_uuid()
@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 test_audit_template_list(self):
raw_output = self.watcher('audittemplate list')
self.assert_table_structure([raw_output], self.list_fields)
def test_audit_template_detailed_list(self):
raw_output = self.watcher('audittemplate list --detail')
self.assert_table_structure([raw_output], self.detailed_list_fields)
def test_audit_template_show(self):
audit_template = self.watcher(
'audittemplate show %s' % self.audit_template_name)
self.assertIn(self.audit_template_name, audit_template)
self.assert_table_structure([audit_template],
self.detailed_list_fields)
def test_audit_template_update(self):
raw_output = self.watcher('audittemplate update %s replace '
'description="Updated Desc"'
% self.audit_template_name)
audit_template_output = self.parse_show_as_object(raw_output)
assert audit_template_output['Description'] == 'Updated Desc'
class AuditTemplateActiveTests(base.TestCase):
audit_template_name = 'b' + uuidutils.generate_uuid()
list_fields = ['UUID', 'Name', 'Goal', 'Strategy']
detailed_list_fields = list_fields + ['Created At', 'Updated At',
'Deleted At', 'Description',
'Audit Scope']
def _create_audit_template(self):
self.watcher('audittemplate create %s dummy -s dummy '
'-d "Test Audit Template"' % self.audit_template_name)
def _delete_audit_template(self):
self.watcher('audittemplate delete %s' % self.audit_template_name)
def test_create_audit_template(self):
raw_output = self.watcher('audittemplate create %s dummy '
'-s dummy -d "Test Audit Template"'
% self.audit_template_name)
self.assert_table_structure([raw_output], self.detailed_list_fields)
self._delete_audit_template()
def test_delete_audit_template(self):
self._create_audit_template()
raw_output = self.watcher('audittemplate delete %s'
% self.audit_template_name)
self.assertOutput('', raw_output)

View File

@@ -0,0 +1,42 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 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 watcherclient.tests.functional.v1 import base
class GoalTests(base.TestCase):
"""Functional tests for goal."""
dummy_name = 'dummy'
list_fields = ['UUID', 'Name', 'Display name']
def test_goal_list(self):
raw_output = self.watcher('goal list')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.list_fields)
def test_goal_detailed_list(self):
raw_output = self.watcher('goal list --detail')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure(
[raw_output], self.list_fields + ['Efficacy specification'])
def test_goal_show(self):
raw_output = self.watcher('goal show %s' % self.dummy_name)
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure(
[raw_output], self.list_fields + ['Efficacy specification'])
self.assertNotIn('server_consolidation', raw_output)

View File

@@ -0,0 +1,41 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 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 watcherclient.tests.functional.v1 import base
class ScoringEngineTests(base.TestCase):
"""Functional tests for scoring engine."""
dummy_name = 'dummy_scorer'
list_fields = ['UUID', 'Name', 'Description']
detailed_list_fields = list_fields + ['Metainfo']
def test_scoringengine_list(self):
raw_output = self.watcher('scoringengine list')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.list_fields)
def test_scoringengine_detailed_list(self):
raw_output = self.watcher('scoringengine list --detail')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.detailed_list_fields)
def test_scoringengine_show(self):
raw_output = self.watcher('scoringengine show %s' % self.dummy_name)
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.detailed_list_fields)
self.assertNotIn('dummy_avg_scorer', raw_output)

View File

@@ -0,0 +1,46 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 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 watcherclient.tests.functional.v1 import base
class ServiceTests(base.TestCase):
"""Functional tests for service."""
decision_engine_name = 'watcher-decision-engine'
applier_name = 'watcher-applier'
list_fields = ['ID', 'Name', 'Host', 'Status']
def test_service_list(self):
raw_output = self.watcher('service list')
self.assertIn(self.decision_engine_name, raw_output)
self.assertIn(self.applier_name, raw_output)
self.assert_table_structure([raw_output], self.list_fields)
def test_service_detailed_list(self):
raw_output = self.watcher('service list --detail')
self.assertIn(self.decision_engine_name, raw_output)
self.assertIn(self.applier_name, raw_output)
self.assert_table_structure([raw_output],
self.list_fields + ['Last seen up'])
def test_service_show(self):
raw_output = self.watcher('service show %s'
% self.decision_engine_name)
self.assertIn(self.decision_engine_name, raw_output)
self.assert_table_structure([raw_output],
self.list_fields + ['Last seen up'])
self.assertNotIn(self.applier_name, raw_output)

View File

@@ -0,0 +1,42 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 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 watcherclient.tests.functional.v1 import base
class StrategyTests(base.TestCase):
"""Functional tests for strategy."""
dummy_name = 'dummy'
list_fields = ['UUID', 'Name', 'Display name', 'Goal']
def test_strategy_list(self):
raw_output = self.watcher('strategy list')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.list_fields)
def test_strategy_detailed_list(self):
raw_output = self.watcher('strategy list --detail')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output],
self.list_fields + ['Parameters spec'])
def test_strategy_show(self):
raw_output = self.watcher('strategy show %s' % self.dummy_name)
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output],
self.list_fields + ['Parameters spec'])
self.assertNotIn('basic', raw_output)

View File

@@ -1,142 +0,0 @@
# -*- coding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import fixtures
from watcherclient.client import get_client
from watcherclient import exceptions as exc
from watcherclient.tests import utils
def fake_get_ksclient(**kwargs):
return utils.FakeKeystone('KSCLIENT_AUTH_TOKEN')
class ClientTest(utils.BaseTestCase):
def test_get_client_with_auth_token_watcher_url(self):
self.useFixture(fixtures.MonkeyPatch(
'watcherclient.client._get_ksclient', fake_get_ksclient))
kwargs = {
'watcher_url': 'http://watcher.example.org:6385/',
'os_auth_token': 'USER_AUTH_TOKEN',
}
client = get_client('1', **kwargs)
self.assertEqual('USER_AUTH_TOKEN', client.http_client.auth_token)
self.assertEqual('http://watcher.example.org:6385/',
client.http_client.endpoint)
def test_get_client_no_auth_token(self):
self.useFixture(fixtures.MonkeyPatch(
'watcherclient.client._get_ksclient', fake_get_ksclient))
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': '',
}
client = get_client('1', **kwargs)
self.assertEqual('KSCLIENT_AUTH_TOKEN', client.http_client.auth_token)
self.assertEqual('http://localhost:6385/v1/f14b41234',
client.http_client.endpoint)
self.assertEqual(fake_get_ksclient().auth_ref,
client.http_client.auth_ref)
def test_get_client_with_region_no_auth_token(self):
self.useFixture(fixtures.MonkeyPatch(
'watcherclient.client._get_ksclient', fake_get_ksclient))
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_region_name': 'REGIONONE',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': '',
}
client = get_client('1', **kwargs)
self.assertEqual('KSCLIENT_AUTH_TOKEN', client.http_client.auth_token)
self.assertEqual('http://regionhost:6385/v1/f14b41234',
client.http_client.endpoint)
self.assertEqual(fake_get_ksclient().auth_ref,
client.http_client.auth_ref)
def test_get_client_with_auth_token(self):
self.useFixture(fixtures.MonkeyPatch(
'watcherclient.client._get_ksclient', fake_get_ksclient))
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': 'USER_AUTH_TOKEN',
}
client = get_client('1', **kwargs)
self.assertEqual('USER_AUTH_TOKEN', client.http_client.auth_token)
self.assertEqual('http://localhost:6385/v1/f14b41234',
client.http_client.endpoint)
self.assertEqual(fake_get_ksclient().auth_ref,
client.http_client.auth_ref)
def test_get_client_with_region_name_auth_token(self):
self.useFixture(fixtures.MonkeyPatch(
'watcherclient.client._get_ksclient', fake_get_ksclient))
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_region_name': 'REGIONONE',
'os_auth_token': 'USER_AUTH_TOKEN',
}
client = get_client('1', **kwargs)
self.assertEqual('USER_AUTH_TOKEN', client.http_client.auth_token)
self.assertEqual('http://regionhost:6385/v1/f14b41234',
client.http_client.endpoint)
self.assertEqual(fake_get_ksclient().auth_ref,
client.http_client.auth_ref)
def test_get_client_no_url_and_no_token(self):
self.useFixture(fixtures.MonkeyPatch(
'watcherclient.client._get_ksclient', fake_get_ksclient))
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': '',
'os_auth_token': '',
}
self.assertRaises(exc.AmbiguousAuthSystem, get_client, '1', **kwargs)
# test the alias as well to ensure backwards compatibility
self.assertRaises(exc.AmbigiousAuthSystem, get_client, '1', **kwargs)
def test_ensure_auth_ref_propagated(self):
ksclient = fake_get_ksclient
self.useFixture(fixtures.MonkeyPatch(
'watcherclient.client._get_ksclient', ksclient))
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': '',
}
client = get_client('1', **kwargs)
self.assertEqual(ksclient().auth_ref, client.http_client.auth_ref)

View File

@@ -1,283 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
# 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 json
import six
from watcherclient.common import http
from watcherclient import exceptions as exc
from watcherclient.tests import utils
HTTP_CLASS = six.moves.http_client.HTTPConnection
HTTPS_CLASS = http.VerifiedHTTPSConnection
DEFAULT_TIMEOUT = 600
def _get_error_body(faultstring=None, debuginfo=None):
error_body = {
'faultstring': faultstring,
'debuginfo': debuginfo
}
raw_error_body = json.dumps(error_body)
body = {'error_message': raw_error_body}
raw_body = json.dumps(body)
return raw_body
class HttpClientTest(utils.BaseTestCase):
def test_url_generation_trailing_slash_in_base(self):
client = http.HTTPClient('http://localhost/')
url = client._make_connection_url('/v1/resources')
self.assertEqual('/v1/resources', url)
def test_url_generation_without_trailing_slash_in_base(self):
client = http.HTTPClient('http://localhost')
url = client._make_connection_url('/v1/resources')
self.assertEqual('/v1/resources', url)
def test_url_generation_prefix_slash_in_path(self):
client = http.HTTPClient('http://localhost/')
url = client._make_connection_url('/v1/resources')
self.assertEqual('/v1/resources', url)
def test_url_generation_without_prefix_slash_in_path(self):
client = http.HTTPClient('http://localhost')
url = client._make_connection_url('v1/resources')
self.assertEqual('/v1/resources', url)
def test_server_exception_empty_body(self):
error_body = _get_error_body()
fake_resp = utils.FakeResponse({'content-type': 'application/json'},
six.StringIO(error_body),
version=1,
status=500)
client = http.HTTPClient('http://localhost/')
client.get_connection = (
lambda *a, **kw: utils.FakeConnection(fake_resp))
error = self.assertRaises(exc.InternalServerError,
client.json_request,
'GET', '/v1/resources')
self.assertEqual('Internal Server Error (HTTP 500)', str(error))
def test_server_exception_msg_only(self):
error_msg = 'test error msg'
error_body = _get_error_body(error_msg)
fake_resp = utils.FakeResponse({'content-type': 'application/json'},
six.StringIO(error_body),
version=1,
status=500)
client = http.HTTPClient('http://localhost/')
client.get_connection = (
lambda *a, **kw: utils.FakeConnection(fake_resp))
error = self.assertRaises(exc.InternalServerError,
client.json_request,
'GET', '/v1/resources')
self.assertEqual(error_msg + ' (HTTP 500)', str(error))
def test_server_exception_msg_and_traceback(self):
error_msg = 'another test error'
error_trace = ("\"Traceback (most recent call last):\\n\\n "
"File \\\"/usr/local/lib/python2.7/...")
error_body = _get_error_body(error_msg, error_trace)
fake_resp = utils.FakeResponse({'content-type': 'application/json'},
six.StringIO(error_body),
version=1,
status=500)
client = http.HTTPClient('http://localhost/')
client.get_connection = (
lambda *a, **kw: utils.FakeConnection(fake_resp))
error = self.assertRaises(exc.InternalServerError,
client.json_request,
'GET', '/v1/resources')
self.assertEqual(
'%(error)s (HTTP 500)\n%(trace)s' % {'error': error_msg,
'trace': error_trace},
"%(error)s\n%(details)s" % {'error': str(error),
'details': str(error.details)})
def test_get_connection_params(self):
endpoint = 'http://watcher-host:6385'
expected = (HTTP_CLASS,
('watcher-host', 6385, ''),
{'timeout': DEFAULT_TIMEOUT})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_get_connection_params_with_trailing_slash(self):
endpoint = 'http://watcher-host:6385/'
expected = (HTTP_CLASS,
('watcher-host', 6385, ''),
{'timeout': DEFAULT_TIMEOUT})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_get_connection_params_with_ssl(self):
endpoint = 'https://watcher-host:6385'
expected = (HTTPS_CLASS,
('watcher-host', 6385, ''),
{
'timeout': DEFAULT_TIMEOUT,
'ca_file': None,
'cert_file': None,
'key_file': None,
'insecure': False,
})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_get_connection_params_with_ssl_params(self):
endpoint = 'https://watcher-host:6385'
ssl_args = {
'ca_file': '/path/to/ca_file',
'cert_file': '/path/to/cert_file',
'key_file': '/path/to/key_file',
'insecure': True,
}
expected_kwargs = {'timeout': DEFAULT_TIMEOUT}
expected_kwargs.update(ssl_args)
expected = (HTTPS_CLASS,
('watcher-host', 6385, ''),
expected_kwargs)
params = http.HTTPClient.get_connection_params(endpoint, **ssl_args)
self.assertEqual(expected, params)
def test_get_connection_params_with_timeout(self):
endpoint = 'http://watcher-host:6385'
expected = (HTTP_CLASS,
('watcher-host', 6385, ''),
{'timeout': 300.0})
params = http.HTTPClient.get_connection_params(endpoint, timeout=300)
self.assertEqual(expected, params)
def test_get_connection_params_with_version(self):
endpoint = 'http://watcher-host:6385/v1'
expected = (HTTP_CLASS,
('watcher-host', 6385, ''),
{'timeout': DEFAULT_TIMEOUT})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_get_connection_params_with_version_trailing_slash(self):
endpoint = 'http://watcher-host:6385/v1/'
expected = (HTTP_CLASS,
('watcher-host', 6385, ''),
{'timeout': DEFAULT_TIMEOUT})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_get_connection_params_with_subpath(self):
endpoint = 'http://watcher-host:6385/watcher'
expected = (HTTP_CLASS,
('watcher-host', 6385, '/watcher'),
{'timeout': DEFAULT_TIMEOUT})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_get_connection_params_with_subpath_trailing_slash(self):
endpoint = 'http://watcher-host:6385/watcher/'
expected = (HTTP_CLASS,
('watcher-host', 6385, '/watcher'),
{'timeout': DEFAULT_TIMEOUT})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_get_connection_params_with_subpath_version(self):
endpoint = 'http://watcher-host:6385/watcher/v1'
expected = (HTTP_CLASS,
('watcher-host', 6385, '/watcher'),
{'timeout': DEFAULT_TIMEOUT})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_get_connection_params_with_subpath_version_trailing_slash(self):
endpoint = 'http://watcher-host:6385/watcher/v1/'
expected = (HTTP_CLASS,
('watcher-host', 6385, '/watcher'),
{'timeout': DEFAULT_TIMEOUT})
params = http.HTTPClient.get_connection_params(endpoint)
self.assertEqual(expected, params)
def test_401_unauthorized_exception(self):
error_body = _get_error_body()
fake_resp = utils.FakeResponse({'content-type': 'text/plain'},
six.StringIO(error_body),
version=1,
status=401)
client = http.HTTPClient('http://localhost/')
client.get_connection = (
lambda *a, **kw: utils.FakeConnection(fake_resp))
self.assertRaises(exc.Unauthorized, client.json_request,
'GET', '/v1/resources')
class SessionClientTest(utils.BaseTestCase):
def test_server_exception_msg_and_traceback(self):
error_msg = 'another test error'
error_trace = ("\"Traceback (most recent call last):\\n\\n "
"File \\\"/usr/local/lib/python2.7/...")
error_body = _get_error_body(error_msg, error_trace)
fake_session = utils.FakeSession({'Content-Type': 'application/json'},
error_body,
500)
client = http.SessionClient(session=fake_session,
auth=None,
interface=None,
service_type='publicURL',
region_name='',
service_name=None)
error = self.assertRaises(exc.InternalServerError,
client.json_request,
'GET', '/v1/resources')
self.assertEqual(
'%(error)s (HTTP 500)\n%(trace)s' % {'error': error_msg,
'trace': error_trace},
"%(error)s\n%(details)s" % {'error': str(error),
'details': str(error.details)})
def test_server_exception_empty_body(self):
error_body = _get_error_body()
fake_session = utils.FakeSession({'Content-Type': 'application/json'},
error_body,
500)
client = http.SessionClient(session=fake_session,
auth=None,
interface=None,
service_type='publicURL',
region_name='',
service_name=None)
error = self.assertRaises(exc.InternalServerError,
client.json_request,
'GET', '/v1/resources')
self.assertEqual('Internal Server Error (HTTP 500)', str(error))

View File

View File

@@ -0,0 +1,150 @@
# Copyright 2016 Mirantis
# All Rights Reserved.
#
# 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 mock
from watcherclient.common import api_versioning
from watcherclient import exceptions
from watcherclient.tests.unit import utils
class APIVersionTestCase(utils.BaseTestCase):
def test_valid_version_strings(self):
def _test_string(version, exp_major, exp_minor):
v = api_versioning.APIVersion(version)
self.assertEqual(v.ver_major, exp_major)
self.assertEqual(v.ver_minor, exp_minor)
_test_string("1.1", 1, 1)
_test_string("2.10", 2, 10)
_test_string("5.234", 5, 234)
_test_string("12.5", 12, 5)
_test_string("2.0", 2, 0)
_test_string("2.200", 2, 200)
def test_null_version(self):
v = api_versioning.APIVersion()
self.assertTrue(v.is_null())
def test_invalid_version_strings(self):
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "2")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "200")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "2.1.4")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "200.23.66.3")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "5 .3")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "5. 3")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "5.03")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "02.1")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "2.001")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, " 2.1")
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.APIVersion, "2.1 ")
def test_version_comparisons(self):
v1 = api_versioning.APIVersion("2.0")
v2 = api_versioning.APIVersion("2.5")
v3 = api_versioning.APIVersion("5.23")
v4 = api_versioning.APIVersion("2.0")
v_null = api_versioning.APIVersion()
self.assertTrue(v1 < v2)
self.assertTrue(v3 > v2)
self.assertTrue(v1 != v2)
self.assertTrue(v1 == v4)
self.assertTrue(v1 != v_null)
self.assertTrue(v_null == v_null)
self.assertRaises(TypeError, v1.__le__, "2.1")
def test_version_matches(self):
v1 = api_versioning.APIVersion("2.0")
v2 = api_versioning.APIVersion("2.5")
v3 = api_versioning.APIVersion("2.45")
v4 = api_versioning.APIVersion("3.3")
v5 = api_versioning.APIVersion("3.23")
v6 = api_versioning.APIVersion("2.0")
v7 = api_versioning.APIVersion("3.3")
v8 = api_versioning.APIVersion("4.0")
v_null = api_versioning.APIVersion()
self.assertTrue(v2.matches(v1, v3))
self.assertTrue(v2.matches(v1, v_null))
self.assertTrue(v1.matches(v6, v2))
self.assertTrue(v4.matches(v2, v7))
self.assertTrue(v4.matches(v_null, v7))
self.assertTrue(v4.matches(v_null, v8))
self.assertFalse(v1.matches(v2, v3))
self.assertFalse(v5.matches(v2, v4))
self.assertFalse(v2.matches(v3, v1))
self.assertRaises(ValueError, v_null.matches, v1, v3)
def test_get_string(self):
v1_string = "3.23"
v1 = api_versioning.APIVersion(v1_string)
self.assertEqual(v1_string, v1.get_string())
self.assertRaises(ValueError,
api_versioning.APIVersion().get_string)
class GetAPIVersionTestCase(utils.BaseTestCase):
def test_get_available_client_versions(self):
output = api_versioning.get_available_major_versions()
self.assertNotEqual([], output)
def test_wrong_format(self):
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.get_api_version, "something_wrong")
def test_wrong_major_version(self):
self.assertRaises(exceptions.UnsupportedVersion,
api_versioning.get_api_version, "2")
@mock.patch("watcherclient.common.api_versioning.APIVersion")
def test_only_major_part_is_presented(self, mock_apiversion):
version = 7
self.assertEqual(mock_apiversion.return_value,
api_versioning.get_api_version(version))
mock_apiversion.assert_called_once_with("%s.0" % str(version))
@mock.patch("watcherclient.common.api_versioning.APIVersion")
def test_major_and_minor_parts_is_presented(self, mock_apiversion):
version = "2.7"
self.assertEqual(mock_apiversion.return_value,
api_versioning.get_api_version(version))
mock_apiversion.assert_called_once_with(version)

View File

@@ -11,10 +11,10 @@
# under the License.
import json
import uuid
from oslo_utils import uuidutils
from keystoneclient.fixture import v2 as ks_v2_fixture
from keystoneclient.fixture import v3 as ks_v3_fixture
from keystoneauth1.fixture import v2 as ks_v2_fixture
from keystoneauth1.fixture import v3 as ks_v3_fixture
# these are copied from python-keystoneclient tests
BASE_HOST = 'http://keystone.example.com'
@@ -49,7 +49,7 @@ V3_VERSION = {'id': 'v3.0',
'status': 'stable',
'updated': UPDATED}
TOKENID = uuid.uuid4().hex
TOKENID = uuidutils.generate_uuid(dashed=False)
def _create_version_list(versions):

View File

@@ -0,0 +1,352 @@
# 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 mock
from keystoneauth1 import loading as kaloading
from watcherclient import client as watcherclient
from watcherclient.common import httpclient
from watcherclient import exceptions
from watcherclient.tests.unit import utils
class ClientTest(utils.BaseTestCase):
def test_get_client_with_auth_token_watcher_url(self):
kwargs = {
'watcher_url': 'http://watcher.example.org:9322/',
'os_auth_token': 'USER_AUTH_TOKEN',
}
client = watcherclient.get_client('1', **kwargs)
self.assertEqual('USER_AUTH_TOKEN', client.http_client.auth_token)
self.assertEqual('http://watcher.example.org:9322/',
client.http_client.endpoint)
@mock.patch.object(kaloading.session, 'Session', autospec=True)
@mock.patch.object(kaloading, 'get_plugin_loader', autospec=True)
def _test_get_client(self, mock_ks_loader, mock_ks_session,
version=None, auth='password', **kwargs):
session = mock_ks_session.return_value.load_from_options.return_value
session.get_endpoint.return_value = 'http://localhost:9322/v1/f14b4123'
mock_ks_loader.return_value.load_from_options.return_value = 'auth'
watcherclient.get_client('1', **kwargs)
mock_ks_loader.assert_called_once_with(auth)
mock_ks_session.return_value.load_from_options.assert_called_once_with(
auth='auth', timeout=kwargs.get('timeout'),
insecure=kwargs.get('insecure'), cert=kwargs.get('cert'),
cacert=kwargs.get('cacert'), key=kwargs.get('key'))
session.get_endpoint.assert_called_once_with(
service_type=kwargs.get('os_service_type') or 'infra-optim',
interface=kwargs.get('os_endpoint_type') or 'publicURL',
region_name=kwargs.get('os_region_name'))
def test_get_client_no_auth_token(self):
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': '',
}
self._test_get_client(**kwargs)
def test_get_client_service_and_endpoint_type_defaults(self):
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': '',
'os_service_type': '',
'os_endpoint_type': ''
}
self._test_get_client(**kwargs)
def test_get_client_with_region_no_auth_token(self):
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_region_name': 'REGIONONE',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': '',
}
self._test_get_client(**kwargs)
def test_get_client_no_url(self):
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': '',
}
self.assertRaises(
exceptions.AmbiguousAuthSystem, watcherclient.get_client,
'1', **kwargs)
# test the alias as well to ensure backwards compatibility
self.assertRaises(
exceptions.AmbigiousAuthSystem, watcherclient.get_client,
'1', **kwargs)
def test_get_client_incorrect_auth_params(self):
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_auth_url': 'http://localhost:35357/v2.0',
}
self.assertRaises(
exceptions.AmbiguousAuthSystem, watcherclient.get_client,
'1', **kwargs)
def test_get_client_with_api_version_latest(self):
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': '',
'os_watcher_api_version': "latest",
}
self._test_get_client(**kwargs)
def test_get_client_with_api_version_numeric(self):
kwargs = {
'os_tenant_name': 'TENANT_NAME',
'os_username': 'USERNAME',
'os_password': 'PASSWORD',
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': '',
'os_watcher_api_version': "1.4",
}
self._test_get_client(**kwargs)
def test_get_client_with_auth_token(self):
kwargs = {
'os_auth_url': 'http://localhost:35357/v2.0',
'os_auth_token': 'USER_AUTH_TOKEN',
}
self._test_get_client(auth='token', **kwargs)
def test_get_client_with_region_name_auth_token(self):
kwargs = {
'os_auth_url': 'http://localhost:35357/v2.0',
'os_region_name': 'REGIONONE',
'os_auth_token': 'USER_AUTH_TOKEN',
}
self._test_get_client(auth='token', **kwargs)
def test_get_client_only_session_passed(self):
session = mock.Mock()
session.get_endpoint.return_value = 'http://localhost:35357/v2.0'
kwargs = {
'session': session,
}
watcherclient.get_client('1', **kwargs)
session.get_endpoint.assert_called_once_with(
service_type='infra-optim',
interface='publicURL',
region_name=None)
def test_get_client_incorrect_session_passed(self):
session = mock.Mock()
session.get_endpoint.side_effect = Exception('boo')
kwargs = {
'session': session,
}
self.assertRaises(
exceptions.AmbiguousAuthSystem, watcherclient.get_client,
'1', **kwargs)
@mock.patch.object(kaloading.session, 'Session', autospec=True)
@mock.patch.object(kaloading, 'get_plugin_loader', autospec=True)
def _test_loader_arguments_passed_correctly(
self, mock_ks_loader, mock_ks_session,
passed_kwargs, expected_kwargs):
session = mock_ks_session.return_value.load_from_options.return_value
session.get_endpoint.return_value = 'http://localhost:9322/v1/f14b4123'
mock_ks_loader.return_value.load_from_options.return_value = 'auth'
watcherclient.get_client('1', **passed_kwargs)
mock_ks_loader.return_value.load_from_options.assert_called_once_with(
**expected_kwargs)
mock_ks_session.return_value.load_from_options.assert_called_once_with(
auth='auth', timeout=passed_kwargs.get('timeout'),
insecure=passed_kwargs.get('insecure'),
cert=passed_kwargs.get('cert'),
cacert=passed_kwargs.get('cacert'), key=passed_kwargs.get('key'))
session.get_endpoint.assert_called_once_with(
service_type=passed_kwargs.get('os_service_type') or 'infra-optim',
interface=passed_kwargs.get('os_endpoint_type') or 'publicURL',
region_name=passed_kwargs.get('os_region_name'))
def test_loader_arguments_token(self):
passed_kwargs = {
'os_auth_url': 'http://localhost:35357/v3',
'os_region_name': 'REGIONONE',
'os_auth_token': 'USER_AUTH_TOKEN',
}
expected_kwargs = {
'auth_url': 'http://localhost:35357/v3',
'project_id': None,
'project_name': None,
'user_domain_id': None,
'user_domain_name': None,
'project_domain_id': None,
'project_domain_name': None,
'token': 'USER_AUTH_TOKEN'
}
self._test_loader_arguments_passed_correctly(
passed_kwargs=passed_kwargs, expected_kwargs=expected_kwargs)
def test_loader_arguments_password_tenant_name(self):
passed_kwargs = {
'os_auth_url': 'http://localhost:35357/v3',
'os_region_name': 'REGIONONE',
'os_tenant_name': 'TENANT',
'os_username': 'user',
'os_password': '1234',
'os_project_domain_id': 'DEFAULT',
'os_user_domain_id': 'DEFAULT'
}
expected_kwargs = {
'auth_url': 'http://localhost:35357/v3',
'project_id': None,
'project_name': 'TENANT',
'user_domain_id': 'DEFAULT',
'user_domain_name': None,
'project_domain_id': 'DEFAULT',
'project_domain_name': None,
'username': 'user',
'password': '1234'
}
self._test_loader_arguments_passed_correctly(
passed_kwargs=passed_kwargs, expected_kwargs=expected_kwargs)
def test_loader_arguments_password_project_id(self):
passed_kwargs = {
'os_auth_url': 'http://localhost:35357/v3',
'os_region_name': 'REGIONONE',
'os_project_id': '1000',
'os_username': 'user',
'os_password': '1234',
'os_project_domain_name': 'domain1',
'os_user_domain_name': 'domain1'
}
expected_kwargs = {
'auth_url': 'http://localhost:35357/v3',
'project_id': '1000',
'project_name': None,
'user_domain_id': None,
'user_domain_name': 'domain1',
'project_domain_id': None,
'project_domain_name': 'domain1',
'username': 'user',
'password': '1234'
}
self._test_loader_arguments_passed_correctly(
passed_kwargs=passed_kwargs, expected_kwargs=expected_kwargs)
@mock.patch.object(watcherclient, 'Client')
@mock.patch.object(kaloading.session, 'Session', autospec=True)
def test_correct_arguments_passed_to_client_constructor_noauth_mode(
self, mock_ks_session, mock_client):
kwargs = {
'watcher_url': 'http://watcher.example.org:9322/',
'os_auth_token': 'USER_AUTH_TOKEN',
'os_watcher_api_version': 'latest',
'insecure': True,
'max_retries': 10,
'retry_interval': 10,
'os_cacert': 'data'
}
watcherclient.get_client('1', **kwargs)
mock_client.assert_called_once_with(
'1', 'http://watcher.example.org:9322/',
**{
'os_watcher_api_version': 'latest',
'max_retries': 10,
'retry_interval': 10,
'token': 'USER_AUTH_TOKEN',
'insecure': True,
'ca_file': 'data',
'cert_file': None,
'key_file': None,
'timeout': None,
'session': None
}
)
self.assertFalse(mock_ks_session.called)
@mock.patch.object(watcherclient, 'Client')
@mock.patch.object(kaloading.session, 'Session', autospec=True)
def test_correct_arguments_passed_to_client_constructor_session_created(
self, mock_ks_session, mock_client):
session = mock_ks_session.return_value.load_from_options.return_value
kwargs = {
'os_auth_url': 'http://localhost:35357/v3',
'os_region_name': 'REGIONONE',
'os_project_id': '1000',
'os_username': 'user',
'os_password': '1234',
'os_project_domain_name': 'domain1',
'os_user_domain_name': 'domain1'
}
watcherclient.get_client('1', **kwargs)
mock_client.assert_called_once_with(
'1', session.get_endpoint.return_value,
**{
'os_watcher_api_version': None,
'max_retries': None,
'retry_interval': None,
'session': session,
}
)
@mock.patch.object(watcherclient, 'Client')
@mock.patch.object(kaloading.session, 'Session', autospec=True)
def test_correct_arguments_passed_to_client_constructor_session_passed(
self, mock_ks_session, mock_client):
session = mock.Mock()
kwargs = {
'session': session,
}
watcherclient.get_client('1', **kwargs)
mock_client.assert_called_once_with(
'1', session.get_endpoint.return_value,
**{
'os_watcher_api_version': None,
'max_retries': None,
'retry_interval': None,
'session': session,
}
)
self.assertFalse(mock_ks_session.called)
def test_safe_header_with_auth_token(self):
(name, value) = ('X-Auth-Token', u'3b640e2e64d946ac8f55615aff221dc1')
expected_header = (u'X-Auth-Token',
'{SHA1}6de9fb3b0b89099030a54abfeb468e7b1b1f0f2b')
client = httpclient.HTTPClient('http://localhost/')
header_redact = client._process_header(name, value)
self.assertEqual(expected_header, header_redact)
def test_safe_header_with_no_auth_token(self):
name, value = ('Accept', 'application/json')
header = ('Accept', 'application/json')
client = httpclient.HTTPClient('http://localhost/')
header_redact = client._process_header(name, value)
self.assertEqual(header, header_redact)

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -12,7 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from watcherclient.tests import utils
from watcherclient.tests.unit import utils
module_str = 'watcherclient'

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 OpenStack LLC.
# All Rights Reserved.
@@ -19,7 +18,7 @@ import mock
from watcherclient.common.apiclient import exceptions as exc
from watcherclient.common import utils
from watcherclient.tests import utils as test_utils
from watcherclient.tests.unit import utils as test_utils
class UtilsTest(test_utils.BaseTestCase):

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
@@ -16,18 +14,16 @@
# under the License.
import copy
import datetime
import os
import fixtures
import mock
from oslo_utils import strutils
from oslotest import base
import six
from watcherclient.common import http
import testtools
class BaseTestCase(base.BaseTestCase):
class BaseTestCase(testtools.TestCase):
def setUp(self):
super(BaseTestCase, self).setUp()
@@ -55,7 +51,7 @@ class FakeAPI(object):
def raw_request(self, *args, **kwargs):
response = self._request(*args, **kwargs)
body_iter = http.ResponseBodyIterator(six.StringIO(response[1]))
body_iter = iter(six.StringIO(response[1]))
return FakeResponse(response[0]), body_iter
def json_request(self, *args, **kwargs):
@@ -77,6 +73,9 @@ class FakeConnection(object):
def getresponse(self):
return self._response
def __repr__(self):
return ("FakeConnection(response=%s)" % (self._response))
class FakeResponse(object):
def __init__(self, headers, body=None, version=None, status=None,
@@ -88,8 +87,9 @@ class FakeResponse(object):
"""
self.headers = headers
self.body = body
self.version = version
self.status = status
self.raw = mock.Mock()
self.raw.version = version
self.status_code = status
self.reason = reason
def getheaders(self):
@@ -101,44 +101,37 @@ class FakeResponse(object):
def read(self, amt):
return self.body.read(amt)
class FakeServiceCatalog(object):
def url_for(self, endpoint_type, service_type, attr=None,
filter_value=None):
if attr == 'region' and filter_value:
return 'http://regionhost:6385/v1/f14b41234'
else:
return 'http://localhost:6385/v1/f14b41234'
class FakeKeystone(object):
service_catalog = FakeServiceCatalog()
timestamp = datetime.datetime.utcnow() + datetime.timedelta(days=5)
def __init__(self, auth_token):
self.auth_token = auth_token
self.auth_ref = {
'token': {'expires': FakeKeystone.timestamp.strftime(
'%Y-%m-%dT%H:%M:%S.%f'),
'id': 'd1a541311782870742235'}
}
def __repr__(self):
return ("FakeResponse(%s, body=%s, version=%s, status=%s, reason=%s)" %
(self.headers, self.body, self.version, self.status,
self.reason))
class FakeSessionResponse(object):
def __init__(self, headers, content=None, status_code=None):
def __init__(self, headers, content=None, status_code=None, version=None):
self.headers = headers
self.content = content
self.status_code = status_code
self.raw = mock.Mock()
self.raw.version = version
self.reason = ''
def iter_content(self, chunk_size):
return iter(self.content)
class FakeSession(object):
def __init__(self, headers, content=None, status_code=None):
def __init__(self, headers, content=None, status_code=None, version=None):
self.headers = headers
self.content = content
self.status_code = status_code
self.version = version
self.verify = False
self.cert = ('test_cert', 'test_key')
def request(self, url, method, **kwargs):
return FakeSessionResponse(self.headers, self.content,
self.status_code)
request = FakeSessionResponse(
self.headers, self.content, self.status_code, self.version)
return request

View File

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,10 +17,10 @@ import json
import shlex
import mock
from osc_lib import utils as oscutils
from watcherclient import shell
from watcherclient.tests import utils
from watcherclient.v1 import client
from watcherclient.common import httpclient
from watcherclient.tests.unit import utils
class CommandTestCase(utils.BaseTestCase):
@@ -29,17 +28,29 @@ class CommandTestCase(utils.BaseTestCase):
def setUp(self):
super(CommandTestCase, self).setUp()
self.p_build_http_client = mock.patch.object(
client.Client, 'build_http_client')
self.m_build_http_client = self.p_build_http_client.start()
self.fake_env = {
'debug': False,
'insecure': False,
'no_auth': False,
'os_auth_token': '',
'os_auth_url': 'http://127.0.0.1:5000/v2.0',
'os_endpoint_override': 'http://watcher-endpoint:9322',
'os_username': 'test',
'os_password': 'test',
'timeout': 600,
'os_watcher_api_version': '1'}
self.m_env = mock.Mock(
name='m_env',
side_effect=lambda x, *args, **kwargs: self.fake_env.get(
x.lower(), kwargs.get('default', '')))
self.p_env = mock.patch.object(oscutils, 'env', self.m_env)
self.p_env.start()
self.addCleanup(self.p_env.stop)
self.m_watcher_client = mock.Mock(side_effect=client.Client)
self.p_create_client = mock.patch.object(
shell.WatcherShell, 'create_client', self.m_watcher_client)
self.p_create_client.start()
self.addCleanup(self.p_build_http_client.stop)
self.addCleanup(self.p_create_client.stop)
self.p_construct_http_client = mock.patch.object(
httpclient, '_construct_http_client')
self.m_construct_http_client = self.p_construct_http_client.start()
self.addCleanup(self.p_construct_http_client.stop)
def run_cmd(self, cmd, formatting='json'):
if formatting:

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
@@ -15,12 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import testtools
from testtools.matchers import HasLength
from testtools import matchers
from watcherclient.tests import utils
from watcherclient.tests.unit import utils
import watcherclient.v1.action
ACTION1 = {
@@ -57,10 +53,6 @@ ACTION_PLAN1 = {
'state': 'RECOMMENDED'
}
UPDATED_ACTION1 = copy.deepcopy(ACTION1)
NEW_EXTRA = 'key1=val1'
UPDATED_ACTION1['extra'] = NEW_EXTRA
fake_responses = {
'/v1/actions':
{
@@ -100,10 +92,6 @@ fake_responses = {
{},
None,
),
'PATCH': (
{},
UPDATED_ACTION1,
),
},
'/v1/actions/detail?action_plan_uuid=%s' % ACTION1['action_plan']:
{
@@ -127,7 +115,7 @@ fake_responses_pagination = {
'GET': (
{},
{"actions": [ACTION1],
"next": "http://127.0.0.1:6385/v1/actions/?limit=1"}
"next": "http://127.0.0.1:9322/v1/actions/?limit=1"}
),
},
'/v1/actions/?limit=1':
@@ -210,7 +198,7 @@ class ActionManagerTest(testtools.TestCase):
('GET', '/v1/actions/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(actions, HasLength(1))
self.assertThat(actions, matchers.HasLength(1))
def test_actions_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
@@ -221,7 +209,7 @@ class ActionManagerTest(testtools.TestCase):
('GET', '/v1/actions/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(actions, HasLength(2))
self.assertThat(actions, matchers.HasLength(2))
def test_actions_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)
@@ -252,22 +240,3 @@ class ActionManagerTest(testtools.TestCase):
self.assertEqual(ACTION1['uuid'], action.uuid)
self.assertEqual(ACTION1['action_plan'], action.action_plan)
self.assertEqual(ACTION1['next'], action.next)
def test_delete(self):
action = self.mgr.delete(action_id=ACTION1['uuid'])
expect = [
('DELETE', '/v1/actions/%s' % ACTION1['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertIsNone(action)
def test_update(self):
patch = {'op': 'replace',
'value': NEW_EXTRA,
'path': '/extra'}
action = self.mgr.update(action_id=ACTION1['uuid'], patch=patch)
expect = [
('PATCH', '/v1/actions/%s' % ACTION1['uuid'], {}, patch),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(NEW_EXTRA, action.extra)

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
@@ -18,9 +16,9 @@
import copy
import testtools
from testtools.matchers import HasLength
from testtools import matchers
from watcherclient.tests import utils
from watcherclient.tests.unit import utils
import watcherclient.v1.action_plan
ACTION_PLAN1 = {
@@ -86,7 +84,7 @@ fake_responses_pagination = {
'GET': (
{},
{"action_plans": [ACTION_PLAN1],
"next": "http://127.0.0.1:6385/v1/action_plans/?limit=1"}
"next": "http://127.0.0.1:9322/v1/action_plans/?limit=1"}
),
},
'/v1/action_plans/?limit=1':
@@ -147,7 +145,7 @@ class ActionPlanManagerTest(testtools.TestCase):
('GET', '/v1/action_plans/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(action_plans, HasLength(1))
self.assertThat(action_plans, matchers.HasLength(1))
def test_action_plans_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
@@ -158,7 +156,7 @@ class ActionPlanManagerTest(testtools.TestCase):
('GET', '/v1/action_plans/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(action_plans, HasLength(2))
self.assertThat(action_plans, matchers.HasLength(2))
def test_action_plans_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,13 +14,12 @@
# limitations under the License.
import datetime
import mock
import six
from watcherclient import exceptions
from watcherclient import shell
from watcherclient.tests.v1 import base
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 IBM Corp
#
@@ -15,13 +14,12 @@
# under the License.
import datetime
import mock
import six
from watcherclient import exceptions
from watcherclient import shell
from watcherclient.tests.v1 import base
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields
@@ -30,7 +28,7 @@ ACTION_1 = {
'action_plan_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'state': 'PENDING',
'action_type': 'migrate',
'next_uuid': '239f02a5-9649-4e14-9d33-ac2bf67cb755',
'parents': ['239f02a5-9649-4e14-9d33-ac2bf67cb755'],
'input_parameters': {"test": 1},
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
@@ -42,7 +40,7 @@ ACTION_2 = {
'action_plan_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'state': 'PENDING',
'action_type': 'migrate',
'next_uuid': '67653274-eb24-c7ba-70f6-a84e73d80843',
'parents': ['67653274-eb24-c7ba-70f6-a84e73d80843'],
'input_parameters': {"test": 2},
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
@@ -52,7 +50,7 @@ ACTION_2 = {
ACTION_3 = {
'uuid': '67653274-eb24-c7ba-70f6-a84e73d80843',
'action_plan_uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea',
'next_uuid': None,
'parents': [],
'state': 'PENDING',
'action_type': 'sleep',
'created_at': datetime.datetime.now().isoformat(),

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
@@ -18,16 +16,15 @@
import copy
import testtools
from testtools.matchers import HasLength
from testtools import matchers
from watcherclient.tests import utils
from watcherclient.tests.unit import utils
import watcherclient.v1.audit
AUDIT1 = {
'id': 1,
'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9',
'deadline': None,
'audit_type': 'ONE_SHOT',
'goal': 'fc087747-61be-4aad-8126-b701731ae836',
'strategy': '2cf86250-d309-4b81-818e-1537f3dba6e5',
@@ -36,7 +33,6 @@ AUDIT1 = {
AUDIT2 = {
'id': 2,
'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea',
'deadline': None,
'audit_type': 'ONE_SHOT',
'goal': 'fc087747-61be-4aad-8126-b701731ae836',
'strategy': None,
@@ -100,7 +96,7 @@ fake_responses_pagination = {
'GET': (
{},
{"audits": [AUDIT1],
"next": "http://127.0.0.1:6385/v1/audits/?limit=1"}
"next": "http://127.0.0.1:9322/v1/audits/?limit=1"}
),
},
'/v1/audits/?limit=1':
@@ -129,6 +125,26 @@ fake_responses_sorting = {
},
}
fake_responses_goal = {
'/v1/audits/?goal=dummy':
{
'GET': (
{},
{"audits": [AUDIT2, AUDIT1]}
),
},
}
fake_responses_strategy = {
'/v1/audits/?strategy=dummy':
{
'GET': (
{},
{"audits": [AUDIT1]}
),
},
}
class AuditManagerTest(testtools.TestCase):
def setUp(self):
@@ -160,7 +176,7 @@ class AuditManagerTest(testtools.TestCase):
('GET', '/v1/audits/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(audits, HasLength(1))
self.assertThat(audits, matchers.HasLength(1))
def test_audits_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
@@ -171,7 +187,7 @@ class AuditManagerTest(testtools.TestCase):
('GET', '/v1/audits/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(audits, HasLength(2))
self.assertThat(audits, matchers.HasLength(2))
def test_audits_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)
@@ -193,6 +209,26 @@ class AuditManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(audits))
def test_audits_list_goal(self):
self.api = utils.FakeAPI(fake_responses_goal)
self.mgr = watcherclient.v1.audit.AuditManager(self.api)
audits = self.mgr.list(goal='dummy')
expect = [
('GET', '/v1/audits/?goal=dummy', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(audits))
def test_audits_list_strategy(self):
self.api = utils.FakeAPI(fake_responses_strategy)
self.mgr = watcherclient.v1.audit.AuditManager(self.api)
audits = self.mgr.list(strategy='dummy')
expect = [
('GET', '/v1/audits/?strategy=dummy', {}, 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

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 IBM Corp
#
@@ -15,13 +14,12 @@
# under the License.
import datetime
import mock
import six
from watcherclient import exceptions
from watcherclient import shell
from watcherclient.tests.v1 import base
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields
@@ -29,8 +27,6 @@ AUDIT_TEMPLATE_1 = {
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'name': 'at1',
'description': 'Audit Template 1 description',
'host_aggregate': 5,
'extra': {'automatic': False},
'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836',
'strategy_uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5',
'created_at': datetime.datetime.now().isoformat(),
@@ -59,12 +55,10 @@ STRATEGY_1 = {
AUDIT_1 = {
'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9',
'deadline': None,
'audit_type': 'ONESHOT',
'state': 'PENDING',
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'audit_template_name': 'at1',
'host_aggregate': 5,
'goal_name': 'SERVER_CONSOLIDATION',
'strategy_name': 'basic',
'created_at': datetime.datetime.now().isoformat(),
@@ -72,40 +66,42 @@ AUDIT_1 = {
'deleted_at': None,
'parameters': None,
'interval': None,
'scope': '',
'auto_trigger': False,
}
AUDIT_2 = {
'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea',
'deadline': None,
'audit_type': 'ONESHOT',
'state': 'PENDING',
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'audit_template_name': 'at1',
'host_aggregate': None,
'goal_name': 'fc087747-61be-4aad-8126-b701731ae836',
'strategy_name': None,
'strategy_name': 'auto',
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
'deleted_at': None,
'parameters': None,
'interval': None,
'scope': '',
'auto_trigger': False,
}
AUDIT_3 = {
'uuid': '43199d0e-0712-1213-9674-5ae2af8dhgte',
'deadline': None,
'audit_type': 'ONESHOT',
'state': 'PENDING',
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'audit_template_name': 'at1',
'host_aggregate': 3,
'goal_name': None,
'strategy_name': None,
'strategy_name': 'auto',
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
'deleted_at': None,
'parameters': None,
'interval': 3600,
'scope': '',
'auto_trigger': True,
}
@@ -289,7 +285,7 @@ class AuditShellTest(base.CommandTestCase):
result)
self.m_audit_mgr.create.assert_called_once_with(
audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2',
audit_type='ONESHOT')
audit_type='ONESHOT', auto_trigger=False)
def test_do_audit_create_with_audit_template_name(self):
audit = resource.Audit(mock.Mock(), AUDIT_3)
@@ -305,6 +301,7 @@ class AuditShellTest(base.CommandTestCase):
result)
self.m_audit_mgr.create.assert_called_once_with(
audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2',
auto_trigger=False,
audit_type='ONESHOT')
def test_do_audit_create_with_goal(self):
@@ -320,8 +317,9 @@ class AuditShellTest(base.CommandTestCase):
result)
self.m_audit_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
auto_trigger=False,
audit_type='ONESHOT'
)
)
def test_do_audit_create_with_goal_and_strategy(self):
audit = resource.Audit(mock.Mock(), AUDIT_1)
@@ -338,25 +336,9 @@ class AuditShellTest(base.CommandTestCase):
self.m_audit_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
strategy='2cf86250-d309-4b81-818e-1537f3dba6e5',
auto_trigger=False,
audit_type='ONESHOT'
)
def test_do_audit_create_with_deadline(self):
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd(
'audit create -g fc087747-61be-4aad-8126-b701731ae836 -d '
'2016-04-28T10:48:32.064802')
self.assertEqual(0, exit_code)
self.assertEqual(
self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS),
result)
self.m_audit_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
audit_type='ONESHOT',
deadline='2016-04-28T10:48:32.064802')
)
def test_do_audit_create_with_type(self):
audit = resource.Audit(mock.Mock(), AUDIT_1)
@@ -371,7 +353,7 @@ class AuditShellTest(base.CommandTestCase):
result)
self.m_audit_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
audit_type='ONESHOT')
auto_trigger=False, audit_type='ONESHOT')
def test_do_audit_create_with_parameter(self):
audit = resource.Audit(mock.Mock(), AUDIT_1)
@@ -388,6 +370,7 @@ class AuditShellTest(base.CommandTestCase):
self.m_audit_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
audit_type='ONESHOT',
auto_trigger=False,
parameters={'para1': 10, 'para2': 20})
def test_do_audit_create_with_type_continuous(self):
@@ -405,4 +388,5 @@ class AuditShellTest(base.CommandTestCase):
self.m_audit_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
audit_type='CONTINUOUS',
auto_trigger=False,
interval='3600')

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
@@ -20,7 +18,7 @@ import copy
from six.moves.urllib import parse as urlparse
from testtools import matchers
from watcherclient.tests import utils
from watcherclient.tests.unit import utils
import watcherclient.v1.audit_template
AUDIT_TMPL1 = {
@@ -28,8 +26,6 @@ AUDIT_TMPL1 = {
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'name': 'Audit Template 1',
'description': 'Audit Template 1 description',
'host_aggregate': 5,
'extra': {'automatic': False},
'goal_uuid': '7568667b-51fe-4087-9eb1-29b26891036f',
'goal_name': 'SERVER_CONSOLIDATION',
'strategy_uuid': 'bbe6b966-f98e-439b-a01a-17b9b3b8478b',
@@ -41,8 +37,6 @@ AUDIT_TMPL2 = {
'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea',
'name': 'Audit Template 2',
'description': 'Audit Template 2 description',
'host_aggregate': 8,
'extra': {'automatic': True},
'goal_uuid': 'e75ee410-b32b-465f-88b5-4397705f9473',
'goal_name': 'DUMMY',
'strategy_uuid': 'ae99a4a4-acbc-4c67-abe1-e37128fac45d',
@@ -54,8 +48,6 @@ AUDIT_TMPL3 = {
'uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588',
'name': 'Audit Template 3',
'description': 'Audit Template 3 description',
'host_aggregate': 7,
'extra': {'automatic': True},
'goal_uuid': '7568667b-51fe-4087-9eb1-29b26891036f',
'goal_name': 'SERVER_CONSOLIDATION',
}
@@ -157,7 +149,7 @@ fake_responses_pagination = {
'GET': (
{},
{"audit_templates": [AUDIT_TMPL1],
"next": "http://127.0.0.1:6385/v1/audit_templates/?limit=1"}
"next": "http://127.0.0.1:9322/v1/audit_templates/?limit=1"}
),
},
'/v1/audit_templates/?limit=1':
@@ -407,12 +399,9 @@ class AuditTemplateManagerTest(utils.BaseTestCase):
self.assertEqual(AUDIT_TMPL1['name'], audit_template.name)
self.assertEqual(AUDIT_TMPL1['description'],
audit_template.description)
self.assertEqual(AUDIT_TMPL1['host_aggregate'],
audit_template.host_aggregate)
self.assertEqual(AUDIT_TMPL1['goal_uuid'], audit_template.goal_uuid)
self.assertEqual(AUDIT_TMPL1['strategy_uuid'],
audit_template.strategy_uuid)
self.assertEqual(AUDIT_TMPL1['extra'], audit_template.extra)
def test_audit_templates_show_by_name(self):
audit_template = self.mgr.get(urlparse.quote(AUDIT_TMPL1['name']))
@@ -427,12 +416,9 @@ class AuditTemplateManagerTest(utils.BaseTestCase):
self.assertEqual(AUDIT_TMPL1['name'], audit_template.name)
self.assertEqual(AUDIT_TMPL1['description'],
audit_template.description)
self.assertEqual(AUDIT_TMPL1['host_aggregate'],
audit_template.host_aggregate)
self.assertEqual(AUDIT_TMPL1['goal_uuid'], audit_template.goal_uuid)
self.assertEqual(AUDIT_TMPL1['strategy_uuid'],
audit_template.strategy_uuid)
self.assertEqual(AUDIT_TMPL1['extra'], audit_template.extra)
def test_create(self):
audit_template = self.mgr.create(**CREATE_AUDIT_TEMPLATE)

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 IBM Corp
#
@@ -15,12 +14,11 @@
# under the License.
import datetime
import mock
import six
from watcherclient import shell
from watcherclient.tests.v1 import base
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields
@@ -47,8 +45,6 @@ AUDIT_TEMPLATE_1 = {
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'name': 'at1',
'description': 'Audit Template 1 description',
'host_aggregate': 5,
'extra': {'automatic': False},
'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836',
'goal_name': 'SERVER_CONSOLIDATION',
'strategy_uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5',
@@ -56,14 +52,13 @@ AUDIT_TEMPLATE_1 = {
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
'deleted_at': None,
'scope': []
}
AUDIT_TEMPLATE_2 = {
'uuid': '2a60ca9b-09b0-40ff-8674-de8a36fc4bc8',
'name': 'at2',
'description': 'Audit Template 2',
'host_aggregate': 3,
'extra': {'automatic': False},
'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836',
'goal_name': 'SERVER_CONSOLIDATION',
'strategy_uuid': None,
@@ -71,6 +66,7 @@ AUDIT_TEMPLATE_2 = {
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
'deleted_at': None,
'scope': []
}
@@ -299,7 +295,7 @@ class AuditTemplateShellTest(base.CommandTestCase):
self.m_audit_template_mgr.update.return_value = audit_template
exit_code, result = self.run_cmd(
'audittemplate update at1 replace host_aggregate=5')
'audittemplate update at1 replace description="New description"')
self.assertEqual(0, exit_code)
self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS,
@@ -307,7 +303,8 @@ class AuditTemplateShellTest(base.CommandTestCase):
result)
self.m_audit_template_mgr.update.assert_called_once_with(
'at1',
[{'op': 'replace', 'path': '/host_aggregate', 'value': 5}])
[{'op': 'replace', 'path': '/description',
'value': 'New description'}])
def test_do_audit_template_create(self):
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
@@ -346,8 +343,7 @@ class AuditTemplateShellTest(base.CommandTestCase):
self.m_audit_template_mgr.create.return_value = audit_template
exit_code, result = self.run_cmd(
'audittemplate create at1 fc087747-61be-4aad-8126-b701731ae836 '
'-a 5')
'audittemplate create at1 fc087747-61be-4aad-8126-b701731ae836')
self.assertEqual(0, exit_code)
self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS,
@@ -355,22 +351,4 @@ class AuditTemplateShellTest(base.CommandTestCase):
result)
self.m_audit_template_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
name='at1',
host_aggregate='5')
def test_do_audit_template_create_with_extra(self):
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
self.m_audit_template_mgr.create.return_value = audit_template
exit_code, result = self.run_cmd(
'audittemplate create at1 fc087747-61be-4aad-8126-b701731ae836 '
'-e automatic=true')
self.assertEqual(0, exit_code)
self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS,
self.FIELD_LABELS),
result)
self.m_audit_template_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
name='at1',
extra={'automatic': True})
name='at1')

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
@@ -17,9 +15,9 @@
import testtools
from testtools.matchers import HasLength
from testtools import matchers
from watcherclient.tests import utils
from watcherclient.tests.unit import utils
import watcherclient.v1.goal
GOAL1 = {
@@ -71,7 +69,7 @@ fake_responses_pagination = {
'GET': (
{},
{"goals": [GOAL1],
"next": "http://127.0.0.1:6385/v1/goals/?limit=1"}
"next": "http://127.0.0.1:9322/v1/goals/?limit=1"}
),
},
'/v1/goals/?limit=1':
@@ -132,7 +130,7 @@ class GoalManagerTest(testtools.TestCase):
('GET', '/v1/goals/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(goals, HasLength(1))
self.assertThat(goals, matchers.HasLength(1))
def test_goals_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
@@ -143,7 +141,7 @@ class GoalManagerTest(testtools.TestCase):
('GET', '/v1/goals/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(goals, HasLength(2))
self.assertThat(goals, matchers.HasLength(2))
def test_goals_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,12 +14,11 @@
# limitations under the License.
import datetime
import mock
import six
from watcherclient import shell
from watcherclient.tests.v1 import base
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 Intel
#
@@ -16,9 +15,9 @@
import testtools
from testtools.matchers import HasLength
from testtools import matchers
from watcherclient.tests import utils
from watcherclient.tests.unit import utils
import watcherclient.v1.scoring_engine
SE1 = {
@@ -70,7 +69,7 @@ fake_responses_pagination = {
'GET': (
{},
{"scoring_engines": [SE1],
"next": "http://127.0.0.1:6385/v1/scoring_engines/?limit=1"}
"next": "http://127.0.0.1:9322/v1/scoring_engines/?limit=1"}
),
},
'/v1/scoring_engines/?limit=1':
@@ -133,7 +132,7 @@ class ScoringEngineManagerTest(testtools.TestCase):
('GET', '/v1/scoring_engines/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(scoring_engines, HasLength(1))
self.assertThat(scoring_engines, matchers.HasLength(1))
def test_scoring_engines_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
@@ -145,7 +144,7 @@ class ScoringEngineManagerTest(testtools.TestCase):
('GET', '/v1/scoring_engines/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(scoring_engines, HasLength(2))
self.assertThat(scoring_engines, matchers.HasLength(2))
def test_scoring_engines_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 Intel
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,12 +14,11 @@
# limitations under the License.
import datetime
import mock
import six
from watcherclient import shell
from watcherclient.tests.v1 import base
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields

View File

@@ -0,0 +1,182 @@
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
# 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 testtools
from testtools import matchers
from watcherclient.tests.unit import utils
import watcherclient.v1.service
SERVICE1 = {
'id': 1,
'name': 'watcher-applier',
'host': 'controller',
'status': 'ACTIVE',
}
SERVICE2 = {
'id': 2,
'name': 'watcher-decision-engine',
'host': 'controller',
'status': 'FAILED',
}
fake_responses = {
'/v1/services':
{
'GET': (
{},
{"services": [SERVICE1]},
),
},
'/v1/services/detail':
{
'GET': (
{},
{"services": [SERVICE1]},
)
},
'/v1/services/%s' % SERVICE1['id']:
{
'GET': (
{},
SERVICE1,
),
},
'/v1/services/%s' % SERVICE1['name']:
{
'GET': (
{},
SERVICE1,
),
},
}
fake_responses_pagination = {
'/v1/services':
{
'GET': (
{},
{"services": [SERVICE1],
"next": "http://127.0.0.1:6385/v1/services/?limit=1"}
),
},
'/v1/services/?limit=1':
{
'GET': (
{},
{"services": [SERVICE2]}
),
},
}
fake_responses_sorting = {
'/v1/services/?sort_key=id':
{
'GET': (
{},
{"services": [SERVICE1, SERVICE2]}
),
},
'/v1/services/?sort_dir=desc':
{
'GET': (
{},
{"services": [SERVICE2, SERVICE1]}
),
},
}
class ServiceManagerTest(testtools.TestCase):
def setUp(self):
super(ServiceManagerTest, self).setUp()
self.api = utils.FakeAPI(fake_responses)
self.mgr = watcherclient.v1.service.ServiceManager(self.api)
def test_services_list(self):
services = self.mgr.list()
expect = [
('GET', '/v1/services', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(services))
def test_services_list_detail(self):
services = self.mgr.list(detail=True)
expect = [
('GET', '/v1/services/detail', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(services))
def test_services_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = watcherclient.v1.service.ServiceManager(self.api)
services = self.mgr.list(limit=1)
expect = [
('GET', '/v1/services/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(services, matchers.HasLength(1))
def test_services_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = watcherclient.v1.service.ServiceManager(self.api)
services = self.mgr.list(limit=0)
expect = [
('GET', '/v1/services', {}, None),
('GET', '/v1/services/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(services, matchers.HasLength(2))
def test_services_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)
self.mgr = watcherclient.v1.service.ServiceManager(self.api)
services = self.mgr.list(sort_key='id')
expect = [
('GET', '/v1/services/?sort_key=id', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(services))
def test_services_list_sort_dir(self):
self.api = utils.FakeAPI(fake_responses_sorting)
self.mgr = watcherclient.v1.service.ServiceManager(self.api)
services = self.mgr.list(sort_dir='desc')
expect = [
('GET', '/v1/services/?sort_dir=desc', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(services))
def test_services_show(self):
service = self.mgr.get(SERVICE1['id'])
expect = [
('GET', '/v1/services/%s' % SERVICE1['id'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(SERVICE1['id'], service.id)
def test_services_show_by_name(self):
service = self.mgr.get(SERVICE1['name'])
expect = [
('GET', '/v1/services/%s' % SERVICE1['name'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(SERVICE1['name'], service.name)

View File

@@ -0,0 +1,121 @@
# Copyright (c) 2016 Servionica
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import datetime
import mock
import six
from watcherclient import shell
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields
SERVICE_1 = {
'name': 'watcher-applier',
'host': 'controller',
'status': 'ACTIVE',
'last_seen_up': None,
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
'deleted_at': None,
}
SERVICE_2 = {
'name': 'watcher-decision-engine',
'host': 'controller',
'status': 'FAILED',
'last_seen_up': None,
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
'deleted_at': None,
}
class ServiceShellTest(base.CommandTestCase):
SHORT_LIST_FIELDS = resource_fields.SERVICE_SHORT_LIST_FIELDS
SHORT_LIST_FIELD_LABELS = (
resource_fields.SERVICE_SHORT_LIST_FIELD_LABELS)
FIELDS = resource_fields.SERVICE_FIELDS
FIELD_LABELS = resource_fields.SERVICE_FIELD_LABELS
def setUp(self):
super(self.__class__, self).setUp()
p_service_manager = mock.patch.object(resource, 'ServiceManager')
self.m_service_mgr_cls = p_service_manager.start()
self.addCleanup(p_service_manager.stop)
self.m_service_mgr = mock.Mock()
self.m_service_mgr_cls.return_value = self.m_service_mgr
self.stdout = six.StringIO()
self.cmd = shell.WatcherShell(stdout=self.stdout)
def test_do_service_list(self):
service1 = resource.Service(mock.Mock(), SERVICE_1)
service2 = resource.Service(mock.Mock(), SERVICE_2)
self.m_service_mgr.list.return_value = [
service1, service2]
exit_code, results = self.run_cmd('service list')
for res in results:
del res['ID']
self.assertEqual(0, exit_code)
self.assertEqual(
[self.resource_as_dict(service1, self.SHORT_LIST_FIELDS,
self.SHORT_LIST_FIELD_LABELS),
self.resource_as_dict(service2, self.SHORT_LIST_FIELDS,
self.SHORT_LIST_FIELD_LABELS)],
results)
self.m_service_mgr.list.assert_called_once_with(detail=False)
def test_do_service_list_detail(self):
service1 = resource.Service(mock.Mock(), SERVICE_1)
service2 = resource.Service(mock.Mock(), SERVICE_2)
self.m_service_mgr.list.return_value = [
service1, service2]
exit_code, results = self.run_cmd('service list --detail')
for res in results:
del res['ID']
self.assertEqual(0, exit_code)
self.assertEqual(
[self.resource_as_dict(service1, self.FIELDS,
self.FIELD_LABELS),
self.resource_as_dict(service2, self.FIELDS,
self.FIELD_LABELS)],
results)
self.m_service_mgr.list.assert_called_once_with(detail=True)
def test_do_service_show_by_name(self):
service = resource.Service(mock.Mock(), SERVICE_1)
self.m_service_mgr.get.return_value = service
exit_code, result = self.run_cmd('service show watcher-applier')
del result['ID']
self.assertEqual(0, exit_code)
self.assertEqual(
self.resource_as_dict(service, self.FIELDS, self.FIELD_LABELS),
result)
self.m_service_mgr.get.assert_called_once_with('watcher-applier')

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
@@ -17,9 +15,9 @@
import testtools
from testtools.matchers import HasLength
from testtools import matchers
from watcherclient.tests import utils
from watcherclient.tests.unit import utils
import watcherclient.v1.strategy
STRATEGY1 = {
@@ -73,7 +71,7 @@ fake_responses_pagination = {
'GET': (
{},
{"strategies": [STRATEGY1],
"next": "http://127.0.0.1:6385/v1/strategies/?limit=1"}
"next": "http://127.0.0.1:9322/v1/strategies/?limit=1"}
),
},
'/v1/strategies/?limit=1':
@@ -134,7 +132,7 @@ class StrategyManagerTest(testtools.TestCase):
('GET', '/v1/strategies/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(strategies, HasLength(1))
self.assertThat(strategies, matchers.HasLength(1))
def test_strategies_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
@@ -145,7 +143,7 @@ class StrategyManagerTest(testtools.TestCase):
('GET', '/v1/strategies/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(strategies, HasLength(2))
self.assertThat(strategies, matchers.HasLength(2))
def test_strategies_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,12 +14,11 @@
# limitations under the License.
import datetime
import mock
import six
from watcherclient import shell
from watcherclient.tests.v1 import base
from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields

View File

@@ -1,321 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
# 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 copy
import testtools
from testtools.matchers import HasLength
from watcherclient.tests import utils
import watcherclient.v1.metric_collector
METRIC_COLLECTOR1 = {
'id': 1,
'uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588',
'category': 'cat1',
'endpoint': 'http://metric_collector_1:6446',
}
METRIC_COLLECTOR2 = {
'id': 2,
'uuid': '67653274-eb24-c7ba-70f6-a84e73d80843',
'category': 'cat2',
}
METRIC_COLLECTOR3 = {
'id': 3,
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'category': 'cat2',
'endpoint': 'http://metric_collector_3:6446',
}
CREATE_METRIC_COLLECTOR = copy.deepcopy(METRIC_COLLECTOR1)
del CREATE_METRIC_COLLECTOR['id']
del CREATE_METRIC_COLLECTOR['uuid']
UPDATED_METRIC_COLLECTOR1 = copy.deepcopy(METRIC_COLLECTOR1)
NEW_ENDPOINT = 'http://metric_collector_1:6447'
UPDATED_METRIC_COLLECTOR1['endpoint'] = NEW_ENDPOINT
fake_responses = {
'/v1/metric-collectors':
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR1]},
),
'POST': (
{},
CREATE_METRIC_COLLECTOR,
),
},
'/v1/metric-collectors/?category=%s' % METRIC_COLLECTOR1['category']:
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR1]},
),
},
'/v1/metric-collectors/?category=%s' % METRIC_COLLECTOR2['category']:
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR2, METRIC_COLLECTOR3]},
),
},
'/v1/metric-collectors/detail':
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR1]},
),
},
'/v1/metric-collectors/%s' % METRIC_COLLECTOR1['uuid']:
{
'GET': (
{},
METRIC_COLLECTOR1,
),
'DELETE': (
{},
None,
),
'PATCH': (
{},
UPDATED_METRIC_COLLECTOR1,
),
},
'/v1/metric-collectors/detail?category=%s' % METRIC_COLLECTOR1['category']:
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR1]},
),
},
'/v1/metric-collectors/detail?category=%s' % METRIC_COLLECTOR2['category']:
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR2, METRIC_COLLECTOR3]},
),
},
}
fake_responses_pagination = {
'/v1/metric-collectors':
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR1],
"next": "http://127.0.0.1:6385/v1/metric-collectors/?limit=1"}
),
},
'/v1/metric-collectors/?limit=1':
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR2]}
),
},
}
fake_responses_sorting = {
'/v1/metric-collectors/?sort_key=updated_at':
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR2, METRIC_COLLECTOR1]}
),
},
'/v1/metric-collectors/?sort_dir=desc':
{
'GET': (
{},
{"metric-collectors": [METRIC_COLLECTOR2, METRIC_COLLECTOR1]}
),
},
}
class MetricCollectorManagerTest(testtools.TestCase):
def setUp(self):
super(MetricCollectorManagerTest, self).setUp()
self.api = utils.FakeAPI(fake_responses)
self.mgr = watcherclient.v1.metric_collector \
.MetricCollectorManager(self.api)
def test_metric_collectors_list(self):
metric_collectors = self.mgr.list()
expect = [
('GET', '/v1/metric-collectors', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(metric_collectors))
def test_metric_collectors_list_by_category(self):
metric_collectors = self.mgr.list(
category=METRIC_COLLECTOR1['category']
)
expect = [
('GET',
'/v1/metric-collectors/?category=%s' %
METRIC_COLLECTOR1['category'],
{},
None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(metric_collectors))
def test_metric_collectors_list_by_category_bis(self):
metric_collectors = self.mgr.list(
category=METRIC_COLLECTOR2['category']
)
expect = [
('GET',
'/v1/metric-collectors/?category=%s' %
METRIC_COLLECTOR2['category'],
{},
None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(metric_collectors))
def test_metric_collectors_list_detail(self):
metric_collectors = self.mgr.list(detail=True)
expect = [
('GET', '/v1/metric-collectors/detail', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(metric_collectors))
def test_metric_collectors_list_by_category_detail(self):
metric_collectors = self.mgr.list(
category=METRIC_COLLECTOR1['category'],
detail=True)
expect = [
('GET',
'/v1/metric-collectors/detail?category=%s' %
METRIC_COLLECTOR1['category'],
{},
None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(metric_collectors))
def test_metric_collectors_list_by_category_detail_bis(self):
metric_collectors = self.mgr.list(
category=METRIC_COLLECTOR2['category'],
detail=True)
expect = [
('GET',
'/v1/metric-collectors/detail?category=%s' %
METRIC_COLLECTOR2['category'],
{},
None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(metric_collectors))
def test_metric_collectors_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = watcherclient.v1.metric_collector \
.MetricCollectorManager(self.api)
metric_collectors = self.mgr.list(limit=1)
expect = [
('GET', '/v1/metric-collectors/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(metric_collectors, HasLength(1))
def test_metric_collectors_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = watcherclient.v1.metric_collector \
.MetricCollectorManager(self.api)
metric_collectors = self.mgr.list(limit=0)
expect = [
('GET', '/v1/metric-collectors', {}, None),
('GET', '/v1/metric-collectors/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(metric_collectors, HasLength(2))
def test_metric_collectors_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)
self.mgr = watcherclient.v1.metric_collector \
.MetricCollectorManager(self.api)
metric_collectors = self.mgr.list(sort_key='updated_at')
expect = [
('GET', '/v1/metric-collectors/?sort_key=updated_at', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(metric_collectors))
def test_metric_collectors_list_sort_dir(self):
self.api = utils.FakeAPI(fake_responses_sorting)
self.mgr = watcherclient.v1.metric_collector \
.MetricCollectorManager(self.api)
metric_collectors = self.mgr.list(sort_dir='desc')
expect = [
('GET', '/v1/metric-collectors/?sort_dir=desc', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(metric_collectors))
def test_metric_collectors_show(self):
metric_collector = self.mgr.get(METRIC_COLLECTOR1['uuid'])
expect = [
('GET', '/v1/metric-collectors/%s' %
METRIC_COLLECTOR1['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(METRIC_COLLECTOR1['uuid'], metric_collector.uuid)
self.assertEqual(METRIC_COLLECTOR1['category'],
metric_collector.category)
self.assertEqual(METRIC_COLLECTOR1['endpoint'],
metric_collector.endpoint)
def test_create(self):
metric_collector = self.mgr.create(**CREATE_METRIC_COLLECTOR)
expect = [
('POST', '/v1/metric-collectors', {}, CREATE_METRIC_COLLECTOR),
]
self.assertEqual(expect, self.api.calls)
self.assertTrue(metric_collector)
def test_delete(self):
metric_collector = self.mgr.delete(
metric_collector_id=METRIC_COLLECTOR1['uuid'])
expect = [
('DELETE', '/v1/metric-collectors/%s' %
METRIC_COLLECTOR1['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertIsNone(metric_collector)
def test_update(self):
patch = {'op': 'replace',
'value': NEW_ENDPOINT,
'path': '/endpoint'}
metric_collector = self.mgr.update(
metric_collector_id=METRIC_COLLECTOR1['uuid'], patch=patch)
expect = [
('PATCH', '/v1/metric-collectors/%s' %
METRIC_COLLECTOR1['uuid'], {}, patch),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(NEW_ENDPOINT, metric_collector.endpoint)

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,6 +19,7 @@ from watcherclient.v1 import audit
from watcherclient.v1 import audit_template
from watcherclient.v1 import goal
from watcherclient.v1 import scoring_engine
from watcherclient.v1 import service
from watcherclient.v1 import strategy
Action = action.Action
@@ -34,6 +34,8 @@ Goal = goal.Goal
GoalManager = goal.GoalManager
ScoringEngine = scoring_engine.ScoringEngine
ScoringEngineManager = scoring_engine.ScoringEngineManager
Service = service.Service
ServiceManager = service.ServiceManager
Strategy = strategy.Strategy
StrategyManager = strategy.StrategyManager
@@ -41,4 +43,4 @@ __all__ = (
"Action", "ActionManager", "ActionPlan", "ActionPlanManager",
"Audit", "AuditManager", "AuditTemplate", "AuditTemplateManager",
"Goal", "GoalManager", "ScoringEngine", "ScoringEngineManager",
"Strategy", "StrategyManager")
"Service", "ServiceManager", "Strategy", "StrategyManager")

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Red Hat, Inc.
#
@@ -82,9 +81,3 @@ class ActionManager(base.Manager):
return self._list(self._path(action_id))[0]
except IndexError:
return None
def delete(self, action_id):
return self._delete(self._path(action_id))
def update(self, action_id, patch):
return self._update(self._path(action_id), patch)

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Red Hat, Inc.
#

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,7 +29,7 @@ def format_global_efficacy(global_efficacy):
formatted_global_efficacy = None
if (global_efficacy.get('value') is not None and
global_efficacy.get('unit')):
formatted_global_efficacy = "%(value)s %(unit)s" % dict(
formatted_global_efficacy = "%(value).2f %(unit)s" % dict(
unit=global_efficacy.get('unit'),
value=global_efficacy.get('value'))
elif global_efficacy.get('value') is not None:
@@ -183,11 +182,6 @@ class CreateActionPlan(command.ShowOne):
dest='audit_template_uuid',
metavar='<audit_template>',
help=_('ActionPlan template used for this audit (name or uuid).'))
parser.add_argument(
'-d', '--deadline',
dest='deadline',
metavar='<deadline>',
help=_('Descrition of the audit.'))
parser.add_argument(
'-t', '--audit_type',
dest='audit_type',
@@ -200,7 +194,7 @@ class CreateActionPlan(command.ShowOne):
def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim")
field_list = ['audit_template_uuid', 'audit_type', 'deadline']
field_list = ['audit_template_uuid', 'audit_type']
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
if k in field_list and v is not None)
if fields.get('audit_template_uuid'):
@@ -229,7 +223,7 @@ class UpdateActionPlan(command.ShowOne):
'op',
metavar='<op>',
choices=['add', 'replace', 'remove'],
help=_("Operation: 'add'), 'replace', or 'remove'."))
help=_("Operation: 'add', 'replace', or 'remove'."))
parser.add_argument(
'attributes',
metavar='<path=value>',

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Red Hat, Inc.
#
@@ -19,9 +18,8 @@ from watcherclient.common import utils
from watcherclient import exceptions as exc
CREATION_ATTRIBUTES = ['audit_template_uuid', 'host_aggregate',
'deadline', 'audit_type', 'interval',
'parameters', 'goal', 'strategy']
CREATION_ATTRIBUTES = ['audit_template_uuid', 'audit_type', 'interval',
'parameters', 'goal', 'strategy', 'auto_trigger']
class Audit(base.Resource):
@@ -37,7 +35,7 @@ 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):
sort_dir=None, detail=False, goal=None, strategy=None):
"""Retrieve a list of audit.
:param audit_template: Name of the audit
@@ -68,6 +66,10 @@ class AuditManager(base.Manager):
filters = utils.common_filters(limit, sort_key, sort_dir)
if audit_template is not None:
filters.append('audit_template=%s' % audit_template)
if goal is not None:
filters.append('goal=%s' % goal)
if strategy is not None:
filters.append('strategy=%s' % strategy)
path = ''
if detail:

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,6 +40,8 @@ class ShowAudit(command.ShowOne):
try:
audit = client.audit.get(parsed_args.audit)
if audit.strategy_name is None:
audit.strategy_name = 'auto'
except exceptions.HTTPNotFound as exc:
raise exceptions.CommandError(str(exc))
@@ -115,6 +116,9 @@ class ListAudit(command.Lister):
try:
data = client.audit.list(**params)
for audit in data:
if audit.strategy_name is None:
audit.strategy_name = 'auto'
except exceptions.HTTPNotFound as ex:
raise exceptions.CommandError(str(ex))
@@ -127,11 +131,6 @@ class CreateAudit(command.ShowOne):
def get_parser(self, prog_name):
parser = super(CreateAudit, self).get_parser(prog_name)
parser.add_argument(
'-d', '--deadline',
dest='deadline',
metavar='<deadline>',
help=_('Descrition of the audit.'))
parser.add_argument(
'-t', '--audit_type',
dest='audit_type',
@@ -151,7 +150,8 @@ class CreateAudit(command.ShowOne):
'-i', '--interval',
dest='interval',
metavar='<interval>',
help=_("Audit interval."))
help=_('Audit interval (in seconds). '
"Only used if the audit is CONTINUOUS."))
parser.add_argument(
'-g', '--goal',
dest='goal',
@@ -162,25 +162,25 @@ class CreateAudit(command.ShowOne):
dest='strategy',
metavar='<strategy>',
help=_('Strategy UUID or name associated to this audit.'))
parser.add_argument(
'-r', '--host-aggregate',
dest='host_aggregate',
metavar='<host-aggregate>',
help=_('Name or UUID of the host aggregate targeted '
'by this audit.'))
parser.add_argument(
'-a', '--audit-template',
dest='audit_template_uuid',
metavar='<audit_template>',
help=_('Audit template used for this audit (name or uuid).'))
parser.add_argument(
'--auto-trigger',
dest='auto_trigger',
action='store_true',
default=False,
help=_('Trigger automatically action plan '
'once audit is succeeded.'))
return parser
def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim")
field_list = ['audit_template_uuid', 'host_aggregate',
'audit_type', 'deadline', 'parameters', 'interval',
'goal', 'strategy']
field_list = ['audit_template_uuid', 'audit_type', 'parameters',
'interval', 'goal', 'strategy', 'auto_trigger']
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
if k in field_list and v is not None)
@@ -201,6 +201,8 @@ class CreateAudit(command.ShowOne):
fields['strategy']).uuid
audit = client.audit.create(**fields)
if audit.strategy_name is None:
audit.strategy_name = 'auto'
columns = res_fields.AUDIT_FIELDS
column_headers = res_fields.AUDIT_FIELD_LABELS

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Red Hat, Inc.
#
@@ -18,8 +17,7 @@ from watcherclient.common import base
from watcherclient.common import utils
from watcherclient import exceptions as exc
CREATION_ATTRIBUTES = ['host_aggregate', 'description', 'name',
'extra', 'goal', 'strategy']
CREATION_ATTRIBUTES = ['description', 'name', 'goal', 'strategy', 'scope']
class AuditTemplate(base.Resource):

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
from osc_lib import utils
from oslo_utils import uuidutils
@@ -126,7 +127,17 @@ class CreateAuditTemplate(command.ShowOne):
"""Create new audit template."""
def get_parser(self, prog_name):
parser = super(CreateAuditTemplate, self).get_parser(prog_name)
class SmartFormatter(argparse.HelpFormatter):
def _split_lines(self, text, width):
if '\n' in text:
return text.splitlines()
else:
return argparse.HelpFormatter._split_lines(
self, text, width)
parser = super(CreateAuditTemplate, self).get_parser(
prog_name, formatter_class=SmartFormatter)
parser.add_argument(
'name',
metavar='<name>',
@@ -143,27 +154,54 @@ class CreateAuditTemplate(command.ShowOne):
parser.add_argument(
'-d', '--description',
metavar='<description>',
help=_('Descrition of the audit template.'))
help=_('Description of the audit template.'))
parser.add_argument(
'-e', '--extra',
metavar='<key=value>',
action='append',
help=_("Record arbitrary key/value metadata. "
"Can be specified multiple times."))
parser.add_argument(
'-a', '--host-aggregate',
dest='host_aggregate',
metavar='<host-aggregate>',
help=_('Name or UUID of the host aggregate targeted '
'by this audit template.'))
'--scope',
metavar='<path>',
help=_("Part of the cluster on which an audit will be done.\n"
"Can be provided either in yaml or json file.\n"
"YAML example:\n"
"---\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"
" - compute_nodes:\n"
" - name: compute1\n"
"\n"
"JSON example:\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"
)
)
return parser
def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim")
field_list = ['host_aggregate', 'description', 'name', 'extra',
'goal', 'strategy']
field_list = ['description', 'name', 'goal', 'strategy', 'scope']
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
if k in field_list and v is not None)
@@ -176,8 +214,10 @@ class CreateAuditTemplate(command.ShowOne):
if not uuidutils.is_uuid_like(fields['strategy']):
fields['strategy'] = client.strategy.get(
fields['strategy']).uuid
if fields.get('scope'):
fields['scope'] = common_utils.serialize_file_to_dict(
fields['scope'])
fields = common_utils.args_array_to_dict(fields, 'extra')
audit_template = client.audit_template.create(**fields)
columns = res_fields.AUDIT_TEMPLATE_FIELDS
@@ -200,7 +240,7 @@ class UpdateAuditTemplate(command.ShowOne):
'op',
metavar='<op>',
choices=['add', 'replace', 'remove'],
help=_("Operation: 'add'), 'replace', or 'remove'."))
help=_("Operation: 'add', 'replace', or 'remove'."))
parser.add_argument(
'attributes',
metavar='<path=value>',

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
@@ -15,7 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from watcherclient.common import http
from watcherclient._i18n import _
from watcherclient.common import httpclient
from watcherclient import exceptions
from watcherclient import v1
@@ -29,17 +29,30 @@ class Client(object):
http requests. (optional)
"""
def __init__(self, *args, **kwargs):
def __init__(self, endpoint=None, *args, **kwargs):
"""Initialize a new client for the Watcher v1 API."""
self.http_client = self.build_http_client(*args, **kwargs)
if kwargs.get('os_watcher_api_version'):
kwargs['api_version_select_state'] = "user"
else:
if not endpoint:
raise exceptions.EndpointException(
_("Must provide 'endpoint' if os_watcher_api_version "
"isn't specified"))
# If the user didn't specify a version, use a cached version if
# one has been stored
host, netport = httpclient.get_server(endpoint)
kwargs['api_version_select_state'] = "default"
kwargs['os_watcher_api_version'] = httpclient.DEFAULT_VER
self.http_client = httpclient._construct_http_client(
endpoint, *args, **kwargs)
self.audit = v1.AuditManager(self.http_client)
self.audit_template = v1.AuditTemplateManager(self.http_client)
self.action = v1.ActionManager(self.http_client)
self.action_plan = v1.ActionPlanManager(self.http_client)
self.goal = v1.GoalManager(self.http_client)
self.scoring_engine = v1.ScoringEngineManager(self.http_client)
self.service = v1.ServiceManager(self.http_client)
self.strategy = v1.StrategyManager(self.http_client)
# self.metric_collector = v1.MetricCollectorManager(self.http_client)
def build_http_client(self, *args, **kwargs):
return http._construct_http_client(*args, **kwargs)

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Red Hat, Inc.
#

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
@@ -80,7 +79,7 @@ class ListGoal(command.Lister):
dest='detail',
action='store_true',
default=False,
help=_("Show detailed information about metric collectors."))
help=_("Show detailed information about each goal."))
parser.add_argument(
'--limit',
metavar='<limit>',

View File

@@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Red Hat, Inc.
#
# 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 watcherclient.common import base
from watcherclient.common import utils
from watcherclient import exceptions as exc
CREATION_ATTRIBUTES = ['endpoint', 'category']
class MetricCollector(base.Resource):
def __repr__(self):
return "<MetricCollector %s>" % self._info
class MetricCollectorManager(base.Manager):
resource_class = MetricCollector
@staticmethod
def _path(id=None):
return \
'/v1/metric-collectors/%s' % id if id else '/v1/metric-collectors'
def list(self, category=None, limit=None, sort_key=None,
sort_dir=None, detail=False):
"""Retrieve a list of metric collector.
:param category: Optional, Metric category, to get all metric
collectors mapped with this category.
:param limit: The maximum number of results to return per
request, if:
1) limit > 0, the maximum number of metric collectors to return.
2) limit == 0, return the entire list of metriccollectors.
3) limit param is NOT specified (None), the number of items
returned respect the maximum imposed by the Watcher API
(see Watcher's api.max_limit option).
:param sort_key: Optional, field used for sorting.
:param sort_dir: Optional, direction of sorting, either 'asc' (the
default) or 'desc'.
:param detail: Optional, boolean whether to return detailed information
about metric collectors.
:returns: A list of metric collectors.
"""
if limit is not None:
limit = int(limit)
filters = utils.common_filters(limit, sort_key, sort_dir)
if category is not None:
filters.append('category=%s' % category)
path = ''
if detail:
path += 'detail'
if filters:
path += '?' + '&'.join(filters)
if limit is None:
return self._list(self._path(path), "metric-collectors")
else:
return self._list_pagination(self._path(path), "metric-collectors",
limit=limit)
def get(self, metric_collector_id):
try:
return self._list(self._path(metric_collector_id))[0]
except IndexError:
return None
def create(self, **kwargs):
new = {}
for (key, value) in kwargs.items():
if key in CREATION_ATTRIBUTES:
new[key] = value
else:
raise exc.InvalidAttribute()
return self._create(self._path(), new)
def delete(self, metric_collector_id):
return self._delete(self._path(metric_collector_id))
def update(self, metric_collector_id, patch):
return self._update(self._path(metric_collector_id), patch)

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2015 b<>com
# All Rights Reserved.
@@ -19,13 +18,11 @@
# Audit Template
AUDIT_TEMPLATE_FIELDS = [
'uuid', 'created_at', 'updated_at', 'deleted_at',
'description', 'host_aggregate', 'name',
'extra', 'goal_name', 'strategy_name']
'description', 'name', 'goal_name', 'strategy_name', 'scope']
AUDIT_TEMPLATE_FIELD_LABELS = [
'UUID', 'Created At', 'Updated At', 'Deleted At',
'Description', 'Host Aggregate ID or Name', 'Name',
'Extra', 'Goal', 'Strategy']
'Description', 'Name', 'Goal', 'Strategy', 'Audit Scope']
AUDIT_TEMPLATE_SHORT_LIST_FIELDS = [
'uuid', 'name', 'goal_name', 'strategy_name']
@@ -34,20 +31,19 @@ AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Goal', 'Strategy']
# Audit
AUDIT_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at',
'deadline', 'state', 'audit_type',
'parameters', 'interval',
'host_aggregate', 'goal_name', 'strategy_name']
'state', 'audit_type', 'parameters', 'interval', 'goal_name',
'strategy_name', 'scope', 'auto_trigger']
AUDIT_FIELD_LABELS = ['UUID', 'Created At', 'Updated At', 'Deleted At',
'Deadline', 'State', 'Audit Type',
'Parameters', 'Interval', 'Host Aggregate ID or Name',
'Goal', 'Strategy']
'State', 'Audit Type', 'Parameters', 'Interval', 'Goal',
'Strategy', 'Audit Scope', 'Auto Trigger']
AUDIT_SHORT_LIST_FIELDS = ['uuid', 'audit_type',
'state', 'goal_name', 'strategy_name']
'state', 'goal_name', 'strategy_name',
'auto_trigger']
AUDIT_SHORT_LIST_FIELD_LABELS = ['UUID', 'Audit Type', 'State', 'Goal',
'Strategy']
'Strategy', 'Auto Trigger']
# Action Plan
ACTION_PLAN_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at',
@@ -65,18 +61,18 @@ ACTION_PLAN_SHORT_LIST_FIELD_LABELS = ['UUID', 'Audit', 'State',
'Updated At', 'Global efficacy']
# Action
ACTION_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at', 'next_uuid',
ACTION_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at', 'parents',
'state', 'action_plan_uuid', 'action_type',
'input_parameters']
ACTION_FIELD_LABELS = ['UUID', 'Created At', 'Updated At', 'Deleted At',
'Next Action', 'State', 'Action Plan', 'Action',
'Parents', 'State', 'Action Plan', 'Action',
'Parameters']
ACTION_SHORT_LIST_FIELDS = ['uuid', 'next_uuid',
ACTION_SHORT_LIST_FIELDS = ['uuid', 'parents',
'state', 'action_plan_uuid', 'action_type']
ACTION_SHORT_LIST_FIELD_LABELS = ['UUID', 'Next Action', 'State',
ACTION_SHORT_LIST_FIELD_LABELS = ['UUID', 'Parents', 'State',
'Action Plan', 'Action']
# Goals
@@ -118,3 +114,9 @@ SCORING_ENGINE_FIELD_LABELS = ['UUID', 'Name', 'Description', 'Metainfo']
SCORING_ENGINE_SHORT_LIST_FIELDS = ['uuid', 'name', 'description']
SCORING_ENGINE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Description']
# Services
SERVICE_FIELDS = ['id', 'name', 'host', 'status', 'last_seen_up']
SERVICE_FIELD_LABELS = ['ID', 'Name', 'Host', 'Status', 'Last seen up']
SERVICE_SHORT_LIST_FIELDS = ['id', 'name', 'host', 'status']
SERVICE_SHORT_LIST_FIELD_LABELS = ['ID', 'Name', 'Host', 'Status']

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 Intel
#

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 Intel
#
# Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -0,0 +1,77 @@
#
# Copyright 2016 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 watcherclient.common import base
from watcherclient.common import utils
class Service(base.Resource):
def __repr__(self):
return "<Service %s>" % self._info
class ServiceManager(base.Manager):
resource_class = Service
@staticmethod
def _path(service=None):
return ('/v1/services/%s' % service
if service else '/v1/services')
def list(self, limit=None, sort_key=None, sort_dir=None, detail=False):
"""Retrieve a list of services.
:param limit: The maximum number of results to return per
request, if:
1) limit > 0, the maximum number of services to return.
2) limit == 0, return the entire list of services.
3) limit param is NOT specified (None), the number of items
returned respect the maximum imposed by the Watcher API
(see Watcher's api.max_limit option).
:param sort_key: Optional, field used for sorting.
:param sort_dir: Optional, direction of sorting, either 'asc' (the
default) or 'desc'.
:param detail: Optional, boolean whether to return detailed information
about services.
:returns: A list of services.
"""
if limit is not None:
limit = int(limit)
filters = utils.common_filters(limit, sort_key, sort_dir)
path = ''
if detail:
path += 'detail'
if filters:
path += '?' + '&'.join(filters)
if limit is None:
return self._list(self._path(path), "services")
else:
return self._list_pagination(self._path(path), "services",
limit=limit)
def get(self, service):
try:
return self._list(self._path(service))[0]
except IndexError:
return None

View File

@@ -0,0 +1,102 @@
# Copyright (c) 2016 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 osc_lib import utils
from watcherclient._i18n import _
from watcherclient.common import command
from watcherclient.common import utils as common_utils
from watcherclient import exceptions
from watcherclient.v1 import resource_fields as res_fields
class ShowService(command.ShowOne):
"""Show detailed information about a given service."""
def get_parser(self, prog_name):
parser = super(ShowService, self).get_parser(prog_name)
parser.add_argument(
'service',
metavar='<service>',
help=_('ID or name of the service'),
)
return parser
def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim")
try:
service = client.service.get(parsed_args.service)
except exceptions.HTTPNotFound as exc:
raise exceptions.CommandError(str(exc))
columns = res_fields.SERVICE_FIELDS
column_headers = res_fields.SERVICE_FIELD_LABELS
return column_headers, utils.get_item_properties(service, columns)
class ListService(command.Lister):
"""List information on retrieved services."""
def get_parser(self, prog_name):
parser = super(ListService, self).get_parser(prog_name)
parser.add_argument(
'--detail',
dest='detail',
action='store_true',
default=False,
help=_("Show detailed information about each service."))
parser.add_argument(
'--limit',
metavar='<limit>',
type=int,
help=_('Maximum number of services to return per request, '
'0 for no limit. Default is the maximum number used '
'by the Watcher API Service.'))
parser.add_argument(
'--sort-key',
metavar='<field>',
help=_('Goal field that will be used for sorting.'))
parser.add_argument(
'--sort-dir',
metavar='<direction>',
choices=['asc', 'desc'],
help='Sort direction: "asc" (the default) or "desc".')
return parser
def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim")
params = {}
if parsed_args.detail:
fields = res_fields.SERVICE_FIELDS
field_labels = res_fields.SERVICE_FIELD_LABELS
else:
fields = res_fields.SERVICE_SHORT_LIST_FIELDS
field_labels = res_fields.SERVICE_SHORT_LIST_FIELD_LABELS
params.update(
common_utils.common_params_for_list(
parsed_args, fields, field_labels))
try:
data = client.service.list(**params)
except exceptions.HTTPNotFound as ex:
raise exceptions.CommandError(str(ex))
return (field_labels,
(utils.get_item_properties(item, fields) for item in data))

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 Red Hat, Inc.
#

View File

@@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# Licensed under the Apache License, Version 2.0 (the "License");