Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3597c6a7bd | |||
| e7b9945933 | |||
| a08c0fde6f | |||
|
|
e227f82fd6 | ||
| 765701da39 | |||
|
|
dc9b5cb347 | ||
|
|
cf18f56c6f | ||
|
|
f2f882e297 | ||
|
|
add4254a3f | ||
|
|
983a347341 | ||
|
|
6a4937d18d | ||
|
|
d0f41996ff | ||
|
|
52a3fd062d | ||
|
|
c97e16fd01 | ||
|
|
6a33b44d7f | ||
|
|
41feafc420 | ||
| e1bcc5f758 | |||
|
|
80f728b14f | ||
|
|
2e97950670 | ||
|
|
21b106dc15 | ||
|
|
7213f7195e | ||
|
|
21f42884f1 | ||
|
|
4c57d61f8d | ||
|
|
e05b498a5f | ||
|
|
8d3ce6c58d | ||
|
|
bb7af48350 | ||
|
|
92c15fa38a | ||
|
|
d3cc035ff7 | ||
|
|
fb22d3e1f8 | ||
| ac0ccb67a6 | |||
|
|
5946fbb5b1 | ||
|
|
2830afdc75 | ||
|
|
1912b9aa82 | ||
|
|
1f9194ced5 | ||
|
|
9387369cd7 | ||
|
|
431319ee4d | ||
|
|
e3c903cfce | ||
|
|
e3f0a3d2b5 | ||
|
|
88351df1c2 | ||
|
|
df3ec7849e | ||
|
|
0c7e306642 | ||
|
|
db70063cee | ||
|
|
1d1f28a256 | ||
|
|
62fdb73dae | ||
|
|
297ca1dfc5 | ||
|
|
a8f919e1f7 | ||
|
|
ea74a22b49 | ||
|
|
3caa385548 | ||
|
|
0e9c827c76 | ||
|
|
dbbe7df1e2 | ||
|
|
be479feef9 | ||
|
|
02583f7469 | ||
|
|
1aebe93477 | ||
|
|
cde518be31 | ||
|
|
573ca6ed59 | ||
|
|
f062e60990 | ||
|
|
c0b2604e9b | ||
|
|
2cab116e30 | ||
|
|
bb5586b477 | ||
|
|
c30f095481 | ||
|
|
ffb71506aa | ||
|
|
32a8799516 | ||
|
|
b52df6899d | ||
|
|
103bb38475 | ||
|
|
76a01b0c7c | ||
|
|
939371d180 | ||
|
|
c62757b093 | ||
|
|
e2082907ff | ||
|
|
16ff74f54c | ||
|
|
de9b8d124b | ||
|
|
a25cd66052 | ||
|
|
9b1e234e36 | ||
|
|
512e43eb32 | ||
|
|
d0f2f10f5d | ||
|
|
8342e64051 | ||
|
|
2c29c49f24 | ||
|
|
a70ebd8a41 | ||
|
|
4db175f342 | ||
|
|
775199cb42 | ||
|
|
9d9c5fe3be | ||
|
|
e4ee4ff03c | ||
|
|
ca6190c3a1 | ||
|
|
74cbf1b17b | ||
|
|
4a1b6eaf81 | ||
|
|
1186c7c34f | ||
|
|
02498f85f6 | ||
|
|
9d1d5e2cae | ||
|
|
b0ac926ee6 | ||
|
|
3e734b177d | ||
|
|
2cfe7f9afc | ||
|
|
df57eb800d | ||
|
|
abea1a8815 | ||
|
|
ab22110f7b | ||
|
|
ec7e8ca803 | ||
|
|
bdd99d60f3 | ||
|
|
25ff2811d5 | ||
|
|
8ce78f8c1f | ||
|
|
ffc50c5001 | ||
|
|
6c66276040 | ||
|
|
c256ee2a21 | ||
|
|
7abd55a823 | ||
|
|
98e09044c1 | ||
|
|
6c8dfd366d | ||
|
|
112e372239 | ||
|
|
c5b4db7fc1 | ||
|
|
7c6b39eeba | ||
|
|
94f6864bf0 | ||
|
|
e391d53c2f | ||
|
|
b7928eb155 | ||
|
|
7b7d07fac1 | ||
|
|
946ef06dc6 | ||
|
|
69f0493968 | ||
|
|
8c89b3327b | ||
|
|
2e9de6868b | ||
|
|
db7a5e4bc9 | ||
|
|
825e5cb081 | ||
|
|
a8185c7067 | ||
|
|
4a0b0e76b4 | ||
|
|
1918d3f26b | ||
|
|
2ca6b6b421 | ||
|
|
1086b23454 | ||
|
|
535b9da7d2 | ||
|
|
34a4ad3041 | ||
|
|
39d4c1e944 | ||
|
|
c1fd569b76 | ||
|
|
eed22ea2bb | ||
|
|
47241e0462 | ||
|
|
88d122a21f | ||
|
|
5e928b80be | ||
|
|
723ad12afa | ||
|
|
3926dcc541 | ||
|
|
c557e45ae2 | ||
|
|
0b8dba2cb9 | ||
|
|
496f4c1365 | ||
|
|
6681724daa | ||
|
|
2551ff0934 | ||
|
|
adc812746c | ||
|
|
8d62995cf4 | ||
|
|
d82be4c8c4 | ||
|
|
152804168d | ||
|
|
4a4add3dcc | ||
|
|
db012f1f8f | ||
|
|
a4c020c19f | ||
|
|
8ef72f18c4 |
11
.coveragerc
Normal file
11
.coveragerc
Normal file
@@ -0,0 +1,11 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = watcherclient
|
||||
omit =
|
||||
watcherclient/tests/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
||||
exclude_lines =
|
||||
@abc.abstract
|
||||
raise NotImplementedError
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -23,11 +23,12 @@ lib64
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.coverage*
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
.stestr/
|
||||
.venv
|
||||
.testrepository/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
@@ -56,6 +57,10 @@ ChangeLog
|
||||
|
||||
sftp-config.json
|
||||
/.idea/
|
||||
/cover/
|
||||
|
||||
# Desktop Service Store
|
||||
*.DS_Store
|
||||
|
||||
# Atom
|
||||
.remote-sync.json
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
host=review.opendev.org
|
||||
port=29418
|
||||
project=openstack/python-watcherclient.git
|
||||
defaultbranch=unmaintained/zed
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[DEFAULT]
|
||||
test_path=${OS_TEST_PATH:-./watcherclient/tests/functional}
|
||||
top_dir=./
|
||||
test_path=./watcherclient/tests/unit
|
||||
top_dir=./
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-160} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./watcherclient/tests} $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
||||
11
.zuul.yaml
11
.zuul.yaml
@@ -1,9 +1,10 @@
|
||||
- project:
|
||||
templates:
|
||||
- openstack-cover-jobs
|
||||
- openstack-python3-zed-jobs
|
||||
- publish-openstack-docs-pti
|
||||
- check-requirements
|
||||
- openstackclient-plugin-jobs
|
||||
check:
|
||||
jobs:
|
||||
- watcherclient-tempest-functional
|
||||
- openstack-tox-lower-constraints
|
||||
gate:
|
||||
jobs:
|
||||
- watcherclient-tempest-functional
|
||||
- openstack-tox-lower-constraints
|
||||
|
||||
@@ -24,7 +24,7 @@ migration, increased energy efficiency and more!
|
||||
|
||||
* Free software: Apache license
|
||||
* Wiki: https://wiki.openstack.org/wiki/Watcher
|
||||
* Source: https://git.openstack.org/cgit/openstack/python-watcher
|
||||
* Source: https://opendev.org/openstack/python-watcherclient
|
||||
* Bugs: https://bugs.launchpad.net/watcher
|
||||
|
||||
Installation
|
||||
|
||||
8
doc/requirements.txt
Normal file
8
doc/requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
openstackdocstheme>=2.2.1 # Apache-2.0
|
||||
sphinx>=2.0.0,!=2.1.0 # BSD
|
||||
sphinxcontrib-apidoc>=0.2.0 # BSD
|
||||
|
||||
@@ -136,7 +136,7 @@ watcher optional arguments
|
||||
Defaults to ``env[OS_AUTH_TOKEN]``.
|
||||
|
||||
``--os-watcher-api-version <os-watcher-api-version>``
|
||||
Defaults to ``env[OS_WATCHER_API_VERSION]``.
|
||||
Defaults to ``env[OS_INFRA_OPTIM_API_VERSION]``.
|
||||
|
||||
``--os-endpoint-type OS_ENDPOINT_TYPE``
|
||||
Defaults to ``env[OS_ENDPOINT_TYPE]`` or "publicURL"
|
||||
@@ -157,7 +157,7 @@ watcher action list
|
||||
[--quote {all,minimal,none,nonnumeric}]
|
||||
[--action-plan <action-plan>] [--audit <audit>]
|
||||
[--detail] [--limit <limit>] [--sort-key <field>]
|
||||
[--sort-dir <direction>]
|
||||
[--sort-dir <direction>] [--marker <marker>]
|
||||
|
||||
List information on retrieved actions.
|
||||
|
||||
@@ -186,6 +186,10 @@ List information on retrieved actions.
|
||||
``--sort-dir <direction>``
|
||||
Sort direction: "asc" (the default) or "desc".
|
||||
|
||||
``--marker <marker>``
|
||||
UUID of the last action in the previous page; displays
|
||||
list of actions after "marker".
|
||||
|
||||
.. _watcher_action_show:
|
||||
|
||||
watcher action show
|
||||
@@ -295,7 +299,8 @@ watcher actionplan list
|
||||
[--fit-width] [--print-empty] [--noindent]
|
||||
[--quote {all,minimal,none,nonnumeric}]
|
||||
[--audit <audit>] [--detail] [--limit <limit>]
|
||||
[--sort-key <field>] [--sort-dir <direction>]
|
||||
[--marker <actionplan>] [--sort-key <field>]
|
||||
[--sort-dir <direction>]
|
||||
|
||||
List information on retrieved action plans.
|
||||
|
||||
@@ -315,6 +320,10 @@ List information on retrieved action plans.
|
||||
0 for no limit. Default is the maximum number used by
|
||||
the Watcher API Service.
|
||||
|
||||
``--marker <actionplan>``
|
||||
The last actionplan UUID of the previous page;
|
||||
displays list of actionplans after "marker".
|
||||
|
||||
``--sort-key <field>``
|
||||
Action Plan field that will be used for sorting.
|
||||
|
||||
@@ -466,7 +475,7 @@ Delete audit command.
|
||||
**Positional arguments:**
|
||||
|
||||
``<audit>``
|
||||
UUID of the audit
|
||||
UUID or name of the audit
|
||||
|
||||
**Optional arguments:**
|
||||
|
||||
@@ -532,7 +541,7 @@ Show detailed information about a given audit.
|
||||
**Positional arguments:**
|
||||
|
||||
``<audit>``
|
||||
UUID of the audit
|
||||
UUID or name of the audit
|
||||
|
||||
**Optional arguments:**
|
||||
|
||||
@@ -556,7 +565,7 @@ Update audit command.
|
||||
**Positional arguments:**
|
||||
|
||||
``<audit>``
|
||||
UUID of the audit.
|
||||
UUID or name of the audit.
|
||||
|
||||
``<op>``
|
||||
Operation: 'add', 'replace', or 'remove'.
|
||||
@@ -611,38 +620,43 @@ Create new audit template.
|
||||
``--scope <path>``
|
||||
Part of the cluster on which an audit will be done.
|
||||
Can be provided either in yaml or json file.
|
||||
YAML example:
|
||||
---- host_aggregates:
|
||||
- id: 1
|
||||
- id: 2
|
||||
- id: 3
|
||||
|
||||
YAML example:
|
||||
::
|
||||
|
||||
---
|
||||
- host_aggregates:
|
||||
- id: 1
|
||||
- id: 2
|
||||
- id: 3
|
||||
- availability_zones:
|
||||
- name: AZ1
|
||||
- name: AZ2
|
||||
- name: AZ1
|
||||
- name: AZ2
|
||||
- exclude:
|
||||
- instances:
|
||||
- uuid: UUID1
|
||||
- uuid: UUID2
|
||||
- compute_nodes:
|
||||
- name: compute1
|
||||
|
||||
JSON example:
|
||||
[{'host_aggregates': [
|
||||
{'id': 1},
|
||||
{'id': 2},
|
||||
{'id': 3}]},
|
||||
{'availability_zones': [
|
||||
{'name': 'AZ1'},
|
||||
{'name': 'AZ2'}]},
|
||||
{'exclude': [
|
||||
{'instances': [
|
||||
{'uuid': 'UUID1'},
|
||||
{'uuid': 'UUID2'}
|
||||
]},
|
||||
{'compute_nodes': [
|
||||
{'name': 'compute1'}
|
||||
]}
|
||||
]}]
|
||||
- instances:
|
||||
- uuid: UUID1
|
||||
- uuid: UUID2
|
||||
- compute_nodes:
|
||||
- name: compute1
|
||||
|
||||
JSON example:
|
||||
::
|
||||
|
||||
[
|
||||
{'host_aggregates': [
|
||||
{'id': 1},
|
||||
{'id': 2},
|
||||
{'id': 3}]},
|
||||
{'availability_zones': [
|
||||
{'name': 'AZ1'},
|
||||
{'name': 'AZ2'}]},
|
||||
{'exclude': [
|
||||
{'instances': [
|
||||
{'uuid': 'UUID1'},
|
||||
{'uuid': 'UUID2'}]},
|
||||
{'compute_nodes': [
|
||||
{'name': 'compute1'}]}]}
|
||||
]
|
||||
|
||||
.. _watcher_audittemplate_delete:
|
||||
|
||||
@@ -680,7 +694,7 @@ watcher audittemplate list
|
||||
[--detail] [--goal <goal>]
|
||||
[--strategy <strategy>] [--limit <limit>]
|
||||
[--sort-key <field>]
|
||||
[--sort-dir <direction>]
|
||||
[--sort-dir <direction>][--marker <marker>]
|
||||
|
||||
List information on retrieved audit templates.
|
||||
|
||||
@@ -709,6 +723,10 @@ List information on retrieved audit templates.
|
||||
``--sort-dir <direction>``
|
||||
Sort direction: "asc" (the default) or "desc".
|
||||
|
||||
``--marker <marker>``
|
||||
UUID of the last audittemplate in the previous page; displays
|
||||
list of audittemplates after "marker".
|
||||
|
||||
.. _watcher_audittemplate_show:
|
||||
|
||||
watcher audittemplate show
|
||||
@@ -781,7 +799,7 @@ watcher goal list
|
||||
[--print-empty] [--noindent]
|
||||
[--quote {all,minimal,none,nonnumeric}] [--detail]
|
||||
[--limit <limit>] [--sort-key <field>]
|
||||
[--sort-dir <direction>]
|
||||
[--sort-dir <direction>][--marker <marker>]
|
||||
|
||||
List information on retrieved goals.
|
||||
|
||||
@@ -804,6 +822,10 @@ List information on retrieved goals.
|
||||
``--sort-dir <direction>``
|
||||
Sort direction: "asc" (the default) or "desc".
|
||||
|
||||
``--marker <marker>``
|
||||
UUID of the last goal in the previous page; displays
|
||||
list of goals after "marker".
|
||||
|
||||
.. _watcher_goal_show:
|
||||
|
||||
watcher goal show
|
||||
@@ -841,7 +863,7 @@ watcher scoringengine list
|
||||
[--quote {all,minimal,none,nonnumeric}]
|
||||
[--detail] [--limit <limit>]
|
||||
[--sort-key <field>]
|
||||
[--sort-dir <direction>]
|
||||
[--sort-dir <direction>][--marker <marker>]
|
||||
|
||||
List information on retrieved scoring engines.
|
||||
|
||||
@@ -864,6 +886,10 @@ List information on retrieved scoring engines.
|
||||
``--sort-dir <direction>``
|
||||
Sort direction: "asc" (the default) or "desc".
|
||||
|
||||
``--marker <marker>``
|
||||
UUID of the last scoringengine in the previous page; displays
|
||||
list of scoringengines after "marker".
|
||||
|
||||
.. _watcher_scoringengine_show:
|
||||
|
||||
watcher scoringengine show
|
||||
@@ -948,6 +974,40 @@ Show detailed information about a given service.
|
||||
``-h, --help``
|
||||
show this help message and exit
|
||||
|
||||
.. _watcher_datamodel_list:
|
||||
|
||||
watcher datamodel list
|
||||
----------------------
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
usage: watcher datamodel list [-h] [-f {csv,json,table,value,yaml}]
|
||||
[-c COLUMN] [--max-width <integer>]
|
||||
[--fit-width] [--print-empty] [--noindent]
|
||||
[--quote {all,minimal,none,nonnumeric}]
|
||||
[--sort-column SORT_COLUMN] [--type <type>]
|
||||
[--audit <audit>] [--detail]
|
||||
|
||||
|
||||
List information on retrieved data model.
|
||||
|
||||
**Optional arguments:**
|
||||
|
||||
``-h, --help``
|
||||
show this help message and exit
|
||||
|
||||
``--type <type>``
|
||||
Type of Datamodel user want to list. Supported values:
|
||||
compute. Future support values: storage, baremetal.
|
||||
Default type is compute.
|
||||
|
||||
``--audit <audit>``
|
||||
UUID of the audit. used to filter data model
|
||||
by the scope in audit.
|
||||
|
||||
``--detail``
|
||||
Show detailed information about data model.
|
||||
|
||||
.. _watcher_strategy_list:
|
||||
|
||||
watcher strategy list
|
||||
@@ -961,6 +1021,7 @@ watcher strategy list
|
||||
[--quote {all,minimal,none,nonnumeric}]
|
||||
[--goal <goal>] [--detail] [--limit <limit>]
|
||||
[--sort-key <field>] [--sort-dir <direction>]
|
||||
[--marker <marker>]
|
||||
|
||||
List information on retrieved strategies.
|
||||
|
||||
@@ -986,6 +1047,10 @@ List information on retrieved strategies.
|
||||
``--sort-dir <direction>``
|
||||
Sort direction: "asc" (the default) or "desc".
|
||||
|
||||
``--marker <marker>``
|
||||
UUID of the last strategy in the previous page; displays
|
||||
list of strategies after "marker".
|
||||
|
||||
.. _watcher_strategy_show:
|
||||
|
||||
watcher strategy show
|
||||
@@ -1010,3 +1075,28 @@ Show detailed information about a given strategy.
|
||||
``-h, --help``
|
||||
show this help message and exit
|
||||
|
||||
.. _watcher_strategy_state:
|
||||
|
||||
watcher strategy state
|
||||
----------------------
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
usage: watcher strategy state [-h] [-f {csv,html,json,table,value,yaml}]
|
||||
[-c COLUMN] [--max-width <integer>]
|
||||
[--fit-width] [--print-empty] [--noindent]
|
||||
[--quote {all,minimal,none,nonnumeric}]
|
||||
[--sort-column SORT_COLUMN]
|
||||
<strategy>
|
||||
|
||||
Retrieve information about strategy requirements.
|
||||
|
||||
**Positional arguments:**
|
||||
|
||||
``<strategy>``
|
||||
Name of the strategy
|
||||
|
||||
**Optional arguments:**
|
||||
|
||||
``-h, --help``
|
||||
show this help message and exit
|
||||
|
||||
@@ -11,7 +11,7 @@ variables::
|
||||
export OS_USERNAME=user
|
||||
export OS_PASSWORD=pass
|
||||
export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b
|
||||
export OS_AUTH_URL=http://auth.example.com:5000/v2.0
|
||||
export OS_AUTH_URL=http://auth.example.com:5000/v3/
|
||||
|
||||
The command line tool will attempt to reauthenticate using your
|
||||
provided credentials for every request. You can override this behavior
|
||||
|
||||
@@ -33,7 +33,7 @@ environment variables::
|
||||
$ export OS_PASSWORD=password
|
||||
$ export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b # or OS_TENANT_NAME
|
||||
$ export OS_TENANT_NAME=project # or OS_TENANT_ID
|
||||
$ export OS_AUTH_URL=http://auth.example.com:5000/v2.0
|
||||
$ export OS_AUTH_URL=http://auth.example.com:5000/v3/
|
||||
|
||||
The command-line tool will attempt to reauthenticate using the provided
|
||||
credentials for every request. You can override this behavior by manually
|
||||
|
||||
@@ -32,7 +32,7 @@ environment variables::
|
||||
$ export OS_PASSWORD=password
|
||||
$ export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b # or OS_TENANT_NAME
|
||||
$ export OS_TENANT_NAME=project # or OS_TENANT_ID
|
||||
$ export OS_AUTH_URL=http://auth.example.com:5000/v2.0
|
||||
$ export OS_AUTH_URL=http://auth.example.com:5000/v3/
|
||||
|
||||
The command-line tool will attempt to reauthenticate using the provided
|
||||
credentials for every request. You can override this behavior by manually
|
||||
@@ -52,7 +52,7 @@ environment variable. (It defaults to the first in the list returned.)
|
||||
Watcher CLI supports bash completion. The command-line tool can automatically
|
||||
fill partially typed commands. To use this feature, source the below file
|
||||
(available at
|
||||
https://git.openstack.org/cgit/openstack/python-watcherclient/tree/tools/watcher.bash_completion)
|
||||
https://opendev.org/openstack/python-watcherclient/src/branch/master/tools/watcher.bash_completion)
|
||||
to your terminal and then bash completion should work::
|
||||
|
||||
$ . watcher.bash_completion
|
||||
|
||||
@@ -12,20 +12,26 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from watcherclient import version as watcherclient_version
|
||||
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinxcontrib.apidoc',
|
||||
'openstackdocstheme',
|
||||
]
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# sphinxcontrib.apidoc options
|
||||
apidoc_module_dir = '../../watcherclient'
|
||||
apidoc_output_dir = 'reference/api'
|
||||
apidoc_excluded_paths = [
|
||||
'tests/*']
|
||||
apidoc_separate_modules = True
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
@@ -36,18 +42,8 @@ source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'python-watcherclient'
|
||||
copyright = u'OpenStack Foundation'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = watcherclient_version.version_info.release_string()
|
||||
# The short X.Y version.
|
||||
version = watcherclient_version.version_info.version_string()
|
||||
project = 'python-watcherclient'
|
||||
copyright = 'OpenStack Foundation'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
modindex_common_prefix = ['watcherclient.']
|
||||
@@ -60,7 +56,7 @@ add_function_parentheses = True
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = 'native'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
@@ -83,17 +79,27 @@ latex_documents = [
|
||||
(
|
||||
'index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'
|
||||
'%s Documentation' % project,
|
||||
'OpenStack Foundation', 'manual'
|
||||
),
|
||||
]
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack/python-watcherclient'
|
||||
bug_project = 'python-watcherclient'
|
||||
bug_tag = ''
|
||||
# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664
|
||||
latex_use_xindy = False
|
||||
|
||||
latex_domain_indices = False
|
||||
|
||||
latex_elements = {
|
||||
'makeindex': '',
|
||||
'printindex': '',
|
||||
'preamble': r'\setcounter{tocdepth}{3}',
|
||||
}
|
||||
|
||||
# openstackdocstheme options
|
||||
openstackdocs_repo_name = 'openstack/python-watcherclient'
|
||||
openstackdocs_pdf_link = True
|
||||
openstackdocs_bug_project = 'python-watcherclient'
|
||||
openstackdocs_bug_tag = ''
|
||||
|
||||
# Must set this variable to include year, month, day, hours, and minutes.
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
#html_theme_options = {"show_other_versions": "True"}
|
||||
|
||||
@@ -44,11 +44,11 @@ Bug tracker
|
||||
https://launchpad.net/python-watcherclient
|
||||
|
||||
Mailing list (prefix subjects with ``[watcher]`` for faster responses)
|
||||
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
|
||||
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
|
||||
|
||||
Code Hosting
|
||||
https://git.openstack.org/cgit/openstack/python-watcherclient
|
||||
https://opendev.org/openstack/python-watcherclient
|
||||
|
||||
Code Review
|
||||
https://review.openstack.org/#/q/status:open+project:openstack/python-watcherclient,n,z
|
||||
https://review.opendev.org/#/q/status:open+project:openstack/python-watcherclient,n,z
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@ Installation
|
||||
If you have `virtualenvwrapper <https://virtualenvwrapper.readthedocs.org/en/latest/install.html>`_ installed::
|
||||
|
||||
$ mkvirtualenv python-watcherclient
|
||||
$ git clone https://git.openstack.org/openstack/python-watcherclient
|
||||
$ git clone https://opendev.org/openstack/python-watcherclient
|
||||
$ cd python-watcherclient && python setup.py install
|
||||
$ pip install -r ./requirements.txt
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
======================
|
||||
Python API Reference
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
autoindex
|
||||
@@ -71,8 +71,7 @@ Once you have an watcher `Client`_, you can perform various tasks::
|
||||
>>> watcher.audit.get(audit_uuid_or_name) # information about a particular audit
|
||||
|
||||
When the `Client`_ needs to propagate an exception, it will usually
|
||||
raise an instance subclassed from
|
||||
`watcherclient.exc.BaseException`_ or `watcherclient.exc.ClientException`_.
|
||||
raise an instance listed in `watcherclient.exceptions`_.
|
||||
|
||||
Refer to the modules themselves, for more details.
|
||||
|
||||
@@ -84,5 +83,4 @@ watcherclient Modules
|
||||
.. _watcherclient.v1.client.Client: api/watcherclient.v1.client.html#watcherclient.v1.client.Client
|
||||
.. _Client: api/watcherclient.v1.client.html#watcherclient.v1.client.Client
|
||||
.. _watcherclient.client.get_client(): api/watcherclient.client.html#watcherclient.client.get_client
|
||||
.. _watcherclient.exc.BaseException: api/watcherclient.exc.html#watcherclient.exc.BaseException
|
||||
.. _watcherclient.exc.ClientException: api/watcherclient.exc.html#watcherclient.exc.ClientException
|
||||
.. _watcherclient.exceptions: api/watcherclient.exceptions.html
|
||||
|
||||
@@ -10,5 +10,5 @@ done so, you can use the API like so.
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api/index
|
||||
api/modules
|
||||
api_v1
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
alabaster==0.7.10
|
||||
appdirs==1.4.3
|
||||
asn1crypto==0.23.0
|
||||
Babel==2.5.3
|
||||
certifi==2018.1.18
|
||||
cffi==1.7.0
|
||||
cffi==1.14.0
|
||||
chardet==3.0.4
|
||||
cliff==2.11.0
|
||||
cmd2==0.8.2
|
||||
coverage==4.0
|
||||
cryptography==2.1
|
||||
cryptography==2.7
|
||||
debtcollector==1.19.0
|
||||
decorator==4.2.1
|
||||
deprecation==2.0
|
||||
@@ -19,7 +18,6 @@ extras==1.0.0
|
||||
fasteners==0.7.0
|
||||
fixtures==3.0.0
|
||||
flake8==2.5.5
|
||||
future==0.16.0
|
||||
hacking==0.12.0
|
||||
idna==2.6
|
||||
imagesize==0.7.1
|
||||
@@ -33,14 +31,11 @@ keystoneauth1==3.4.0
|
||||
linecache2==1.0.0
|
||||
MarkupSafe==1.0
|
||||
mccabe==0.2.1
|
||||
mock==2.0.0
|
||||
monotonic==1.4
|
||||
mox3==0.20.0
|
||||
msgpack-python==0.4.0
|
||||
munch==2.2.0
|
||||
netaddr==0.7.19
|
||||
netifaces==0.10.6
|
||||
openstackdocstheme==1.18.1
|
||||
openstacksdk==0.12.0
|
||||
os-client-config==1.29.0
|
||||
os-service-types==1.2.0
|
||||
@@ -58,7 +53,6 @@ packaging==17.1
|
||||
paramiko==2.0.0
|
||||
pbr==3.1.1
|
||||
pep8==1.5.7
|
||||
prettytable==0.7.2
|
||||
pyasn1==0.1.8
|
||||
pycparser==2.18
|
||||
pyflakes==0.8.1
|
||||
@@ -69,21 +63,17 @@ pyperclip==1.6.0
|
||||
python-dateutil==2.5.3
|
||||
python-mimeparse==1.6.0
|
||||
python-subunit==1.0.0
|
||||
-e git://git.openstack.org/openstack/python-watcherclient@104894958882a4877dad6f469361d2adb41d0b59#egg=python_watcherclient
|
||||
pytz==2018.3
|
||||
PyYAML==3.12
|
||||
PyYAML==3.13
|
||||
requests==2.18.4
|
||||
requestsexceptions==1.4.0
|
||||
rfc3986==0.3.1
|
||||
simplejson==3.13.2
|
||||
six==1.11.0
|
||||
snowballstemmer==1.2.1
|
||||
Sphinx==1.6.5
|
||||
sphinxcontrib-websupport==1.0.1
|
||||
stestr==1.0.0
|
||||
stestr==2.0.0
|
||||
stevedore==1.28.0
|
||||
tempest==17.1.0
|
||||
testrepository==0.0.18
|
||||
testscenarios==0.4
|
||||
testtools==2.2.0
|
||||
traceback2==1.4.0
|
||||
|
||||
6
releasenotes/notes/drop-py-2-7-f7078b44cf99cae1.yaml
Normal file
6
releasenotes/notes/drop-py-2-7-f7078b44cf99cae1.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Python 2.7 support has been dropped. Last release of python-watcherclient
|
||||
to support py2.7 is OpenStack Train. The minimum version of Python now
|
||||
supported by python-watcherclient is Python 3.6.
|
||||
@@ -2,13 +2,12 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
cliff!=2.9.0,>=2.8.0 # Apache-2.0
|
||||
osc-lib>=1.8.0 # Apache-2.0
|
||||
oslo.i18n>=3.15.3 # Apache-2.0
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
PrettyTable<0.8,>=0.7.1 # BSD
|
||||
cliff!=2.9.0,>=2.11.0 # Apache-2.0
|
||||
osc-lib>=1.10.0 # Apache-2.0
|
||||
oslo.i18n>=3.20.0 # Apache-2.0
|
||||
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
|
||||
oslo.utils>=3.36.0 # Apache-2.0
|
||||
pbr!=2.1.0,>=3.1.1 # Apache-2.0
|
||||
keystoneauth1>=3.4.0 # Apache-2.0
|
||||
six>=1.10.0 # MIT
|
||||
PyYAML>=3.12 # MIT
|
||||
six>=1.11.0 # MIT
|
||||
PyYAML>=3.13 # MIT
|
||||
|
||||
44
setup.cfg
44
setup.cfg
@@ -1,11 +1,12 @@
|
||||
[metadata]
|
||||
name = python-watcherclient
|
||||
summary = Python client library for Watcher API
|
||||
description-file =
|
||||
description_file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = https://docs.openstack.org/python-watcherclient/latest/
|
||||
author_email = openstack-discuss@lists.openstack.org
|
||||
home_page = https://docs.openstack.org/python-watcherclient/latest/
|
||||
python_requires = >=3.8
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
@@ -13,10 +14,9 @@ classifier =
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.5
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
|
||||
[files]
|
||||
packages =
|
||||
@@ -25,8 +25,6 @@ packages =
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
watcher = watcherclient.shell:main
|
||||
tempest.test_plugins =
|
||||
watcherclient_tests = watcherclient.plugin:WatcherClientTempestPlugin
|
||||
|
||||
openstack.cli.extension =
|
||||
infra_optim = watcherclient.osc.plugin
|
||||
@@ -53,8 +51,8 @@ openstack.infra_optim.v1 =
|
||||
optimize_audit_delete = watcherclient.v1.audit_shell:DeleteAudit
|
||||
|
||||
optimize_actionplan_show = watcherclient.v1.action_plan_shell:ShowActionPlan
|
||||
optimize_actionplan_delete = watcherclient.v1.action_plan_shell:DeleteActionPlan
|
||||
optimize_actionplan_list = watcherclient.v1.action_plan_shell:ListActionPlan
|
||||
optimize_actionplan_create = watcherclient.v1.action_plan_shell:CreateActionPlan
|
||||
optimize_actionplan_update = watcherclient.v1.action_plan_shell:UpdateActionPlan
|
||||
optimize_actionplan_start = watcherclient.v1.action_plan_shell:StartActionPlan
|
||||
optimize_actionplan_cancel = watcherclient.v1.action_plan_shell:CancelActionPlan
|
||||
@@ -68,6 +66,8 @@ openstack.infra_optim.v1 =
|
||||
optimize_service_show = watcherclient.v1.service_shell:ShowService
|
||||
optimize_service_list = watcherclient.v1.service_shell:ListService
|
||||
|
||||
optimize_datamodel_list = watcherclient.v1.data_model_shell:ListDataModel
|
||||
|
||||
# The same as above but used by the 'watcher' command
|
||||
watcherclient.v1 =
|
||||
goal_show = watcherclient.v1.goal_shell:ShowGoal
|
||||
@@ -91,7 +91,6 @@ watcherclient.v1 =
|
||||
|
||||
actionplan_show = watcherclient.v1.action_plan_shell:ShowActionPlan
|
||||
actionplan_list = watcherclient.v1.action_plan_shell:ListActionPlan
|
||||
actionplan_create = watcherclient.v1.action_plan_shell:CreateActionPlan
|
||||
actionplan_update = watcherclient.v1.action_plan_shell:UpdateActionPlan
|
||||
actionplan_start = watcherclient.v1.action_plan_shell:StartActionPlan
|
||||
actionplan_delete = watcherclient.v1.action_plan_shell:DeleteActionPlan
|
||||
@@ -106,31 +105,10 @@ watcherclient.v1 =
|
||||
service_show = watcherclient.v1.service_shell:ShowService
|
||||
service_list = watcherclient.v1.service_shell:ListService
|
||||
|
||||
datamodel_list = watcherclient.v1.data_model_shell:ListDataModel
|
||||
|
||||
[pbr]
|
||||
autodoc_index_modules = True
|
||||
autodoc_exclude_modules =
|
||||
watcherclient.tests.*
|
||||
api_doc_dir = reference/api
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
warning-is-error = 1
|
||||
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
|
||||
[compile_catalog]
|
||||
directory = watcherclient/locale
|
||||
domain = watcherclient
|
||||
|
||||
[update_catalog]
|
||||
domain = watcherclient
|
||||
output_dir = watcherclient/locale
|
||||
input_file = watcherclient/locale/watcherclient.pot
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext _LI _LW _LE _LC
|
||||
mapping_file = babel.cfg
|
||||
output_file = watcherclient/locale/watcherclient.pot
|
||||
|
||||
9
setup.py
9
setup.py
@@ -13,17 +13,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
||||
|
||||
@@ -4,16 +4,10 @@
|
||||
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
mock>=2.0.0 # BSD
|
||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||
hacking>=3.0.1,<3.1.0 # Apache-2.0
|
||||
oslotest>=3.2.0 # Apache-2.0
|
||||
python-subunit>=1.0.0 # Apache-2.0/BSD
|
||||
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
stestr>=2.0.0 # Apache-2.0
|
||||
testscenarios>=0.4 # Apache-2.0/BSD
|
||||
testtools>=2.2.0 # MIT
|
||||
tempest>=17.1.0 # Apache-2.0
|
||||
|
||||
# Needed for pypi packaging
|
||||
wheel>=0.24.0 # MIT
|
||||
|
||||
80
tox.ini
80
tox.ini
@@ -1,70 +1,80 @@
|
||||
[tox]
|
||||
minversion = 1.8
|
||||
envlist = py35,py27,pep8
|
||||
minversion = 3.18.0
|
||||
envlist = py3,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} {opts} {packages}
|
||||
pip install -U {opts} {packages}
|
||||
passenv = ZUUL_CACHE_DIR
|
||||
REQUIREMENTS_PIP_LOCATION
|
||||
install_command = pip install {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
OS_TEST_PATH=./watcherclient/tests/unit
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
deps =
|
||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/zed}
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
-r{toxinidir}/requirements.txt
|
||||
allowlist_externals =
|
||||
rm
|
||||
commands = rm -f .testrepository/times.dbm
|
||||
python setup.py testr --slowest --testr-args='{posargs}'
|
||||
# The --test-path is defined in .stestr.conf
|
||||
stestr run {posargs}
|
||||
stestr slowest
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
commands = flake8
|
||||
|
||||
[testenv:venv]
|
||||
basepython = python3
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
basepython = python3
|
||||
setenv =
|
||||
PYTHON=coverage run --source watcherclient --parallel-mode
|
||||
commands =
|
||||
python setup.py testr --coverage --testr-args='{posargs}'
|
||||
coverage report
|
||||
stestr run {posargs}
|
||||
coverage combine
|
||||
coverage html -d cover
|
||||
coverage xml -o cover/coverage.xml
|
||||
coverage report
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
basepython = python3
|
||||
deps =
|
||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/zed}
|
||||
-r{toxinidir}/doc/requirements.txt
|
||||
commands = sphinx-build -W -b html doc/source doc/build/html
|
||||
|
||||
|
||||
[testenv:pdf-docs]
|
||||
basepython = python3
|
||||
envdir = {toxworkdir}/docs
|
||||
deps = {[testenv:docs]deps}
|
||||
allowlist_externals =
|
||||
rm
|
||||
make
|
||||
commands =
|
||||
rm -rf doc/build/pdf
|
||||
sphinx-build -W -b latex doc/source doc/build/pdf
|
||||
make -C doc/build/pdf
|
||||
|
||||
[testenv:debug]
|
||||
basepython = python3
|
||||
commands = oslo_debug_helper -t watcherclient/tests/unit {posargs}
|
||||
|
||||
[testenv:functional]
|
||||
basepython = python2.7
|
||||
passenv =
|
||||
OS_PROJECT_DOMAIN_NAME
|
||||
OS_USER_DOMAIN_NAME
|
||||
OS_PROJECT_NAME
|
||||
OS_USERNAME
|
||||
OS_PASSWORD
|
||||
OS_AUTH_URL
|
||||
OS_IDENTITY_API_VERSION
|
||||
OS_IMAGE_API_VERSION
|
||||
setenv =
|
||||
OS_TEST_PATH = ./watcherclient/tests/functional
|
||||
commands = python setup.py testr --slowest --testr-args='--concurrency=1 {posargs}'
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
show-source = True
|
||||
enable-extensions = H203,H106
|
||||
ignore = E123,E125
|
||||
ignore = E123,E125,W504
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
||||
|
||||
[testenv:wheel]
|
||||
basepython = python3
|
||||
commands = python setup.py bdist_wheel
|
||||
|
||||
[hacking]
|
||||
import_exceptions = watcherclient._i18n
|
||||
|
||||
[testenv:lower-constraints]
|
||||
basepython = python3
|
||||
install_command = pip install -U {opts} {packages}
|
||||
deps =
|
||||
-c{toxinidir}/lower-constraints.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import pbr.version
|
||||
|
||||
from watcherclient import client
|
||||
from watcherclient.common import api_versioning
|
||||
from watcherclient import exceptions
|
||||
|
||||
|
||||
@@ -24,3 +25,11 @@ __version__ = pbr.version.VersionInfo(
|
||||
'python-watcherclient').version_string()
|
||||
|
||||
__all__ = ['client', 'exceptions', ]
|
||||
|
||||
API_MIN_VERSION = api_versioning.APIVersion("1.0")
|
||||
# The max version should be the latest version that is supported in the client,
|
||||
# not necessarily the latest that the server can provide. This is only bumped
|
||||
# when client supported the max version, and bumped sequentially, otherwise
|
||||
# the client may break due to server side new version may include some
|
||||
# backward incompatible change.
|
||||
API_MAX_VERSION = api_versioning.APIVersion("1.1")
|
||||
|
||||
@@ -28,7 +28,7 @@ def get_client(api_version, os_auth_token=None, watcher_url=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,
|
||||
os_infra_optim_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.
|
||||
@@ -61,7 +61,7 @@ def get_client(api_version, os_auth_token=None, watcher_url=None,
|
||||
: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 os_infra_optim_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
|
||||
@@ -75,7 +75,7 @@ def get_client(api_version, os_auth_token=None, watcher_url=None,
|
||||
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,
|
||||
'os_infra_optim_api_version': os_infra_optim_api_version,
|
||||
'max_retries': max_retries,
|
||||
'retry_interval': retry_interval,
|
||||
}
|
||||
@@ -182,4 +182,11 @@ def Client(version, *args, **kwargs):
|
||||
python-watcherclient's doc.
|
||||
"""
|
||||
api_version, client_class = _get_client_class_and_version(version)
|
||||
|
||||
kw_api = kwargs.get('os_infra_optim_api_version')
|
||||
endpoint = kwargs.get('endpoint')
|
||||
# If both os_infra_optim_api_version and endpoint are not provided, get
|
||||
# API version from arg.
|
||||
if not kw_api and not endpoint:
|
||||
kwargs['os_infra_optim_api_version'] = api_version.get_string()
|
||||
return client_class(*args, **kwargs)
|
||||
|
||||
@@ -26,14 +26,34 @@ if not LOG.handlers:
|
||||
LOG.addHandler(logging.StreamHandler())
|
||||
|
||||
|
||||
MINOR_1_START_END_TIMING = '1.1'
|
||||
MINOR_2_FORCE_AUDIT = '1.2'
|
||||
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'")
|
||||
|
||||
|
||||
def allow_start_end_audit_time(requested_version):
|
||||
"""Check if we should support optional start/end attributes for Audit.
|
||||
|
||||
Version 1.1 of the API added support for start and end time of continuous
|
||||
audits.
|
||||
"""
|
||||
return (APIVersion(requested_version) >=
|
||||
APIVersion(MINOR_1_START_END_TIMING))
|
||||
|
||||
|
||||
def launch_audit_forced(requested_version):
|
||||
"""Check if we should support force option for Audit.
|
||||
|
||||
Version 1.2 of the API added support for force option.
|
||||
"""
|
||||
return (APIVersion(requested_version) >=
|
||||
APIVersion(MINOR_2_FORCE_AUDIT))
|
||||
|
||||
|
||||
class APIVersion(object):
|
||||
"""This class represents an API Version Request.
|
||||
|
||||
|
||||
@@ -31,10 +31,7 @@ def getid(obj):
|
||||
Abstracts the common pattern of allowing both an object or an
|
||||
object's ID (UUID) as a parameter when dealing with relationships.
|
||||
"""
|
||||
try:
|
||||
return obj.id
|
||||
except AttributeError:
|
||||
return obj
|
||||
return getattr(obj, 'id', obj)
|
||||
|
||||
|
||||
class Manager(object):
|
||||
@@ -134,6 +131,11 @@ class Manager(object):
|
||||
def _delete(self, url):
|
||||
self.api.raw_request('DELETE', url)
|
||||
|
||||
def _start(self, url, body=None, method='POST'):
|
||||
resp, body = self.api.json_request(method, url, body={})
|
||||
if body:
|
||||
return self.resource_class(self, body)
|
||||
|
||||
|
||||
class Resource(base.Resource):
|
||||
"""Represents a particular instance of an object (tenant, user, etc).
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
# Copyright 2012 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.
|
||||
|
||||
# W0603: Using the global statement
|
||||
# W0621: Redefining name %s from outer scope
|
||||
# pylint: disable=W0603,W0621
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import getpass
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import strutils
|
||||
import prettytable
|
||||
import six
|
||||
from six import moves
|
||||
|
||||
from watcherclient._i18n import _
|
||||
|
||||
|
||||
class MissingArgs(Exception):
|
||||
"""Supplied arguments are not sufficient for calling a function."""
|
||||
def __init__(self, missing):
|
||||
self.missing = missing
|
||||
msg = _("Missing arguments: %s") % ", ".join(missing)
|
||||
super(MissingArgs, self).__init__(msg)
|
||||
|
||||
|
||||
def validate_args(fn, *args, **kwargs):
|
||||
"""Check that the supplied args are sufficient for calling a function.
|
||||
|
||||
>>> validate_args(lambda a: None)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
MissingArgs: Missing argument(s): a
|
||||
>>> validate_args(lambda a, b, c, d: None, 0, c=1)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
MissingArgs: Missing argument(s): b, d
|
||||
|
||||
:param fn: the function to check
|
||||
:param arg: the positional arguments supplied
|
||||
:param kwargs: the keyword arguments supplied
|
||||
"""
|
||||
argspec = inspect.getargspec(fn)
|
||||
|
||||
num_defaults = len(argspec.defaults or [])
|
||||
required_args = argspec.args[:len(argspec.args) - num_defaults]
|
||||
|
||||
def isbound(method):
|
||||
return getattr(method, '__self__', None) is not None
|
||||
|
||||
if isbound(fn):
|
||||
required_args.pop(0)
|
||||
|
||||
missing = [arg for arg in required_args if arg not in kwargs]
|
||||
missing = missing[len(args):]
|
||||
if missing:
|
||||
raise MissingArgs(missing)
|
||||
|
||||
|
||||
def arg(*args, **kwargs):
|
||||
"""Decorator for CLI args.
|
||||
|
||||
Example:
|
||||
|
||||
>>> @arg("name", help="Name of the new entity")
|
||||
... def entity_create(args):
|
||||
... pass
|
||||
"""
|
||||
def _decorator(func):
|
||||
add_arg(func, *args, **kwargs)
|
||||
return func
|
||||
return _decorator
|
||||
|
||||
|
||||
def env(*args, **kwargs):
|
||||
"""Returns the first environment variable set.
|
||||
|
||||
If all are empty, defaults to '' or keyword arg `default`.
|
||||
"""
|
||||
for arg in args:
|
||||
value = os.environ.get(arg)
|
||||
if value:
|
||||
return value
|
||||
return kwargs.get('default', '')
|
||||
|
||||
|
||||
def add_arg(func, *args, **kwargs):
|
||||
"""Bind CLI arguments to a shell.py `do_foo` function."""
|
||||
|
||||
if not hasattr(func, 'arguments'):
|
||||
func.arguments = []
|
||||
|
||||
# NOTE(sirp): avoid dups that can occur when the module is shared across
|
||||
# tests.
|
||||
if (args, kwargs) not in func.arguments:
|
||||
# Because of the semantics of decorator composition if we just append
|
||||
# to the options list positional options will appear to be backwards.
|
||||
func.arguments.insert(0, (args, kwargs))
|
||||
|
||||
|
||||
def unauthenticated(func):
|
||||
"""Adds 'unauthenticated' attribute to decorated function.
|
||||
|
||||
Usage:
|
||||
|
||||
>>> @unauthenticated
|
||||
... def mymethod(f):
|
||||
... pass
|
||||
"""
|
||||
func.unauthenticated = True
|
||||
return func
|
||||
|
||||
|
||||
def isunauthenticated(func):
|
||||
"""Checks if the function does not require authentication.
|
||||
|
||||
Mark such functions with the `@unauthenticated` decorator.
|
||||
|
||||
:returns: bool
|
||||
"""
|
||||
return getattr(func, 'unauthenticated', False)
|
||||
|
||||
|
||||
def print_list(objs, fields, formatters=None, sortby_index=0,
|
||||
mixed_case_fields=None, field_labels=None):
|
||||
"""Print a list or objects as a table, one row per object.
|
||||
|
||||
:param objs: iterable of :class:`Resource`
|
||||
:param fields: attributes that correspond to columns, in order
|
||||
:param formatters: `dict` of callables for field formatting
|
||||
:param sortby_index: index of the field for sorting table rows
|
||||
:param mixed_case_fields: fields corresponding to object attributes that
|
||||
have mixed case names (e.g., 'serverId')
|
||||
:param field_labels: Labels to use in the heading of the table, default to
|
||||
fields.
|
||||
"""
|
||||
formatters = formatters or {}
|
||||
mixed_case_fields = mixed_case_fields or []
|
||||
field_labels = field_labels or fields
|
||||
if len(field_labels) != len(fields):
|
||||
raise ValueError(_("Field labels list %(labels)s has different number "
|
||||
"of elements than fields list %(fields)s"),
|
||||
{'labels': field_labels, 'fields': fields})
|
||||
|
||||
if sortby_index is None:
|
||||
kwargs = {}
|
||||
else:
|
||||
kwargs = {'sortby': field_labels[sortby_index]}
|
||||
pt = prettytable.PrettyTable(field_labels)
|
||||
pt.align = 'l'
|
||||
|
||||
for o in objs:
|
||||
row = []
|
||||
for field in fields:
|
||||
data = '-'
|
||||
if field in formatters:
|
||||
data = formatters[field](o)
|
||||
else:
|
||||
if field in mixed_case_fields:
|
||||
field_name = field.replace(' ', '_')
|
||||
else:
|
||||
field_name = field.lower().replace(' ', '_')
|
||||
data = getattr(o, field_name, '')
|
||||
if data is None:
|
||||
data = '-'
|
||||
row.append(data)
|
||||
pt.add_row(row)
|
||||
|
||||
if six.PY3:
|
||||
print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode())
|
||||
else:
|
||||
print(encodeutils.safe_encode(pt.get_string(**kwargs)))
|
||||
|
||||
|
||||
def print_dict(dct, dict_property="Property", wrap=0):
|
||||
"""Print a `dict` as a table of two columns.
|
||||
|
||||
:param dct: `dict` to print
|
||||
:param dict_property: name of the first column
|
||||
:param wrap: wrapping for the second column
|
||||
"""
|
||||
pt = prettytable.PrettyTable([dict_property, 'Value'])
|
||||
pt.align = 'l'
|
||||
for k, v in dct.items():
|
||||
# convert dict to str to check length
|
||||
if isinstance(v, dict):
|
||||
v = six.text_type(v)
|
||||
if wrap > 0:
|
||||
v = textwrap.fill(six.text_type(v), wrap)
|
||||
elif wrap < 0:
|
||||
raise ValueError(_("Wrap argument should be a positive integer"))
|
||||
# if value has a newline, add in multiple rows
|
||||
# e.g. fault with stacktrace
|
||||
if v and isinstance(v, six.string_types) and r'\n' in v:
|
||||
lines = v.strip().split(r'\n')
|
||||
col1 = k
|
||||
for line in lines:
|
||||
pt.add_row([col1, line])
|
||||
col1 = ''
|
||||
else:
|
||||
if v is None:
|
||||
v = '-'
|
||||
pt.add_row([k, v])
|
||||
|
||||
if six.PY3:
|
||||
print(encodeutils.safe_encode(pt.get_string()).decode())
|
||||
else:
|
||||
print(encodeutils.safe_encode(pt.get_string()))
|
||||
|
||||
|
||||
def get_password(max_password_prompts=3):
|
||||
"""Read password from TTY."""
|
||||
verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD"))
|
||||
pw = None
|
||||
if hasattr(sys.stdin, "isatty") and sys.stdin.isatty():
|
||||
# Check for Ctrl-D
|
||||
try:
|
||||
for __ in moves.range(max_password_prompts):
|
||||
pw1 = getpass.getpass("OS Password: ")
|
||||
if verify:
|
||||
pw2 = getpass.getpass("Please verify: ")
|
||||
else:
|
||||
pw2 = pw1
|
||||
if pw1 == pw2 and pw1:
|
||||
pw = pw1
|
||||
break
|
||||
except EOFError:
|
||||
pass
|
||||
return pw
|
||||
|
||||
|
||||
def service_type(stype):
|
||||
"""Adds 'service_type' attribute to decorated function.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@service_type('volume')
|
||||
def mymethod(f):
|
||||
...
|
||||
"""
|
||||
def inner(f):
|
||||
f.service_type = stype
|
||||
return f
|
||||
return inner
|
||||
|
||||
|
||||
def get_service_type(f):
|
||||
"""Retrieves service type from function."""
|
||||
return getattr(f, 'service_type', None)
|
||||
|
||||
|
||||
def pretty_choice_list(l):
|
||||
return ', '.join("'%s'" % i for i in l)
|
||||
|
||||
|
||||
def exit(msg=''):
|
||||
if msg:
|
||||
print(msg, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -34,16 +34,15 @@ from six.moves import http_client
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from watcherclient._i18n import _
|
||||
from watcherclient.common import api_versioning
|
||||
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'
|
||||
|
||||
# Record the latest version that this client was tested with.
|
||||
DEFAULT_VER = '1.latest'
|
||||
# Minor version 4 for adding webhook API
|
||||
LAST_KNOWN_API_VERSION = 4
|
||||
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
USER_AGENT = 'python-watcherclient'
|
||||
@@ -104,20 +103,8 @@ class VersionNegotiationMixin(object):
|
||||
{'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
|
||||
# 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':
|
||||
@@ -126,33 +113,33 @@ class VersionNegotiationMixin(object):
|
||||
"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,
|
||||
% {'req': self.os_infra_optim_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,
|
||||
% {'req': self.os_infra_optim_api_version,
|
||||
'min': min_ver, 'max': max_ver}))
|
||||
|
||||
negotiated_ver = str(
|
||||
min(version.StrictVersion(self.os_watcher_api_version),
|
||||
min(version.StrictVersion(self.os_infra_optim_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
|
||||
self.os_infra_optim_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',
|
||||
min_ver = accessor_func('OpenStack-API-Minimum-Version',
|
||||
None)
|
||||
max_ver = accessor_func('X-OpenStack-Watcher-API-Maximum-Version',
|
||||
max_ver = accessor_func('OpenStack-API-Maximum-Version',
|
||||
None)
|
||||
return min_ver, max_ver
|
||||
|
||||
@@ -206,8 +193,8 @@ class HTTPClient(VersionNegotiationMixin):
|
||||
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.os_infra_optim_api_version = kwargs.get(
|
||||
'os_infra_optim_api_version', DEFAULT_VER)
|
||||
self.api_version_select_state = kwargs.get(
|
||||
'api_version_select_state', 'default')
|
||||
self.conflict_max_retries = kwargs.pop('max_retries',
|
||||
@@ -289,7 +276,7 @@ class HTTPClient(VersionNegotiationMixin):
|
||||
LOG.debug('\n'.join(dump))
|
||||
|
||||
def _make_connection_url(self, url):
|
||||
return urlparse.urljoin(self.endpoint_trimmed, url)
|
||||
return '%s/%s' % (self.endpoint_trimmed.rstrip('/'), url.lstrip('/'))
|
||||
|
||||
def _parse_version_headers(self, resp):
|
||||
return self._generic_parse_version_headers(resp.headers.get)
|
||||
@@ -307,9 +294,15 @@ class HTTPClient(VersionNegotiationMixin):
|
||||
# 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.os_infra_optim_api_version:
|
||||
api_version = api_versioning.get_api_version(
|
||||
self.os_infra_optim_api_version)
|
||||
if api_version.is_latest():
|
||||
api_version = api_versioning.get_api_version(
|
||||
LATEST_VERSION)
|
||||
kwargs['headers'].setdefault(
|
||||
'OpenStack-API-Version',
|
||||
' '.join(['infra-optim', api_version.get_string()]))
|
||||
if self.auth_token:
|
||||
kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
|
||||
|
||||
@@ -334,8 +327,8 @@ class HTTPClient(VersionNegotiationMixin):
|
||||
|
||||
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)
|
||||
kwargs['headers']['OpenStack-API-Version'] = (
|
||||
' '.join(['infra-optim', negotiated_ver]))
|
||||
return self._http_request(url, method, **kwargs)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
@@ -354,7 +347,15 @@ class HTTPClient(VersionNegotiationMixin):
|
||||
# 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])
|
||||
# decoding byte to string is necessary for Python 3 compatibility
|
||||
# this issues has not been found with Python 3 unit tests
|
||||
# because the test creates a fake http response of type str
|
||||
# the if statement satisfies test (str) and real (bytes) behavior
|
||||
body_list = [
|
||||
chunk.decode("utf-8") if isinstance(chunk, bytes)
|
||||
else chunk for chunk in body_iter
|
||||
]
|
||||
body_str = ''.join(body_list)
|
||||
self.log_http_response(resp, body_str)
|
||||
body_iter = six.StringIO(body_str)
|
||||
else:
|
||||
@@ -387,8 +388,8 @@ class HTTPClient(VersionNegotiationMixin):
|
||||
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):
|
||||
http_client.RESET_CONTENT) or
|
||||
content_type is None):
|
||||
return resp, list()
|
||||
|
||||
if 'application/json' in content_type:
|
||||
@@ -477,13 +478,13 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter):
|
||||
"""HTTP client based on Keystone client session."""
|
||||
|
||||
def __init__(self,
|
||||
os_watcher_api_version,
|
||||
os_infra_optim_api_version,
|
||||
api_version_select_state,
|
||||
max_retries,
|
||||
retry_interval,
|
||||
endpoint,
|
||||
**kwargs):
|
||||
self.os_watcher_api_version = os_watcher_api_version
|
||||
self.os_infra_optim_api_version = os_infra_optim_api_version
|
||||
self.api_version_select_state = api_version_select_state
|
||||
self.conflict_max_retries = max_retries
|
||||
self.conflict_retry_interval = retry_interval
|
||||
@@ -508,9 +509,16 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter):
|
||||
_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)
|
||||
if getattr(self, 'os_infra_optim_api_version', None):
|
||||
api_version = api_versioning.get_api_version(
|
||||
self.os_infra_optim_api_version)
|
||||
if api_version.is_latest():
|
||||
api_version = api_versioning.get_api_version(
|
||||
LATEST_VERSION)
|
||||
kwargs['headers'].setdefault(
|
||||
'OpenStack-API-Version',
|
||||
' '.join(['infra-optim',
|
||||
api_version.get_string()]))
|
||||
|
||||
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
|
||||
endpoint_filter.setdefault('interface', self.interface)
|
||||
@@ -521,8 +529,8 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter):
|
||||
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)
|
||||
kwargs['headers']['OpenStack-API-Version'] = (
|
||||
' '.join(['infra-optim', negotiated_ver]))
|
||||
return self._http_request(url, method, **kwargs)
|
||||
if resp.status_code >= http_client.BAD_REQUEST:
|
||||
error_json = _extract_error_json(resp.content)
|
||||
@@ -574,7 +582,7 @@ def _construct_http_client(endpoint=None,
|
||||
session=None,
|
||||
token=None,
|
||||
auth_ref=None,
|
||||
os_watcher_api_version=DEFAULT_VER,
|
||||
os_infra_optim_api_version=DEFAULT_VER,
|
||||
api_version_select_state='default',
|
||||
max_retries=DEFAULT_MAX_RETRIES,
|
||||
retry_interval=DEFAULT_RETRY_INTERVAL,
|
||||
@@ -605,27 +613,29 @@ def _construct_http_client(endpoint=None,
|
||||
'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)
|
||||
return SessionClient(
|
||||
session=session,
|
||||
os_infra_optim_api_version=os_infra_optim_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('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)
|
||||
return HTTPClient(
|
||||
endpoint=endpoint,
|
||||
token=token,
|
||||
auth_ref=auth_ref,
|
||||
os_infra_optim_api_version=os_infra_optim_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)
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import uuid
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2018 Servionica
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
service_option = cfg.BoolOpt("watcher_client",
|
||||
default=True,
|
||||
help="Whether or not watcher_client is expected"
|
||||
"to be available")
|
||||
@@ -71,6 +71,7 @@ class AmbiguousAuthSystem(exceptions.ClientException):
|
||||
"""Could not obtain token and endpoint using provided credentials."""
|
||||
pass
|
||||
|
||||
|
||||
# Alias for backwards compatibility
|
||||
AmbigiousAuthSystem = AmbiguousAuthSystem
|
||||
|
||||
|
||||
@@ -15,9 +15,14 @@ import logging
|
||||
|
||||
from osc_lib import utils
|
||||
|
||||
import watcherclient
|
||||
from watcherclient.common import api_versioning
|
||||
from watcherclient.common import httpclient
|
||||
from watcherclient import exceptions
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_API_VERSION = '1'
|
||||
DEFAULT_API_VERSION = httpclient.LATEST_VERSION
|
||||
API_VERSION_OPTION = 'os_infra_optim_api_version'
|
||||
API_NAME = 'infra-optim'
|
||||
API_VERSIONS = {
|
||||
@@ -27,14 +32,17 @@ API_VERSIONS = {
|
||||
|
||||
def make_client(instance):
|
||||
"""Returns an infra-optim service client."""
|
||||
|
||||
version = api_versioning.APIVersion(instance._api_version[API_NAME])
|
||||
|
||||
infraoptim_client_class = utils.get_client_class(
|
||||
API_NAME,
|
||||
instance._api_version[API_NAME],
|
||||
version.ver_major,
|
||||
API_VERSIONS)
|
||||
LOG.debug('Instantiating infraoptim client: %s', infraoptim_client_class)
|
||||
|
||||
client = infraoptim_client_class(
|
||||
os_watcher_api_version=instance._api_version[API_NAME],
|
||||
os_infra_optim_api_version=instance._api_version[API_NAME],
|
||||
session=instance.session,
|
||||
region_name=instance._region_name,
|
||||
)
|
||||
@@ -53,3 +61,30 @@ def build_option_parser(parser):
|
||||
DEFAULT_API_VERSION +
|
||||
' (Env: OS_INFRA_OPTIM_API_VERSION)'))
|
||||
return parser
|
||||
|
||||
|
||||
def check_api_version(check_version):
|
||||
"""Validate version supplied by user
|
||||
|
||||
Returns:
|
||||
* True if version is OK
|
||||
* False if the version has not been checked and the previous plugin
|
||||
check should be performed
|
||||
* throws an exception if the version is no good
|
||||
"""
|
||||
|
||||
infra_api_version = api_versioning.get_api_version(check_version)
|
||||
|
||||
# Bypass X.latest format microversion
|
||||
if not infra_api_version.is_latest():
|
||||
if infra_api_version > api_versioning.APIVersion("2.0"):
|
||||
if not infra_api_version.matches(
|
||||
watcherclient.API_MIN_VERSION,
|
||||
watcherclient.API_MAX_VERSION,
|
||||
):
|
||||
msg = "versions supported by client: %(min)s - %(max)s" % {
|
||||
"min": watcherclient.API_MIN_VERSION.get_string(),
|
||||
"max": watcherclient.API_MAX_VERSION.get_string(),
|
||||
}
|
||||
raise exceptions.CommandError(msg)
|
||||
return True
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import os
|
||||
|
||||
from tempest.test_discover import plugins
|
||||
|
||||
from watcherclient import config as watcher_config
|
||||
|
||||
|
||||
class WatcherClientTempestPlugin(plugins.TempestPlugin):
|
||||
def load_tests(self):
|
||||
base_path = os.path.split(os.path.dirname(
|
||||
os.path.abspath(__file__)))[0]
|
||||
test_dir = "watcherclient/tests/functional/v1"
|
||||
full_test_dir = os.path.join(base_path, test_dir)
|
||||
return full_test_dir, base_path
|
||||
|
||||
def register_opts(self, conf):
|
||||
conf.register_opt(watcher_config.service_option,
|
||||
group='service_available')
|
||||
|
||||
def get_opt_lists(self):
|
||||
return [('service_available', [watcher_config.service_option])]
|
||||
@@ -39,6 +39,7 @@ API_NAME = 'infra-optim'
|
||||
API_VERSIONS = {
|
||||
'1': 'watcherclient.v1.client.Client',
|
||||
}
|
||||
DEFAULT_OS_INFRA_OPTIM_API_VERSION = '1.latest'
|
||||
_DEFAULT_IDENTITY_API_VERSION = '3'
|
||||
_IDENTITY_API_VERSION_2 = ['2', '2.0']
|
||||
_IDENTITY_API_VERSION_3 = ['3']
|
||||
@@ -47,8 +48,6 @@ _IDENTITY_API_VERSION_3 = ['3']
|
||||
class WatcherShell(app.App):
|
||||
"""Watcher command line interface."""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.client = None
|
||||
|
||||
@@ -149,11 +148,13 @@ class WatcherShell(app.App):
|
||||
metavar='<auth-token>',
|
||||
default=utils.env('OS_AUTH_TOKEN'),
|
||||
help='Defaults to env[OS_AUTH_TOKEN].')
|
||||
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-infra-optim-api-version',
|
||||
metavar='<infra-optim-api-version>',
|
||||
default=utils.env('OS_INFRA_OPTIM_API_VERSION',
|
||||
default=DEFAULT_OS_INFRA_OPTIM_API_VERSION),
|
||||
help='Accepts X, X.Y (where X is major and Y is minor part) or '
|
||||
'"X.latest", defaults to env[OS_INFRA_OPTIM_API_VERSION].')
|
||||
parser.add_argument('--os-endpoint-type',
|
||||
default=utils.env('OS_ENDPOINT_TYPE'),
|
||||
help='Defaults to env[OS_ENDPOINT_TYPE] or '
|
||||
@@ -194,17 +195,18 @@ class WatcherShell(app.App):
|
||||
except Exception as e:
|
||||
if not logging.getLogger('').handlers:
|
||||
logging.basicConfig()
|
||||
self.log.error('Exception raised: %s', str(e))
|
||||
LOG.error('Exception raised: %s', str(e))
|
||||
|
||||
return ret_val
|
||||
|
||||
finally:
|
||||
self.log.info("END return value: %s", ret_val)
|
||||
LOG.info("END return value: %s", ret_val)
|
||||
|
||||
|
||||
def main(argv=sys.argv[1:]):
|
||||
watcher_app = WatcherShell()
|
||||
return watcher_app.run(argv)
|
||||
|
||||
|
||||
if __name__ == '__main__': # pragma: no cover
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
..
|
||||
Except where otherwise noted, this document is licensed under Creative
|
||||
Commons Attribution 3.0 License. You can view the license at:
|
||||
|
||||
https://creativecommons.org/licenses/by/3.0/
|
||||
|
||||
=======
|
||||
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: https://docs.openstack.org/tempest/latest/
|
||||
|
||||
|
||||
Tempest installation
|
||||
--------------------
|
||||
|
||||
You need to install virtualenv, create a virtual environment and activate it::
|
||||
|
||||
$ pip install virtualenv
|
||||
$ virtualenv watcher-env
|
||||
$ . 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 -r 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
|
||||
@@ -1,144 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import testtools
|
||||
|
||||
import six
|
||||
from tempest import clients
|
||||
from tempest.common import credentials_factory as creds_factory
|
||||
from tempest.lib.cli import output_parser
|
||||
from tempest.lib import exceptions
|
||||
|
||||
|
||||
def credentials():
|
||||
# You can get credentials either from tempest.conf file or
|
||||
# from OS environment.
|
||||
tempest_creds = clients.get_auth_provider(
|
||||
creds_factory.get_configured_admin_credentials())
|
||||
creds = tempest_creds.credentials
|
||||
creds_dict = {
|
||||
'--os-username': os.environ.get('OS_USERNAME', creds.username),
|
||||
'--os-password': os.environ.get('OS_PASSWORD', creds.password),
|
||||
'--os-project-name': os.environ.get('OS_PROJECT_NAME',
|
||||
creds.project_name),
|
||||
'--os-auth-url': os.environ.get('OS_AUTH_URL',
|
||||
tempest_creds.auth_url),
|
||||
'--os-project-domain-name': os.environ.get('OS_PROJECT_DOMAIN_ID',
|
||||
creds.project_domain_name),
|
||||
'--os-user-domain-name': os.environ.get('OS_USER_DOMAIN_ID',
|
||||
creds.user_domain_name),
|
||||
}
|
||||
return [x for sub in creds_dict.items() for x in sub]
|
||||
|
||||
|
||||
def execute(cmd, fail_ok=False, merge_stderr=False):
|
||||
"""Executes specified command for the given action."""
|
||||
cmdlist = shlex.split(cmd)
|
||||
cmdlist.extend(credentials())
|
||||
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)
|
||||
|
||||
def has_actionplan_succeeded(self, ap_uuid):
|
||||
return self.parse_show_as_object(
|
||||
self.watcher('actionplan show %s' % ap_uuid)
|
||||
)['State'] == 'SUCCEEDED'
|
||||
|
||||
@classmethod
|
||||
def has_audit_created(cls, audit_uuid):
|
||||
return cls.parse_show_as_object(
|
||||
cls.watcher('audit show %s' % audit_uuid))['State'] == 'SUCCEEDED'
|
||||
@@ -1,81 +0,0 @@
|
||||
# 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
|
||||
|
||||
import functools
|
||||
|
||||
from tempest.lib.common.utils import test_utils
|
||||
|
||||
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_output = cls.parse_show_as_object(cls.watcher(
|
||||
'audit create -a %s' % template_output['Name']))
|
||||
cls.audit_uuid = audit_output['UUID']
|
||||
audit_created = test_utils.call_until_true(
|
||||
func=functools.partial(cls.has_audit_created, cls.audit_uuid),
|
||||
duration=600,
|
||||
sleep_for=2)
|
||||
if not audit_created:
|
||||
raise Exception('Audit has not been succeeded')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
# Delete Action Plan and all related actions.
|
||||
output = cls.parse_show(
|
||||
cls.watcher('actionplan list --audit %s' % cls.audit_uuid))
|
||||
action_plan_uuid = list(output[0])[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 --audit %s'
|
||||
% self.audit_uuid))
|
||||
action_uuid = list(action_list[0])[0]
|
||||
action = self.watcher('action show %s' % action_uuid)
|
||||
self.assertIn(action_uuid, action)
|
||||
self.assert_table_structure([action],
|
||||
self.detailed_list_fields)
|
||||
@@ -1,145 +0,0 @@
|
||||
# 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
|
||||
|
||||
import functools
|
||||
|
||||
from tempest.lib.common.utils import test_utils
|
||||
|
||||
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']
|
||||
audit_created = test_utils.call_until_true(
|
||||
func=functools.partial(cls.has_audit_created, cls.audit_uuid),
|
||||
duration=600,
|
||||
sleep_for=2)
|
||||
if not audit_created:
|
||||
raise Exception('Audit has not been succeeded')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
# Delete action plan
|
||||
output = cls.parse_show(
|
||||
cls.watcher('actionplan list --audit %s' % cls.audit_uuid))
|
||||
action_plan_uuid = list(output[0])[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 = list(action_plan_list[0])[0]
|
||||
actionplan = self.watcher('actionplan show %s' % action_plan_uuid)
|
||||
self.assertIn(action_plan_uuid, actionplan)
|
||||
self.assert_table_structure([actionplan],
|
||||
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 = list(output[0])[0]
|
||||
self.watcher('actionplan start %s' % action_plan_uuid)
|
||||
raw_output = self.watcher('actionplan show %s' % action_plan_uuid)
|
||||
self.assert_table_structure([raw_output], self.detailed_list_fields)
|
||||
|
||||
self.assertTrue(test_utils.call_until_true(
|
||||
func=functools.partial(
|
||||
self.has_actionplan_succeeded, action_plan_uuid),
|
||||
duration=600,
|
||||
sleep_for=2
|
||||
))
|
||||
|
||||
|
||||
class ActionPlanActiveTests(base.TestCase):
|
||||
|
||||
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):
|
||||
self.assertTrue(test_utils.call_until_true(
|
||||
func=functools.partial(
|
||||
self.has_audit_created, self.audit_uuid),
|
||||
duration=600,
|
||||
sleep_for=2
|
||||
))
|
||||
output = self.parse_show(
|
||||
self.watcher('actionplan list --audit %s' % self.audit_uuid))
|
||||
action_plan_uuid = list(output[0])[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()
|
||||
@@ -1,136 +0,0 @@
|
||||
# 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
|
||||
|
||||
import functools
|
||||
|
||||
from tempest.lib.common.utils import test_utils
|
||||
|
||||
from watcherclient.tests.functional.v1 import base
|
||||
|
||||
|
||||
class AuditTests(base.TestCase):
|
||||
"""Functional tests for audit."""
|
||||
|
||||
dummy_name = 'dummy'
|
||||
list_fields = ['UUID', 'Name', 'Audit Type', 'State', 'Goal', 'Strategy']
|
||||
detailed_list_fields = list_fields + ['Created At', 'Updated At',
|
||||
'Deleted At', 'Parameters',
|
||||
'Interval', 'Audit Scope',
|
||||
'Next Run Time']
|
||||
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']
|
||||
audit_created = test_utils.call_until_true(
|
||||
func=functools.partial(cls.has_audit_created, cls.audit_uuid),
|
||||
duration=600,
|
||||
sleep_for=2)
|
||||
if not audit_created:
|
||||
raise Exception('Audit has not been succeeded')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
output = cls.parse_show(
|
||||
cls.watcher('actionplan list --audit %s' % cls.audit_uuid))
|
||||
action_plan_uuid = list(output[0])[0]
|
||||
cls.watcher('actionplan delete %s' % action_plan_uuid)
|
||||
cls.watcher('audit delete %s' % cls.audit_uuid)
|
||||
cls.watcher('audittemplate delete %s' % cls.audit_template_name)
|
||||
|
||||
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', 'Name', '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()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.watcher('audittemplate create %s dummy -s dummy'
|
||||
% cls.audit_template_name)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.watcher('audittemplate delete %s' % cls.audit_template_name)
|
||||
|
||||
def _create_audit(self):
|
||||
return self.parse_show_as_object(
|
||||
self.watcher('audit create -a %s'
|
||||
% self.audit_template_name))['UUID']
|
||||
|
||||
def _delete_audit(self, audit_uuid):
|
||||
self.assertTrue(test_utils.call_until_true(
|
||||
func=functools.partial(
|
||||
self.has_audit_created, audit_uuid),
|
||||
duration=600,
|
||||
sleep_for=2
|
||||
))
|
||||
output = self.parse_show(
|
||||
self.watcher('actionplan list --audit %s' % audit_uuid))
|
||||
action_plan_uuid = list(output[0])[0]
|
||||
self.watcher('actionplan delete %s' % action_plan_uuid)
|
||||
self.watcher('audit delete %s' % audit_uuid)
|
||||
|
||||
def test_create_audit(self):
|
||||
audit = self.watcher('audit create -a %s' % self.audit_template_name)
|
||||
audit_uuid = self.parse_show_as_object(audit)['UUID']
|
||||
self.assert_table_structure([audit], self.detailed_list_fields)
|
||||
self._delete_audit(audit_uuid)
|
||||
|
||||
def test_delete_audit(self):
|
||||
audit_uuid = self._create_audit()
|
||||
self.assertTrue(test_utils.call_until_true(
|
||||
func=functools.partial(
|
||||
self.has_audit_created, audit_uuid),
|
||||
duration=600,
|
||||
sleep_for=2
|
||||
))
|
||||
raw_output = self.watcher('audit delete %s' % audit_uuid)
|
||||
self.assertOutput('', raw_output)
|
||||
output = self.parse_show(
|
||||
self.watcher('actionplan list --audit %s' % audit_uuid))
|
||||
action_plan_uuid = list(output[0])[0]
|
||||
self.watcher('actionplan delete %s' % action_plan_uuid)
|
||||
@@ -1,89 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,41 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,40 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,47 +0,0 @@
|
||||
# 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):
|
||||
# TODO(alexchadin): this method should be refactored since Watcher will
|
||||
# get HA support soon.
|
||||
raw_output = self.watcher('service show %s'
|
||||
% self.decision_engine_name)
|
||||
self.assertIn(self.decision_engine_name, raw_output)
|
||||
self.assert_table_structure([raw_output],
|
||||
self.list_fields + ['Last seen up'])
|
||||
self.assertNotIn(self.applier_name, raw_output)
|
||||
@@ -1,48 +0,0 @@
|
||||
# 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'
|
||||
basic_strategy = 'basic'
|
||||
list_fields = ['UUID', 'Name', 'Display name', 'Goal']
|
||||
state_fields = ['Datasource', 'Metrics', 'CDM', 'Name']
|
||||
|
||||
def test_strategy_list(self):
|
||||
raw_output = self.watcher('strategy list')
|
||||
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)
|
||||
|
||||
def test_strategy_state(self):
|
||||
raw_output = self.watcher('strategy state %s' % self.basic_strategy)
|
||||
self.assertIn(self.basic_strategy, raw_output)
|
||||
self.assert_table_structure([raw_output], self.state_fields)
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
from watcherclient.common import api_versioning
|
||||
from watcherclient import exceptions
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
from keystoneauth1 import loading as kaloading
|
||||
|
||||
@@ -118,7 +118,7 @@ class ClientTest(utils.BaseTestCase):
|
||||
'os_password': 'PASSWORD',
|
||||
'os_auth_url': 'http://localhost:35357/v2.0',
|
||||
'os_auth_token': '',
|
||||
'os_watcher_api_version': "latest",
|
||||
'os_infra_optim_api_version': "latest",
|
||||
}
|
||||
self._test_get_client(**kwargs)
|
||||
|
||||
@@ -129,7 +129,7 @@ class ClientTest(utils.BaseTestCase):
|
||||
'os_password': 'PASSWORD',
|
||||
'os_auth_url': 'http://localhost:35357/v2.0',
|
||||
'os_auth_token': '',
|
||||
'os_watcher_api_version': "1.4",
|
||||
'os_infra_optim_api_version': "1.4",
|
||||
}
|
||||
self._test_get_client(**kwargs)
|
||||
|
||||
@@ -267,7 +267,7 @@ class ClientTest(utils.BaseTestCase):
|
||||
kwargs = {
|
||||
'watcher_url': 'http://watcher.example.org:9322/',
|
||||
'os_auth_token': 'USER_AUTH_TOKEN',
|
||||
'os_watcher_api_version': 'latest',
|
||||
'os_infra_optim_api_version': 'latest',
|
||||
'insecure': True,
|
||||
'max_retries': 10,
|
||||
'retry_interval': 10,
|
||||
@@ -277,7 +277,7 @@ class ClientTest(utils.BaseTestCase):
|
||||
mock_client.assert_called_once_with(
|
||||
'1', 'http://watcher.example.org:9322/',
|
||||
**{
|
||||
'os_watcher_api_version': 'latest',
|
||||
'os_infra_optim_api_version': 'latest',
|
||||
'max_retries': 10,
|
||||
'retry_interval': 10,
|
||||
'token': 'USER_AUTH_TOKEN',
|
||||
@@ -309,7 +309,7 @@ class ClientTest(utils.BaseTestCase):
|
||||
mock_client.assert_called_once_with(
|
||||
'1', session.get_endpoint.return_value,
|
||||
**{
|
||||
'os_watcher_api_version': None,
|
||||
'os_infra_optim_api_version': None,
|
||||
'max_retries': None,
|
||||
'retry_interval': None,
|
||||
'session': session,
|
||||
@@ -328,7 +328,7 @@ class ClientTest(utils.BaseTestCase):
|
||||
mock_client.assert_called_once_with(
|
||||
'1', session.get_endpoint.return_value,
|
||||
**{
|
||||
'os_watcher_api_version': None,
|
||||
'os_infra_optim_api_version': None,
|
||||
'max_retries': None,
|
||||
'retry_interval': None,
|
||||
'session': session,
|
||||
@@ -350,3 +350,11 @@ class ClientTest(utils.BaseTestCase):
|
||||
client = httpclient.HTTPClient('http://localhost/')
|
||||
header_redact = client._process_header(name, value)
|
||||
self.assertEqual(header, header_redact)
|
||||
|
||||
def test_make_connection_url(self):
|
||||
endpoint = 'http://localhost/infra-optim'
|
||||
url = '/v1/goals'
|
||||
expected_url = 'http://localhost/infra-optim/v1/goals'
|
||||
client = httpclient.HTTPClient(endpoint)
|
||||
conn_url = client._make_connection_url(url)
|
||||
self.assertEqual(expected_url, conn_url)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
from watcherclient.common.apiclient import exceptions as exc
|
||||
from watcherclient.common import utils
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
|
||||
import copy
|
||||
import os
|
||||
from unittest import mock
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
from oslo_utils import strutils
|
||||
import six
|
||||
import testtools
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
import shlex
|
||||
from unittest import mock
|
||||
|
||||
import mock
|
||||
from osc_lib import utils as oscutils
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
@@ -25,7 +25,7 @@ from watcherclient.tests.unit import utils
|
||||
|
||||
class CommandTestCase(utils.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self, os_infra_optim_api_version='1.0'):
|
||||
super(CommandTestCase, self).setUp()
|
||||
|
||||
self.fake_env = {
|
||||
@@ -38,7 +38,7 @@ class CommandTestCase(utils.BaseTestCase):
|
||||
'os_username': 'test',
|
||||
'os_password': 'test',
|
||||
'timeout': 600,
|
||||
'os_watcher_api_version': '1'}
|
||||
'os_infra_optim_api_version': os_infra_optim_api_version}
|
||||
self.m_env = mock.Mock(
|
||||
name='m_env',
|
||||
side_effect=lambda x, *args, **kwargs: self.fake_env.get(
|
||||
@@ -53,7 +53,7 @@ class CommandTestCase(utils.BaseTestCase):
|
||||
self.addCleanup(self.p_construct_http_client.stop)
|
||||
|
||||
def run_cmd(self, cmd, formatting='json'):
|
||||
if formatting:
|
||||
if formatting and formatting != 'table':
|
||||
formatter_arg = " -f %s" % formatting
|
||||
formatter = jsonutils.loads
|
||||
else:
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
from unittest import mock
|
||||
|
||||
import testtools
|
||||
from testtools import matchers
|
||||
|
||||
from oslo_utils.uuidutils import generate_uuid
|
||||
from watcherclient.common.apiclient.exceptions import HTTPClientError
|
||||
from watcherclient.tests.unit import utils
|
||||
import watcherclient.v1.action_plan
|
||||
|
||||
@@ -39,6 +42,18 @@ UPDATED_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1)
|
||||
NEW_STATE = 'PENDING'
|
||||
UPDATED_ACTION_PLAN['state'] = NEW_STATE
|
||||
|
||||
START_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1)
|
||||
START_ACTION_PLAN['state'] = NEW_STATE
|
||||
|
||||
ONGOING_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1)
|
||||
ONGOING_ACTION_PLAN['state'] = 'ONGOING'
|
||||
|
||||
CANCELLING_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1)
|
||||
CANCELLING_ACTION_PLAN['state'] = 'CANCELLING'
|
||||
|
||||
CANCELD_ACTION_PLAN = copy.deepcopy(ACTION_PLAN2)
|
||||
CANCELD_ACTION_PLAN['state'] = 'CANCELLED'
|
||||
|
||||
fake_responses = {
|
||||
'/v1/action_plans':
|
||||
{
|
||||
@@ -69,6 +84,13 @@ fake_responses = {
|
||||
UPDATED_ACTION_PLAN,
|
||||
),
|
||||
},
|
||||
'/v1/action_plans/%s/start' % ACTION_PLAN1['uuid']:
|
||||
{
|
||||
'POST': (
|
||||
{},
|
||||
START_ACTION_PLAN,
|
||||
),
|
||||
},
|
||||
'/v1/action_plans/detail?uuid=%s' % ACTION_PLAN1['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
@@ -120,6 +142,31 @@ fake_responses_sorting = {
|
||||
},
|
||||
}
|
||||
|
||||
fake_responses_cancel = {
|
||||
'/v1/action_plans/%s' % ACTION_PLAN1['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
[ONGOING_ACTION_PLAN],
|
||||
),
|
||||
'PATCH': (
|
||||
{},
|
||||
CANCELLING_ACTION_PLAN,
|
||||
),
|
||||
},
|
||||
'/v1/action_plans/%s' % ACTION_PLAN2['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
[ACTION_PLAN2],
|
||||
),
|
||||
'PATCH': (
|
||||
{},
|
||||
CANCELD_ACTION_PLAN,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ActionPlanManagerTest(testtools.TestCase):
|
||||
|
||||
@@ -206,6 +253,54 @@ class ActionPlanManagerTest(testtools.TestCase):
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(ACTION_PLAN1['uuid'], action_plan.uuid)
|
||||
|
||||
def test_action_plans_get_index_error(self):
|
||||
|
||||
# verify this method will return None when meet indexError
|
||||
fake_uuid = generate_uuid()
|
||||
self.api.json_request = mock.Mock(return_value=('404', []))
|
||||
self.assertIsNone(self.mgr.get(fake_uuid))
|
||||
|
||||
def test_action_plans_delete(self):
|
||||
# verity that action plan was successfully deleted
|
||||
self.api.raw_request = mock.Mock(return_value=('204', []))
|
||||
self.assertIsNone(self.mgr.delete(ACTION_PLAN1['uuid']))
|
||||
|
||||
# verity that delete a wrong action plan will raise Exception
|
||||
fake_uuid = generate_uuid()
|
||||
self.api.raw_request = mock.Mock(
|
||||
side_effect=HTTPClientError('404 Not Found'))
|
||||
self.assertRaises(HTTPClientError, self.mgr.delete, fake_uuid)
|
||||
|
||||
def test_action_plans_cancel(self):
|
||||
# verity that the status of action plan can be converted from
|
||||
# 'ONGOING' to 'CANCELLING'
|
||||
self.api = utils.FakeAPI(fake_responses_cancel)
|
||||
self.mgr = watcherclient.v1.action_plan.ActionPlanManager(self.api)
|
||||
patch = {'op': 'replace',
|
||||
'value': 'CANCELLING',
|
||||
'path': '/state'}
|
||||
action_plan = self.mgr.cancel(action_plan_id=ACTION_PLAN1['uuid'])
|
||||
expect = [
|
||||
('GET', '/v1/action_plans/%s' % ACTION_PLAN1['uuid'], {}, None),
|
||||
('PATCH', '/v1/action_plans/%s' % ACTION_PLAN1['uuid'], {},
|
||||
[patch])
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual('CANCELLING', action_plan.state)
|
||||
|
||||
# verity that the status of action plan can be converted from
|
||||
# 'RECOMMENDED' to 'CANCELLED'
|
||||
patch['value'] = 'CANCELLED'
|
||||
self.api.calls = []
|
||||
action_plan = self.mgr.cancel(action_plan_id=ACTION_PLAN2['uuid'])
|
||||
expect = [
|
||||
('GET', '/v1/action_plans/%s' % ACTION_PLAN2['uuid'], {}, None),
|
||||
('PATCH', '/v1/action_plans/%s' % ACTION_PLAN2['uuid'], {},
|
||||
[patch])
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual('CANCELLED', action_plan.state)
|
||||
|
||||
def test_action_plan_update(self):
|
||||
patch = {'op': 'replace',
|
||||
'value': NEW_STATE,
|
||||
@@ -220,3 +315,10 @@ class ActionPlanManagerTest(testtools.TestCase):
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(NEW_STATE, action_plan.state)
|
||||
|
||||
def test_action_plan_start(self):
|
||||
action_plan = self.mgr.start(ACTION_PLAN1['uuid'])
|
||||
expect = [('POST', '/v1/action_plans/%s/start'
|
||||
% ACTION_PLAN1['uuid'], {}, {})]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(NEW_STATE, action_plan.state)
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
from oslo_utils.uuidutils import generate_uuid
|
||||
import six
|
||||
|
||||
from watcherclient import exceptions
|
||||
@@ -44,6 +46,7 @@ ACTION_PLAN_1 = {
|
||||
"description": "Dummy Global Efficacy2"}
|
||||
],
|
||||
'deleted_at': None,
|
||||
'hostname': ''
|
||||
}
|
||||
|
||||
ACTION_PLAN_2 = {
|
||||
@@ -63,6 +66,7 @@ ACTION_PLAN_2 = {
|
||||
"description": "Dummy Global Efficacy",
|
||||
}],
|
||||
'deleted_at': None,
|
||||
'hostname': ''
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +128,17 @@ class ActionPlanShellTest(base.CommandTestCase):
|
||||
self.assertEqual(action_plan2.global_efficacy,
|
||||
results[1]['Global efficacy'])
|
||||
|
||||
def test_do_action_plan_list_by_table(self):
|
||||
action_plan1 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1)
|
||||
action_plan2 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_2)
|
||||
self.m_action_plan_mgr.list.return_value = [
|
||||
action_plan1, action_plan2]
|
||||
|
||||
exit_code, results = self.run_cmd('actionplan list', 'table')
|
||||
self.assertEqual(0, exit_code)
|
||||
self.assertIn(ACTION_PLAN_1['uuid'], results)
|
||||
self.assertIn(ACTION_PLAN_2['uuid'], results)
|
||||
|
||||
self.m_action_plan_mgr.list.assert_called_once_with(detail=False)
|
||||
|
||||
def test_do_action_plan_list_detail(self):
|
||||
@@ -193,6 +208,32 @@ class ActionPlanShellTest(base.CommandTestCase):
|
||||
self.assertEqual(1, exit_code)
|
||||
self.assertEqual('', result)
|
||||
|
||||
def test_do_action_plan_show_by_random_uuid(self):
|
||||
# verify that show a wrong actionplan will raise Exception
|
||||
self.m_action_plan_mgr.get.side_effect = exceptions.HTTPNotFound
|
||||
fake_uuid = generate_uuid()
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'actionplan show {}'.format(fake_uuid), formatting=None)
|
||||
self.assertEqual(1, exit_code)
|
||||
self.assertEqual('', result)
|
||||
|
||||
self.m_action_plan_mgr.get.assert_called_once_with(fake_uuid)
|
||||
|
||||
def test_do_action_plan_show_uuid_by_table(self):
|
||||
# verify that show an actionplan can be in a 'table' format
|
||||
action_plan = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1)
|
||||
self.m_action_plan_mgr.get.return_value = action_plan
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'actionplan show d9d9978e-6db5-4a05-8eab-1531795d7004',
|
||||
formatting='table')
|
||||
self.assertEqual(0, exit_code)
|
||||
self.assertIn(ACTION_PLAN_1['uuid'], result)
|
||||
|
||||
self.m_action_plan_mgr.get.assert_called_once_with(
|
||||
'd9d9978e-6db5-4a05-8eab-1531795d7004')
|
||||
|
||||
def test_do_action_plan_delete(self):
|
||||
self.m_action_plan_mgr.delete.return_value = ''
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
import six
|
||||
|
||||
from watcherclient import exceptions
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
from unittest 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
|
||||
|
||||
AUDIT_TEMPLATE_1 = {
|
||||
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
@@ -52,73 +52,86 @@ STRATEGY_1 = {
|
||||
'deleted_at': None,
|
||||
}
|
||||
|
||||
AUDIT_1 = {
|
||||
'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9',
|
||||
'audit_type': 'ONESHOT',
|
||||
'state': 'PENDING',
|
||||
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
'audit_template_name': 'at1',
|
||||
'goal_name': 'SERVER_CONSOLIDATION',
|
||||
'strategy_name': 'basic',
|
||||
'created_at': datetime.datetime.now().isoformat(),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'parameters': None,
|
||||
'interval': None,
|
||||
'scope': '',
|
||||
'auto_trigger': False,
|
||||
'next_run_time': None,
|
||||
'name': 'my_audit1',
|
||||
}
|
||||
|
||||
AUDIT_2 = {
|
||||
'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea',
|
||||
'audit_type': 'ONESHOT',
|
||||
'state': 'PENDING',
|
||||
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
'audit_template_name': 'at1',
|
||||
'goal_name': 'fc087747-61be-4aad-8126-b701731ae836',
|
||||
'strategy_name': 'auto',
|
||||
'created_at': datetime.datetime.now().isoformat(),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'parameters': None,
|
||||
'interval': None,
|
||||
'scope': '',
|
||||
'auto_trigger': False,
|
||||
'next_run_time': None,
|
||||
'name': 'my_audit2',
|
||||
}
|
||||
|
||||
AUDIT_3 = {
|
||||
'uuid': '43199d0e-0712-1213-9674-5ae2af8dhgte',
|
||||
'audit_type': 'ONESHOT',
|
||||
'state': 'PENDING',
|
||||
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
'audit_template_name': 'at1',
|
||||
'goal_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,
|
||||
'next_run_time': None,
|
||||
'name': 'my_audit3',
|
||||
}
|
||||
|
||||
|
||||
class AuditShellTest(base.CommandTestCase):
|
||||
|
||||
SHORT_LIST_FIELDS = resource_fields.AUDIT_SHORT_LIST_FIELDS
|
||||
SHORT_LIST_FIELD_LABELS = resource_fields.AUDIT_SHORT_LIST_FIELD_LABELS
|
||||
FIELDS = resource_fields.AUDIT_FIELDS
|
||||
FIELD_LABELS = resource_fields.AUDIT_FIELD_LABELS
|
||||
AUDIT_1 = {
|
||||
'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9',
|
||||
'audit_type': 'ONESHOT',
|
||||
'state': 'PENDING',
|
||||
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
'audit_template_name': 'at1',
|
||||
'goal_name': 'SERVER_CONSOLIDATION',
|
||||
'strategy_name': 'basic',
|
||||
'created_at': datetime.datetime.now().isoformat(),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'parameters': None,
|
||||
'interval': None,
|
||||
'scope': '',
|
||||
'auto_trigger': False,
|
||||
'next_run_time': None,
|
||||
'name': 'my_audit1',
|
||||
'hostname': '',
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(self.__class__, self).setUp()
|
||||
AUDIT_2 = {
|
||||
'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea',
|
||||
'audit_type': 'ONESHOT',
|
||||
'state': 'PENDING',
|
||||
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
'audit_template_name': 'at1',
|
||||
'goal_name': 'fc087747-61be-4aad-8126-b701731ae836',
|
||||
'strategy_name': 'auto',
|
||||
'created_at': datetime.datetime.now().isoformat(),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'parameters': None,
|
||||
'interval': None,
|
||||
'scope': '',
|
||||
'auto_trigger': False,
|
||||
'next_run_time': None,
|
||||
'name': 'my_audit2',
|
||||
'hostname': '',
|
||||
}
|
||||
|
||||
AUDIT_3 = {
|
||||
'uuid': '43199d0e-0712-1213-9674-5ae2af8dhgte',
|
||||
'audit_type': 'ONESHOT',
|
||||
'state': 'PENDING',
|
||||
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
'audit_template_name': 'at1',
|
||||
'goal_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,
|
||||
'next_run_time': None,
|
||||
'name': 'my_audit3',
|
||||
'hostname': '',
|
||||
}
|
||||
|
||||
SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type',
|
||||
'state', 'goal_name', 'strategy_name',
|
||||
'auto_trigger']
|
||||
SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Audit Type', 'State', 'Goal',
|
||||
'Strategy', 'Auto Trigger']
|
||||
FIELDS = ['uuid', 'name', 'created_at', 'updated_at', 'deleted_at',
|
||||
'state', 'audit_type', 'parameters', 'interval', 'goal_name',
|
||||
'strategy_name', 'scope', 'auto_trigger', 'next_run_time',
|
||||
'hostname']
|
||||
FIELD_LABELS = ['UUID', 'Name', 'Created At', 'Updated At', 'Deleted At',
|
||||
'State', 'Audit Type', 'Parameters', 'Interval', 'Goal',
|
||||
'Strategy', 'Audit Scope', 'Auto Trigger',
|
||||
'Next Run Time', 'Hostname']
|
||||
|
||||
def setUp(self, os_infra_optim_api_version='1.0'):
|
||||
super(AuditShellTest, self).setUp(
|
||||
os_infra_optim_api_version=os_infra_optim_api_version)
|
||||
|
||||
# goal mock
|
||||
p_goal_manager = mock.patch.object(resource, 'GoalManager')
|
||||
@@ -155,8 +168,8 @@ class AuditShellTest(base.CommandTestCase):
|
||||
self.cmd = shell.WatcherShell(stdout=self.stdout)
|
||||
|
||||
def test_do_audit_list(self):
|
||||
audit1 = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit2 = resource.Audit(mock.Mock(), AUDIT_2)
|
||||
audit1 = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
audit2 = resource.Audit(mock.Mock(), self.AUDIT_2)
|
||||
self.m_audit_mgr.list.return_value = [
|
||||
audit1, audit2]
|
||||
|
||||
@@ -173,7 +186,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
self.m_audit_mgr.list.assert_called_once_with(detail=False)
|
||||
|
||||
def test_do_audit_list_marker(self):
|
||||
audit2 = resource.Audit(mock.Mock(), AUDIT_2)
|
||||
audit2 = resource.Audit(mock.Mock(), self.AUDIT_2)
|
||||
self.m_audit_mgr.list.return_value = [audit2]
|
||||
|
||||
exit_code, results = self.run_cmd(
|
||||
@@ -190,8 +203,8 @@ class AuditShellTest(base.CommandTestCase):
|
||||
marker='5869da81-4876-4687-a1ed-12cd64cf53d9')
|
||||
|
||||
def test_do_audit_list_detail(self):
|
||||
audit1 = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit2 = resource.Audit(mock.Mock(), AUDIT_2)
|
||||
audit1 = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
audit2 = resource.Audit(mock.Mock(), self.AUDIT_2)
|
||||
self.m_audit_mgr.list.return_value = [
|
||||
audit1, audit2]
|
||||
|
||||
@@ -208,7 +221,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
self.m_audit_mgr.list.assert_called_once_with(detail=True)
|
||||
|
||||
def test_do_audit_show_by_uuid(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.get.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -222,7 +235,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
'5869da81-4876-4687-a1ed-12cd64cf53d9')
|
||||
|
||||
def test_do_audit_show_by_name(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.get.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -275,7 +288,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
'5b157edd-5a7e-4aaa-b511-f7b33ec86e9f')
|
||||
|
||||
def test_do_audit_update(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.update.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -291,7 +304,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
[{'op': 'replace', 'path': '/state', 'value': 'PENDING'}])
|
||||
|
||||
def test_do_audit_update_by_name(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.update.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -306,7 +319,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
[{'op': 'replace', 'path': '/state', 'value': 'PENDING'}])
|
||||
|
||||
def test_do_audit_create_with_audit_template_uuid(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_3)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_3)
|
||||
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
|
||||
self.m_audit_template_mgr.get.return_value = audit_template
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
@@ -325,7 +338,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_audit_template_name(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_3)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_3)
|
||||
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
|
||||
self.m_audit_template_mgr.get.return_value = audit_template
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
@@ -343,7 +356,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_goal(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -360,7 +373,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_goal_and_strategy(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -379,7 +392,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_type(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -396,7 +409,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_parameter(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -414,8 +427,25 @@ class AuditShellTest(base.CommandTestCase):
|
||||
parameters={'para1': 10, 'para2': 20}
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_type_event(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -g fc087747-61be-4aad-8126-b701731ae836 -t EVENT')
|
||||
|
||||
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',
|
||||
auto_trigger=False,
|
||||
audit_type='EVENT'
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_type_continuous(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -434,7 +464,7 @@ class AuditShellTest(base.CommandTestCase):
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_name(self):
|
||||
audit = resource.Audit(mock.Mock(), AUDIT_1)
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
@@ -452,3 +482,219 @@ class AuditShellTest(base.CommandTestCase):
|
||||
interval='3600',
|
||||
name='my_audit'
|
||||
)
|
||||
|
||||
|
||||
class AuditShellTestv11(AuditShellTest):
|
||||
def setUp(self):
|
||||
super(AuditShellTestv11, self).setUp(os_infra_optim_api_version='1.1')
|
||||
v11 = dict(start_time=None, end_time=None)
|
||||
for audit in (self.AUDIT_1, self.AUDIT_2, self.AUDIT_3):
|
||||
audit.update(v11)
|
||||
self.FIELDS.extend(['start_time', 'end_time'])
|
||||
self.FIELD_LABELS.extend(['Start Time', 'End Time'])
|
||||
|
||||
|
||||
class AuditShellTestv12(AuditShellTest):
|
||||
def setUp(self):
|
||||
super(AuditShellTestv12, self).setUp(os_infra_optim_api_version='1.2')
|
||||
v11 = dict(start_time=None, end_time=None)
|
||||
v12 = dict(force=False)
|
||||
for audit in (self.AUDIT_1, self.AUDIT_2, self.AUDIT_3):
|
||||
audit.update(v11)
|
||||
audit.update(v12)
|
||||
self.FIELDS.extend(['start_time', 'end_time', 'force'])
|
||||
self.FIELD_LABELS.extend(['Start Time', 'End Time', 'Force'])
|
||||
|
||||
def test_do_audit_create_with_force(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_3)
|
||||
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
|
||||
self.m_audit_template_mgr.get.return_value = audit_template
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -a f8e47706-efcf-49a4-a5c4-af604eb492f2 --force')
|
||||
|
||||
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(
|
||||
audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
audit_type='ONESHOT',
|
||||
auto_trigger=False,
|
||||
force=True
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_audit_template_uuid(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_3)
|
||||
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
|
||||
self.m_audit_template_mgr.get.return_value = audit_template
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -a f8e47706-efcf-49a4-a5c4-af604eb492f2')
|
||||
|
||||
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(
|
||||
audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
audit_type='ONESHOT',
|
||||
auto_trigger=False,
|
||||
force=False
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_audit_template_name(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_3)
|
||||
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
|
||||
self.m_audit_template_mgr.get.return_value = audit_template
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd('audit create -a at1')
|
||||
|
||||
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(
|
||||
audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2',
|
||||
auto_trigger=False,
|
||||
audit_type='ONESHOT',
|
||||
force=False
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_goal(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -g fc087747-61be-4aad-8126-b701731ae836')
|
||||
|
||||
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',
|
||||
auto_trigger=False,
|
||||
audit_type='ONESHOT',
|
||||
force=False
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_goal_and_strategy(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -g fc087747-61be-4aad-8126-b701731ae836 -s '
|
||||
'2cf86250-d309-4b81-818e-1537f3dba6e5')
|
||||
|
||||
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',
|
||||
strategy='2cf86250-d309-4b81-818e-1537f3dba6e5',
|
||||
auto_trigger=False,
|
||||
audit_type='ONESHOT',
|
||||
force=False
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_type(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -g fc087747-61be-4aad-8126-b701731ae836 -t ONESHOT')
|
||||
|
||||
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',
|
||||
auto_trigger=False,
|
||||
audit_type='ONESHOT',
|
||||
force=False
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_parameter(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -g fc087747-61be-4aad-8126-b701731ae836 -p para1=10 '
|
||||
'-p para2=20')
|
||||
|
||||
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',
|
||||
auto_trigger=False,
|
||||
parameters={'para1': 10, 'para2': 20},
|
||||
force=False
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_type_continuous(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -g fc087747-61be-4aad-8126-b701731ae836 '
|
||||
'-t CONTINUOUS -i 3600')
|
||||
|
||||
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='CONTINUOUS',
|
||||
auto_trigger=False,
|
||||
interval='3600',
|
||||
force=False
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_type_event(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -g fc087747-61be-4aad-8126-b701731ae836 -t EVENT')
|
||||
|
||||
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',
|
||||
auto_trigger=False,
|
||||
audit_type='EVENT',
|
||||
force=False
|
||||
)
|
||||
|
||||
def test_do_audit_create_with_name(self):
|
||||
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
|
||||
self.m_audit_mgr.create.return_value = audit
|
||||
|
||||
exit_code, result = self.run_cmd(
|
||||
'audit create -g fc087747-61be-4aad-8126-b701731ae836 '
|
||||
'-t CONTINUOUS -i 3600 --name my_audit')
|
||||
|
||||
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='CONTINUOUS',
|
||||
auto_trigger=False,
|
||||
interval='3600',
|
||||
name='my_audit',
|
||||
force=False
|
||||
)
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
import six
|
||||
|
||||
from watcherclient import shell
|
||||
|
||||
75
watcherclient/tests/unit/v1/test_data_model.py
Normal file
75
watcherclient/tests/unit/v1/test_data_model.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# Copyright 2019 ZTE corporation.
|
||||
# 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 watcherclient.tests.unit import utils
|
||||
import watcherclient.v1.data_model
|
||||
|
||||
DATA_MODEL = {
|
||||
'context': [{
|
||||
"server_uuid": "1bf91464-9b41-428d-a11e-af691e5563bb",
|
||||
"server_name": "fake-name",
|
||||
"server_state": "active",
|
||||
"node_uuid": "253e5dd0-9384-41ab-af13-4f2c2ce26112",
|
||||
"node_hostname": "localhost.localdomain",
|
||||
}]
|
||||
}
|
||||
|
||||
AUDIT = "81332bfc-36f8-444d-99e2-b7285d602528"
|
||||
|
||||
fake_responses = {
|
||||
'/v1/data_model/?data_model_type=compute':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
DATA_MODEL,
|
||||
),
|
||||
},
|
||||
'/v1/data_model/?audit_uuid=%s&data_model_type=compute' % AUDIT:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
DATA_MODEL,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class DataModelManagerTest(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DataModelManagerTest, self).setUp()
|
||||
self.api = utils.FakeAPI(fake_responses)
|
||||
self.mgr = watcherclient.v1.data_model.DataModelManager(self.api)
|
||||
|
||||
def test_data_model_list(self):
|
||||
data_model = self.mgr.list()
|
||||
expect = [
|
||||
('GET', '/v1/data_model/?data_model_type=compute', {}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(1, len(data_model.context))
|
||||
|
||||
def test_data_model_list_audit(self):
|
||||
data_model = self.mgr.list(
|
||||
audit='%s' % AUDIT)
|
||||
expect = [
|
||||
('GET', '/v1/data_model/?'
|
||||
'audit_uuid=81332bfc-36f8-444d-99e2-b7285d602528'
|
||||
'&data_model_type=compute',
|
||||
{}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(1, len(data_model.context))
|
||||
131
watcherclient/tests/unit/v1/test_data_model_shell.py
Normal file
131
watcherclient/tests/unit/v1/test_data_model_shell.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# Copyright 2019 ZTE Corporation.
|
||||
#
|
||||
# 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 unittest 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
|
||||
|
||||
|
||||
DATA_MODEL = {
|
||||
'context': [{
|
||||
"server_uuid": "1bf91464-9b41-428d-a11e-af691e5563bb",
|
||||
"server_name": "fake-name",
|
||||
"server_state": "active",
|
||||
"server_vcpus": "1",
|
||||
"server_memory": "512",
|
||||
"server_disk": "1",
|
||||
"node_uuid": "253e5dd0-9384-41ab-af13-4f2c2ce26112",
|
||||
"node_hostname": "localhost.localdomain",
|
||||
"node_vcpus": "4",
|
||||
"node_vcpu_ratio": "16.0",
|
||||
"node_memory": "16383",
|
||||
"node_memory_ratio": "1.5",
|
||||
"node_disk": "37",
|
||||
"node_disk_ratio": "1.0",
|
||||
"node_state": "up",
|
||||
}]
|
||||
}
|
||||
|
||||
LIST_RESULT = [{
|
||||
"Server UUID": "1bf91464-9b41-428d-a11e-af691e5563bb",
|
||||
"Server Name": "fake-name",
|
||||
"Server Vcpus": "1",
|
||||
"Server Memory": "512",
|
||||
"Server Disk": "1",
|
||||
"Server State": "active",
|
||||
"Node UUID": "253e5dd0-9384-41ab-af13-4f2c2ce26112",
|
||||
"Node Host Name": "localhost.localdomain",
|
||||
"Node Vcpus": "4",
|
||||
"Node Vcpu Ratio": "16.0",
|
||||
"Node Memory": "16383",
|
||||
"Node Memory Ratio": "1.5",
|
||||
"Node Disk": "37",
|
||||
"Node Disk Ratio": "1.0",
|
||||
"Node State": "up",
|
||||
}]
|
||||
|
||||
SHORT_LIST_RESULT = [{
|
||||
"Server UUID": "1bf91464-9b41-428d-a11e-af691e5563bb",
|
||||
"Server Name": "fake-name",
|
||||
"Server State": "active",
|
||||
"Node UUID": "253e5dd0-9384-41ab-af13-4f2c2ce26112",
|
||||
"Node Host Name": "localhost.localdomain",
|
||||
}]
|
||||
|
||||
|
||||
class DataModelShellTest(base.CommandTestCase):
|
||||
|
||||
SHORT_LIST_FIELDS = resource_fields.COMPUTE_MODEL_SHORT_LIST_FIELDS
|
||||
SHORT_LIST_FIELD_LABELS = (
|
||||
resource_fields.COMPUTE_MODEL_SHORT_LIST_FIELD_LABELS)
|
||||
FIELDS = resource_fields.COMPUTE_MODEL_LIST_FIELDS
|
||||
FIELD_LABELS = resource_fields.COMPUTE_MODEL_LIST_FIELD_LABELS
|
||||
|
||||
def setUp(self):
|
||||
super(self.__class__, self).setUp()
|
||||
|
||||
p_data_model_manager = mock.patch.object(
|
||||
resource, 'DataModelManager')
|
||||
|
||||
self.m_data_model_mgr_cls = p_data_model_manager.start()
|
||||
|
||||
self.addCleanup(p_data_model_manager.stop)
|
||||
|
||||
self.m_data_model_mgr = mock.Mock()
|
||||
|
||||
self.m_data_model_mgr_cls.return_value = self.m_data_model_mgr
|
||||
|
||||
self.stdout = six.StringIO()
|
||||
self.cmd = shell.WatcherShell(stdout=self.stdout)
|
||||
|
||||
def test_do_data_model_list(self):
|
||||
data_model = resource.DataModel(mock.Mock(), DATA_MODEL)
|
||||
self.m_data_model_mgr.list.return_value = data_model
|
||||
|
||||
exit_code, results = self.run_cmd('datamodel list')
|
||||
|
||||
self.assertEqual(0, exit_code)
|
||||
expect_values = sorted(SHORT_LIST_RESULT[0].values())
|
||||
result_values = sorted(results[0].values())
|
||||
self.assertEqual(expect_values, result_values)
|
||||
|
||||
def test_do_data_model_list_detail(self):
|
||||
data_model = resource.DataModel(mock.Mock(), DATA_MODEL)
|
||||
self.m_data_model_mgr.list.return_value = data_model
|
||||
|
||||
exit_code, results = self.run_cmd('datamodel list --detail')
|
||||
|
||||
self.assertEqual(0, exit_code)
|
||||
expect_values = sorted(LIST_RESULT[0].values())
|
||||
result_values = sorted(results[0].values())
|
||||
self.assertEqual(expect_values, result_values)
|
||||
|
||||
def test_do_data_model_list_filter_by_audit(self):
|
||||
data_model = resource.DataModel(mock.Mock(), DATA_MODEL)
|
||||
self.m_data_model_mgr.list.return_value = data_model
|
||||
|
||||
exit_code, results = self.run_cmd(
|
||||
'datamodel list --audit '
|
||||
'770ef053-ecb3-48b0-85b5-d55a2dbc6588')
|
||||
|
||||
self.assertEqual(0, exit_code)
|
||||
expect_values = sorted(SHORT_LIST_RESULT[0].values())
|
||||
result_values = sorted(results[0].values())
|
||||
self.assertEqual(expect_values, result_values)
|
||||
@@ -98,6 +98,16 @@ fake_responses_sorting = {
|
||||
},
|
||||
}
|
||||
|
||||
fake_responses_marker = {
|
||||
'/v1/goals/?marker=fc087747-61be-4aad-8126-b701731ae836':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"goals": [GOAL2]}
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class GoalManagerTest(testtools.TestCase):
|
||||
|
||||
@@ -132,6 +142,17 @@ class GoalManagerTest(testtools.TestCase):
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertThat(goals, matchers.HasLength(1))
|
||||
|
||||
def test_goals_list_marker(self):
|
||||
self.api = utils.FakeAPI(fake_responses_marker)
|
||||
self.mgr = watcherclient.v1.goal.GoalManager(self.api)
|
||||
goals = self.mgr.list(marker=GOAL1['uuid'])
|
||||
expect = [
|
||||
('GET', '/v1/goals/?marker=fc087747-61be-4aad-8126-b701731ae836',
|
||||
{}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(1, len(goals))
|
||||
|
||||
def test_goals_list_pagination_no_limit(self):
|
||||
self.api = utils.FakeAPI(fake_responses_pagination)
|
||||
self.mgr = watcherclient.v1.goal.GoalManager(self.api)
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
import six
|
||||
|
||||
from watcherclient import shell
|
||||
@@ -93,6 +94,23 @@ class GoalShellTest(base.CommandTestCase):
|
||||
|
||||
self.m_goal_mgr.list.assert_called_once_with(detail=False)
|
||||
|
||||
def test_do_goal_list_marker(self):
|
||||
goal2 = resource.Goal(mock.Mock(), GOAL_2)
|
||||
self.m_goal_mgr.list.return_value = [goal2]
|
||||
|
||||
exit_code, results = self.run_cmd(
|
||||
'goal list --marker fc087747-61be-4aad-8126-b701731ae836')
|
||||
|
||||
self.assertEqual(0, exit_code)
|
||||
self.assertEqual(
|
||||
[self.resource_as_dict(goal2, self.SHORT_LIST_FIELDS,
|
||||
self.SHORT_LIST_FIELD_LABELS)],
|
||||
results)
|
||||
|
||||
self.m_goal_mgr.list.assert_called_once_with(
|
||||
detail=False,
|
||||
marker='fc087747-61be-4aad-8126-b701731ae836')
|
||||
|
||||
def test_do_goal_list_detail(self):
|
||||
goal1 = resource.Goal(mock.Mock(), GOAL_1)
|
||||
goal2 = resource.Goal(mock.Mock(), GOAL_2)
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
import six
|
||||
|
||||
from watcherclient import shell
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
import six
|
||||
|
||||
from watcherclient import shell
|
||||
|
||||
@@ -107,6 +107,16 @@ fake_responses_sorting = {
|
||||
},
|
||||
}
|
||||
|
||||
fake_responses_marker = {
|
||||
'/v1/strategies/?marker=2cf86250-d309-4b81-818e-1537f3dba6e5':
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
{"strategies": [STRATEGY2]}
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class StrategyManagerTest(testtools.TestCase):
|
||||
|
||||
@@ -131,6 +141,19 @@ class StrategyManagerTest(testtools.TestCase):
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(1, len(strategies))
|
||||
|
||||
def test_strategies_list_marker(self):
|
||||
self.api = utils.FakeAPI(fake_responses_marker)
|
||||
self.mgr = watcherclient.v1.strategy.StrategyManager(self.api)
|
||||
strategies = self.mgr.list(marker=STRATEGY1['uuid'])
|
||||
expect = [
|
||||
('GET',
|
||||
'/v1/strategies/?marker=2cf86250-d309-4b81-818e-1537f3dba6e5',
|
||||
{},
|
||||
None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(1, len(strategies))
|
||||
|
||||
def test_strategies_list_limit(self):
|
||||
self.api = utils.FakeAPI(fake_responses_pagination)
|
||||
self.mgr = watcherclient.v1.strategy.StrategyManager(self.api)
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
import six
|
||||
from unittest import mock
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from watcherclient import shell
|
||||
from watcherclient.tests.unit.v1 import base
|
||||
@@ -90,6 +90,23 @@ class StrategyShellTest(base.CommandTestCase):
|
||||
|
||||
self.m_strategy_mgr.list.assert_called_once_with(detail=False)
|
||||
|
||||
def test_do_strategy_list_marker(self):
|
||||
strategy2 = resource.Strategy(mock.Mock(), STRATEGY_2)
|
||||
self.m_strategy_mgr.list.return_value = [strategy2]
|
||||
|
||||
exit_code, results = self.run_cmd(
|
||||
'strategy list --marker 2cf86250-d309-4b81-818e-1537f3dba6e5')
|
||||
|
||||
self.assertEqual(0, exit_code)
|
||||
self.assertEqual(
|
||||
[self.resource_as_dict(strategy2, self.SHORT_LIST_FIELDS,
|
||||
self.SHORT_LIST_FIELD_LABELS)],
|
||||
results)
|
||||
|
||||
self.m_strategy_mgr.list.assert_called_once_with(
|
||||
detail=False,
|
||||
marker='2cf86250-d309-4b81-818e-1537f3dba6e5')
|
||||
|
||||
def test_do_strategy_list_detail(self):
|
||||
strategy1 = resource.Strategy(mock.Mock(), STRATEGY_1)
|
||||
strategy2 = resource.Strategy(mock.Mock(), STRATEGY_2)
|
||||
|
||||
@@ -17,6 +17,7 @@ from watcherclient.v1 import action
|
||||
from watcherclient.v1 import action_plan
|
||||
from watcherclient.v1 import audit
|
||||
from watcherclient.v1 import audit_template
|
||||
from watcherclient.v1 import data_model
|
||||
from watcherclient.v1 import goal
|
||||
from watcherclient.v1 import scoring_engine
|
||||
from watcherclient.v1 import service
|
||||
@@ -38,9 +39,12 @@ Service = service.Service
|
||||
ServiceManager = service.ServiceManager
|
||||
Strategy = strategy.Strategy
|
||||
StrategyManager = strategy.StrategyManager
|
||||
DataModel = data_model.DataModel
|
||||
DataModelManager = data_model.DataModelManager
|
||||
|
||||
__all__ = (
|
||||
"Action", "ActionManager", "ActionPlan", "ActionPlanManager",
|
||||
"Audit", "AuditManager", "AuditTemplate", "AuditTemplateManager",
|
||||
"Goal", "GoalManager", "ScoringEngine", "ScoringEngineManager",
|
||||
"Service", "ServiceManager", "Strategy", "StrategyManager")
|
||||
"Service", "ServiceManager", "Strategy", "StrategyManager",
|
||||
"DataModel", "DataModelManager")
|
||||
|
||||
@@ -27,8 +27,13 @@ class ActionPlanManager(base.Manager):
|
||||
resource_class = ActionPlan
|
||||
|
||||
@staticmethod
|
||||
def _path(id=None):
|
||||
return '/v1/action_plans/%s' % id if id else '/v1/action_plans'
|
||||
def _path(id=None, q_param=None):
|
||||
if id and q_param:
|
||||
return '/v1/action_plans/%s/%s' % (id, q_param)
|
||||
elif id:
|
||||
return '/v1/action_plans/%s' % id
|
||||
else:
|
||||
return '/v1/action_plans'
|
||||
|
||||
def list(self, audit=None, limit=None, sort_key=None,
|
||||
sort_dir=None, detail=False, marker=None):
|
||||
@@ -90,8 +95,7 @@ class ActionPlanManager(base.Manager):
|
||||
return self._update(self._path(action_plan_id), patch)
|
||||
|
||||
def start(self, action_plan_id):
|
||||
patch = [{'op': 'replace', 'value': 'PENDING', 'path': '/state'}]
|
||||
return self._update(self._path(action_plan_id), patch)
|
||||
return self._start(self._path(action_plan_id, 'start'))
|
||||
|
||||
def cancel(self, action_plan_id):
|
||||
action_plan = self.get(action_plan_id)
|
||||
|
||||
@@ -70,7 +70,7 @@ class ShowActionPlan(command.ShowOne):
|
||||
yaml_format.YAMLFormatter().emit_one(
|
||||
column_names=list(resource.capitalize()
|
||||
for resource in formatted_global_efficacy),
|
||||
data=[value for value in formatted_global_efficacy.itervalues()],
|
||||
data=[value for value in formatted_global_efficacy.values()],
|
||||
stdout=out,
|
||||
parsed_args=parsed_args,
|
||||
)
|
||||
@@ -200,47 +200,6 @@ class ListActionPlan(command.Lister):
|
||||
(utils.get_item_properties(item, fields) for item in data))
|
||||
|
||||
|
||||
class CreateActionPlan(command.ShowOne):
|
||||
"""Create new audit."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateActionPlan, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'-a', '--audit-template',
|
||||
required=True,
|
||||
dest='audit_template_uuid',
|
||||
metavar='<audit_template>',
|
||||
help=_('Audit template used for this audit (name or uuid).'))
|
||||
parser.add_argument(
|
||||
'-t', '--audit_type',
|
||||
dest='audit_type',
|
||||
metavar='<audit_type>',
|
||||
default='ONESHOT',
|
||||
choices=['ONESHOT', 'CONTINUOUS'],
|
||||
help=_("Audit type. It must be ONESHOT or CONTINUOUS. "
|
||||
"Default is ONESHOT."))
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = getattr(self.app.client_manager, "infra-optim")
|
||||
|
||||
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'):
|
||||
if not uuidutils.is_uuid_like(fields['audit_template_uuid']):
|
||||
fields['audit_template_uuid'] = client.audit_template.get(
|
||||
fields['audit_template_uuid']).uuid
|
||||
|
||||
audit = client.audit.create(**fields)
|
||||
|
||||
columns = res_fields.ACTION_PLAN_FIELDS
|
||||
column_headers = res_fields.ACTION_PLAN_FIELD_LABELS
|
||||
|
||||
return column_headers, utils.get_item_properties(audit, columns)
|
||||
|
||||
|
||||
class UpdateActionPlan(command.ShowOne):
|
||||
"""Update action plan command."""
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ from watcherclient import exceptions as exc
|
||||
|
||||
CREATION_ATTRIBUTES = ['audit_template_uuid', 'audit_type', 'interval',
|
||||
'parameters', 'goal', 'strategy', 'auto_trigger',
|
||||
'name']
|
||||
'name', 'start_time', 'end_time', 'force']
|
||||
|
||||
|
||||
class Audit(base.Resource):
|
||||
|
||||
@@ -13,16 +13,34 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import copy
|
||||
|
||||
from osc_lib import utils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from watcherclient._i18n import _
|
||||
from watcherclient.common import api_versioning
|
||||
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
|
||||
|
||||
|
||||
def drop_unsupported_field(app_args, fields, field_labels):
|
||||
fields = copy.copy(fields)
|
||||
field_labels = copy.copy(field_labels)
|
||||
api_ver = app_args.os_infra_optim_api_version
|
||||
if not api_versioning.allow_start_end_audit_time(api_ver):
|
||||
for field, label in zip(('start_time', 'end_time'),
|
||||
('Start Time', 'End Time')):
|
||||
fields.remove(field)
|
||||
field_labels.remove(label)
|
||||
if not api_versioning.launch_audit_forced(api_ver):
|
||||
fields.remove('force')
|
||||
field_labels.remove('Force')
|
||||
return fields, field_labels
|
||||
|
||||
|
||||
class ShowAudit(command.ShowOne):
|
||||
"""Show detailed information about a given audit."""
|
||||
|
||||
@@ -47,6 +65,8 @@ class ShowAudit(command.ShowOne):
|
||||
|
||||
columns = res_fields.AUDIT_FIELDS
|
||||
column_headers = res_fields.AUDIT_FIELD_LABELS
|
||||
columns, column_headers = drop_unsupported_field(
|
||||
self.app_args, columns, column_headers)
|
||||
|
||||
return column_headers, utils.get_item_properties(audit, columns)
|
||||
|
||||
@@ -118,6 +138,10 @@ class ListAudit(command.Lister):
|
||||
fields = res_fields.AUDIT_SHORT_LIST_FIELDS
|
||||
field_labels = res_fields.AUDIT_SHORT_LIST_FIELD_LABELS
|
||||
|
||||
if parsed_args.detail:
|
||||
fields, field_labels = drop_unsupported_field(
|
||||
self.app_args, fields, field_labels)
|
||||
|
||||
params.update(common_utils.common_params_for_list(
|
||||
parsed_args, fields, field_labels))
|
||||
|
||||
@@ -143,8 +167,8 @@ class CreateAudit(command.ShowOne):
|
||||
dest='audit_type',
|
||||
metavar='<audit_type>',
|
||||
default='ONESHOT',
|
||||
choices=['ONESHOT', 'CONTINUOUS'],
|
||||
help=_("Audit type. It must be ONESHOT or CONTINUOUS. "
|
||||
choices=['ONESHOT', 'CONTINUOUS', 'EVENT'],
|
||||
help=_("Audit type. It must be ONESHOT, CONTINUOUS or EVENT. "
|
||||
"Default is ONESHOT."))
|
||||
parser.add_argument(
|
||||
'-p', '--parameter',
|
||||
@@ -158,7 +182,7 @@ class CreateAudit(command.ShowOne):
|
||||
dest='interval',
|
||||
metavar='<interval>',
|
||||
help=_('Audit interval (in seconds or cron format). '
|
||||
'Cron inteval can be used like: "*/5 * * * *". '
|
||||
'Cron interval can be used like: ``*/5 * * * *``. '
|
||||
'Only used if the audit is CONTINUOUS.'))
|
||||
parser.add_argument(
|
||||
'-g', '--goal',
|
||||
@@ -187,6 +211,24 @@ class CreateAudit(command.ShowOne):
|
||||
dest='name',
|
||||
metavar='<name>',
|
||||
help=_('Name for this audit.'))
|
||||
parser.add_argument(
|
||||
'--start-time',
|
||||
dest='start_time',
|
||||
metavar='<start_time>',
|
||||
help=_('CONTINUOUS audit local start time. '
|
||||
'Format: YYYY-MM-DD hh:mm:ss'))
|
||||
parser.add_argument(
|
||||
'--end-time',
|
||||
dest='end_time',
|
||||
metavar='<end_time>',
|
||||
help=_('CONTINUOUS audit local end time. '
|
||||
'Format: YYYY-MM-DD hh:mm:ss'))
|
||||
parser.add_argument(
|
||||
'--force',
|
||||
dest='force',
|
||||
action='store_true',
|
||||
help=_('Launch audit even if action plan '
|
||||
'is ongoing. default is False'))
|
||||
|
||||
return parser
|
||||
|
||||
@@ -194,25 +236,27 @@ class CreateAudit(command.ShowOne):
|
||||
client = getattr(self.app.client_manager, "infra-optim")
|
||||
|
||||
field_list = ['audit_template_uuid', 'audit_type', 'parameters',
|
||||
'interval', 'goal', 'strategy', 'auto_trigger', 'name']
|
||||
'interval', 'goal', 'strategy', 'auto_trigger',
|
||||
'name']
|
||||
|
||||
api_ver = self.app_args.os_infra_optim_api_version
|
||||
if api_versioning.allow_start_end_audit_time(api_ver):
|
||||
if parsed_args.start_time is not None:
|
||||
field_list.append('start_time')
|
||||
if parsed_args.end_time is not None:
|
||||
field_list.append('end_time')
|
||||
if api_versioning.launch_audit_forced(api_ver):
|
||||
if parsed_args.force is not None:
|
||||
field_list.append('force')
|
||||
|
||||
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
|
||||
if k in field_list and v is not None)
|
||||
fields = common_utils.args_array_to_dict(fields, 'parameters')
|
||||
|
||||
if fields.get('goal'):
|
||||
if not uuidutils.is_uuid_like(fields['goal']):
|
||||
fields['goal'] = client.goal.get(fields['goal']).uuid
|
||||
|
||||
if fields.get('audit_template_uuid'):
|
||||
if not uuidutils.is_uuid_like(fields['audit_template_uuid']):
|
||||
fields['audit_template_uuid'] = client.audit_template.get(
|
||||
fields['audit_template_uuid']).uuid
|
||||
# optional
|
||||
if fields.get('strategy'):
|
||||
if not uuidutils.is_uuid_like(fields['strategy']):
|
||||
fields['strategy'] = client.strategy.get(
|
||||
fields['strategy']).uuid
|
||||
|
||||
audit = client.audit.create(**fields)
|
||||
if audit.strategy_name is None:
|
||||
@@ -220,6 +264,8 @@ class CreateAudit(command.ShowOne):
|
||||
|
||||
columns = res_fields.AUDIT_FIELDS
|
||||
column_headers = res_fields.AUDIT_FIELD_LABELS
|
||||
columns, column_headers = drop_unsupported_field(
|
||||
self.app_args, columns, column_headers)
|
||||
|
||||
return column_headers, utils.get_item_properties(audit, columns)
|
||||
|
||||
@@ -261,6 +307,9 @@ class UpdateAudit(command.ShowOne):
|
||||
columns = res_fields.AUDIT_FIELDS
|
||||
column_headers = res_fields.AUDIT_FIELD_LABELS
|
||||
|
||||
columns, column_headers = drop_unsupported_field(
|
||||
self.app_args, columns, column_headers)
|
||||
|
||||
return column_headers, utils.get_item_properties(audit, columns)
|
||||
|
||||
|
||||
|
||||
@@ -167,8 +167,8 @@ class CreateAuditTemplate(command.ShowOne):
|
||||
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"
|
||||
"YAML example::\n"
|
||||
"\n"
|
||||
" - compute:\n"
|
||||
" - host_aggregates:\n"
|
||||
" - id: 1\n"
|
||||
@@ -201,48 +201,49 @@ class CreateAuditTemplate(command.ShowOne):
|
||||
" - uuid: UUID1\n"
|
||||
" - uuid: UUID2\n"
|
||||
"\n"
|
||||
"JSON example:\n"
|
||||
"[\n"
|
||||
" {\"compute\":\n"
|
||||
" [{\"host_aggregates\": [\n"
|
||||
" {\"id\": 1},\n"
|
||||
" {\"id\": 2},\n"
|
||||
" {\"id\": 3}]},\n"
|
||||
" {\"availability_zones\": [\n"
|
||||
" {\"name\": \"AZ1\"},\n"
|
||||
" {\"name\": \"AZ2\"}]},\n"
|
||||
" {\"exclude\": [\n"
|
||||
" {\"instances\": [\n"
|
||||
" {\"uuid\": \"UUID1\"},\n"
|
||||
" {\"uuid\": \"UUID2\"}\n"
|
||||
" ]},\n"
|
||||
" {\"compute_nodes\": [\n"
|
||||
" {\"name\": \"compute1\"}\n"
|
||||
" ]}\n"
|
||||
" ]}]\n"
|
||||
" },\n"
|
||||
" {\"storage\":\n"
|
||||
" [{\"availability_zones\": [\n"
|
||||
" {\"name\": \"AZ1\"},\n"
|
||||
" {\"name\": \"AZ2\"}]},\n"
|
||||
" {\"volume_types\": [\n"
|
||||
" {\"name\": \"lvm1\"},\n"
|
||||
" {\"name\": \"lvm2\"}]},\n"
|
||||
" {\"exclude\": [\n"
|
||||
" {\"storage_pools\": [\n"
|
||||
" {\"name\": \"host0@backend0#pool0\"},\n"
|
||||
" {\"name\": \"host1@backend1#pool1\"}\n"
|
||||
" ]},\n"
|
||||
" {\"volumes\": [\n"
|
||||
" {\"uuid\": \"UUID1\"},\n"
|
||||
" {\"uuid\": \"UUID2\"}\n"
|
||||
" ]},\n"
|
||||
" {\"projects\": [\n"
|
||||
" {\"uuid\": \"UUID1\"},\n"
|
||||
" {\"uuid\": \"UUID2\"}\n"
|
||||
" ]},\n"
|
||||
" ]}]\n"
|
||||
" }\n"
|
||||
"JSON example::\n"
|
||||
"\n"
|
||||
" [\n"
|
||||
" {\"compute\":\n"
|
||||
" [{\"host_aggregates\": [\n"
|
||||
" {\"id\": 1},\n"
|
||||
" {\"id\": 2},\n"
|
||||
" {\"id\": 3}]},\n"
|
||||
" {\"availability_zones\": [\n"
|
||||
" {\"name\": \"AZ1\"},\n"
|
||||
" {\"name\": \"AZ2\"}]},\n"
|
||||
" {\"exclude\": [\n"
|
||||
" {\"instances\": [\n"
|
||||
" {\"uuid\": \"UUID1\"},\n"
|
||||
" {\"uuid\": \"UUID2\"}\n"
|
||||
" ]},\n"
|
||||
" {\"compute_nodes\": [\n"
|
||||
" {\"name\": \"compute1\"}\n"
|
||||
" ]}\n"
|
||||
" ]}]\n"
|
||||
" },\n"
|
||||
" {\"storage\":\n"
|
||||
" [{\"availability_zones\": [\n"
|
||||
" {\"name\": \"AZ1\"},\n"
|
||||
" {\"name\": \"AZ2\"}]},\n"
|
||||
" {\"volume_types\": [\n"
|
||||
" {\"name\": \"lvm1\"},\n"
|
||||
" {\"name\": \"lvm2\"}]},\n"
|
||||
" {\"exclude\": [\n"
|
||||
" {\"storage_pools\": [\n"
|
||||
" {\"name\": \"host0@backend0#pool0\"},\n"
|
||||
" {\"name\": \"host1@backend1#pool1\"}\n"
|
||||
" ]},\n"
|
||||
" {\"volumes\": [\n"
|
||||
" {\"uuid\": \"UUID1\"},\n"
|
||||
" {\"uuid\": \"UUID2\"}\n"
|
||||
" ]},\n"
|
||||
" {\"projects\": [\n"
|
||||
" {\"uuid\": \"UUID1\"},\n"
|
||||
" {\"uuid\": \"UUID2\"}\n"
|
||||
" ]},\n"
|
||||
" ]}]\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -31,19 +31,19 @@ class Client(object):
|
||||
|
||||
def __init__(self, endpoint=None, *args, **kwargs):
|
||||
"""Initialize a new client for the Watcher v1 API."""
|
||||
if kwargs.get('os_watcher_api_version'):
|
||||
if kwargs.get('os_infra_optim_api_version'):
|
||||
kwargs['api_version_select_state'] = "user"
|
||||
else:
|
||||
if not endpoint:
|
||||
raise exceptions.EndpointException(
|
||||
_("Must provide 'endpoint' if os_watcher_api_version "
|
||||
_("Must provide 'endpoint' if os_infra_optim_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
|
||||
kwargs['os_infra_optim_api_version'] = httpclient.DEFAULT_VER
|
||||
|
||||
self.http_client = httpclient._construct_http_client(
|
||||
endpoint, *args, **kwargs)
|
||||
@@ -56,3 +56,4 @@ class Client(object):
|
||||
self.scoring_engine = v1.ScoringEngineManager(self.http_client)
|
||||
self.service = v1.ServiceManager(self.http_client)
|
||||
self.strategy = v1.StrategyManager(self.http_client)
|
||||
self.data_model = v1.DataModelManager(self.http_client)
|
||||
|
||||
56
watcherclient/v1/data_model.py
Normal file
56
watcherclient/v1/data_model.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Copyright 2019 ZTE Corporation.
|
||||
#
|
||||
# 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 DataModel(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<DataModel %s>" % self._info
|
||||
|
||||
|
||||
class DataModelManager(base.Manager):
|
||||
resource_class = DataModel
|
||||
|
||||
@staticmethod
|
||||
def _path(filters=None):
|
||||
if filters:
|
||||
path = '/v1/data_model/%s' % filters
|
||||
else:
|
||||
path = '/v1/data_model'
|
||||
return path
|
||||
|
||||
def list(self, data_model_type='compute', audit=None):
|
||||
"""Retrieve a list of data model.
|
||||
|
||||
:param data_model_type: The type of data model user wants to list.
|
||||
Supported values: compute.
|
||||
Future support values: storage, baremetal.
|
||||
The default value is compute.
|
||||
:param audit: The UUID of the audit, used to filter data model
|
||||
by the scope in audit.
|
||||
|
||||
:returns: A list of data model.
|
||||
|
||||
"""
|
||||
path = ''
|
||||
filters = utils.common_filters()
|
||||
|
||||
if audit:
|
||||
filters.append('audit_uuid=%s' % audit)
|
||||
filters.append('data_model_type=%s' % data_model_type)
|
||||
|
||||
path += '?' + '&'.join(filters)
|
||||
|
||||
return self._list(self._path(path))[0]
|
||||
77
watcherclient/v1/data_model_shell.py
Normal file
77
watcherclient/v1/data_model_shell.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# Copyright 2019 ZTE Corporation.
|
||||
#
|
||||
# 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._i18n import _
|
||||
from watcherclient.common import command
|
||||
from watcherclient import exceptions
|
||||
from watcherclient.v1 import resource_fields as res_fields
|
||||
|
||||
|
||||
class ListDataModel(command.Lister):
|
||||
"""List information on retrieved data model."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListDataModel, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--type',
|
||||
metavar='<type>',
|
||||
dest='type',
|
||||
help=_('Type of Datamodel user want to list. '
|
||||
'Supported values: compute. '
|
||||
'Future support values: storage, baremetal. '
|
||||
'Default type is compute.'))
|
||||
parser.add_argument(
|
||||
'--audit',
|
||||
metavar='<audit>',
|
||||
dest='audit',
|
||||
help=_('UUID of the audit'))
|
||||
parser.add_argument(
|
||||
'--detail',
|
||||
dest='detail',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_("Show detailed information about data model."))
|
||||
return parser
|
||||
|
||||
def get_tuple(self, dic, fields):
|
||||
ret_tup = []
|
||||
for item in fields:
|
||||
ret_tup.append(dic.get(item))
|
||||
return tuple(ret_tup)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = getattr(self.app.client_manager, "infra-optim")
|
||||
allowed_type = ['compute', 'storage', 'baremetal']
|
||||
params = {}
|
||||
if parsed_args.audit:
|
||||
params["audit"] = parsed_args.audit
|
||||
if parsed_args.type:
|
||||
if parsed_args.type not in allowed_type:
|
||||
raise exceptions.CommandError(
|
||||
'Type %s error, '
|
||||
'Please check the valid type!' % parsed_args.type)
|
||||
params["data_model_type"] = parsed_args.type
|
||||
try:
|
||||
data_model = client.data_model.list(**params)
|
||||
except exceptions.HTTPNotFound as exc:
|
||||
raise exceptions.CommandError(str(exc))
|
||||
# TODO(chenker) Add Storage MODEL_FIELDS when using Storage Datamodel.
|
||||
if parsed_args.detail:
|
||||
fields = res_fields.COMPUTE_MODEL_LIST_FIELDS
|
||||
field_labels = res_fields.COMPUTE_MODEL_LIST_FIELD_LABELS
|
||||
else:
|
||||
fields = res_fields.COMPUTE_MODEL_SHORT_LIST_FIELDS
|
||||
field_labels = res_fields.COMPUTE_MODEL_SHORT_LIST_FIELD_LABELS
|
||||
return (field_labels,
|
||||
(self.get_tuple(item, fields) for item in data_model.context))
|
||||
@@ -29,7 +29,8 @@ class GoalManager(base.Manager):
|
||||
def _path(goal=None):
|
||||
return '/v1/goals/%s' % goal if goal else '/v1/goals'
|
||||
|
||||
def list(self, limit=None, sort_key=None, sort_dir=None, detail=False):
|
||||
def list(self, limit=None, sort_key=None, sort_dir=None, detail=False,
|
||||
marker=None):
|
||||
"""Retrieve a list of goal.
|
||||
|
||||
:param limit: The maximum number of results to return per
|
||||
@@ -49,13 +50,15 @@ class GoalManager(base.Manager):
|
||||
:param detail: Optional, boolean whether to return detailed information
|
||||
about audits.
|
||||
|
||||
:returns: A list of audits.
|
||||
:param marker: Optional, UUID of the last goal in the previous page.
|
||||
|
||||
:returns: A list of goals.
|
||||
|
||||
"""
|
||||
if limit is not None:
|
||||
limit = int(limit)
|
||||
|
||||
filters = utils.common_filters(limit, sort_key, sort_dir)
|
||||
filters = utils.common_filters(limit, sort_key, sort_dir, marker)
|
||||
path = ''
|
||||
if detail:
|
||||
path += 'detail'
|
||||
|
||||
@@ -96,6 +96,13 @@ class ListGoal(command.Lister):
|
||||
metavar='<direction>',
|
||||
choices=['asc', 'desc'],
|
||||
help=_('Sort direction: "asc" (the default) or "desc".'))
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
dest='marker',
|
||||
metavar='<marker>',
|
||||
default=None,
|
||||
help=_('UUID of the last goal in the previous page; '
|
||||
'displays list of goals after "marker".'))
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
@@ -32,12 +32,14 @@ AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Goal', 'Strategy']
|
||||
# Audit
|
||||
AUDIT_FIELDS = ['uuid', 'name', 'created_at', 'updated_at', 'deleted_at',
|
||||
'state', 'audit_type', 'parameters', 'interval', 'goal_name',
|
||||
'strategy_name', 'scope', 'auto_trigger', 'next_run_time']
|
||||
'strategy_name', 'scope', 'auto_trigger', 'next_run_time',
|
||||
'hostname', 'start_time', 'end_time', 'force']
|
||||
|
||||
AUDIT_FIELD_LABELS = ['UUID', 'Name', 'Created At', 'Updated At', 'Deleted At',
|
||||
'State', 'Audit Type', 'Parameters', 'Interval', 'Goal',
|
||||
'Strategy', 'Audit Scope', 'Auto Trigger',
|
||||
'Next Run Time']
|
||||
'Next Run Time', 'Hostname', 'Start Time', 'End Time',
|
||||
'Force']
|
||||
|
||||
AUDIT_SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type',
|
||||
'state', 'goal_name', 'strategy_name',
|
||||
@@ -49,11 +51,12 @@ AUDIT_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Audit Type', 'State', 'Goal',
|
||||
# Action Plan
|
||||
ACTION_PLAN_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at',
|
||||
'audit_uuid', 'strategy_name', 'state',
|
||||
'efficacy_indicators', 'global_efficacy']
|
||||
'efficacy_indicators', 'global_efficacy', 'hostname']
|
||||
|
||||
ACTION_PLAN_FIELD_LABELS = ['UUID', 'Created At', 'Updated At', 'Deleted At',
|
||||
'Audit', 'Strategy', 'State',
|
||||
'Efficacy indicators', 'Global efficacy']
|
||||
'Efficacy indicators', 'Global efficacy',
|
||||
'Hostname']
|
||||
|
||||
ACTION_PLAN_SHORT_LIST_FIELDS = ['uuid', 'audit_uuid', 'state',
|
||||
'updated_at', 'global_efficacy']
|
||||
@@ -95,6 +98,28 @@ STRATEGY_FIELDS = ['uuid', 'name', 'display_name', 'goal_name',
|
||||
STRATEGY_FIELD_LABELS = ['UUID', 'Name', 'Display name', 'Goal',
|
||||
'Parameters spec']
|
||||
|
||||
# Data Model
|
||||
|
||||
COMPUTE_MODEL_LIST_FIELDS = [
|
||||
'server_uuid', 'server_name', 'server_vcpus',
|
||||
'server_memory', 'server_disk', 'server_state', 'node_uuid',
|
||||
'node_hostname', 'node_vcpus', 'node_vcpu_ratio', 'node_memory',
|
||||
'node_memory_ratio', 'node_disk', 'node_disk_ratio', 'node_state']
|
||||
|
||||
COMPUTE_MODEL_LIST_FIELD_LABELS = [
|
||||
'Server_UUID', 'Server Name', 'Server Vcpus',
|
||||
'Server Memory', 'Server Disk', 'Server State', 'Node UUID',
|
||||
'Node Host Name', 'Node Vcpus', 'Node Vcpu Ratio', 'Node Memory',
|
||||
'Node Memory Ratio', 'Node Disk', 'Node Disk Ratio', 'Node State']
|
||||
|
||||
COMPUTE_MODEL_SHORT_LIST_FIELDS = [
|
||||
'server_uuid', 'server_name',
|
||||
'server_state', 'node_uuid', 'node_hostname']
|
||||
|
||||
COMPUTE_MODEL_SHORT_LIST_FIELD_LABELS = [
|
||||
'Server UUID', 'Server Name',
|
||||
'Server State', 'Node UUID', 'Node Host Name']
|
||||
|
||||
STRATEGY_SHORT_LIST_FIELDS = ['uuid', 'name', 'display_name', 'goal_name']
|
||||
|
||||
STRATEGY_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Display name', 'Goal']
|
||||
|
||||
@@ -38,7 +38,7 @@ class StrategyManager(base.Manager):
|
||||
return path
|
||||
|
||||
def list(self, goal=None, limit=None, sort_key=None,
|
||||
sort_dir=None, detail=False):
|
||||
sort_dir=None, detail=False, marker=None):
|
||||
"""Retrieve a list of strategy.
|
||||
|
||||
:param goal: The UUID of the goal to filter by
|
||||
@@ -58,14 +58,15 @@ class StrategyManager(base.Manager):
|
||||
|
||||
:param detail: Optional, boolean whether to return detailed information
|
||||
about audits.
|
||||
|
||||
:param marker: Optional, UUID of the last strategy in the previous
|
||||
page.
|
||||
:returns: A list of audits.
|
||||
|
||||
"""
|
||||
if limit is not None:
|
||||
limit = int(limit)
|
||||
|
||||
filters = utils.common_filters(limit, sort_key, sort_dir)
|
||||
filters = utils.common_filters(limit, sort_key, sort_dir, marker)
|
||||
|
||||
if goal:
|
||||
filters.append(parse.urlencode(dict(goal=goal)))
|
||||
|
||||
@@ -123,7 +123,13 @@ class ListStrategy(command.Lister):
|
||||
metavar='<direction>',
|
||||
choices=['asc', 'desc'],
|
||||
help='Sort direction: "asc" (the default) or "desc".')
|
||||
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
dest='marker',
|
||||
metavar='<marker>',
|
||||
default=None,
|
||||
help=_('UUID of the last strategy in the previous page; '
|
||||
'displays list of strategies after "marker".'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
||||
Reference in New Issue
Block a user