Compare commits

..

7 Commits

Author SHA1 Message Date
OpenDev Sysadmins
582b52b1b8 OpenDev Migration Patch
This commit was bulk generated and pushed by the OpenDev sysadmins
as a part of the Git hosting and code review systems migration
detailed in these mailing list posts:

http://lists.openstack.org/pipermail/openstack-discuss/2019-March/003603.html
http://lists.openstack.org/pipermail/openstack-discuss/2019-April/004920.html

Attempts have been made to correct repository namespaces and
hostnames based on simple pattern matching, but it's possible some
were updated incorrectly or missed entirely. Please reach out to us
via the contact information listed at https://opendev.org/ with any
questions you may have.
2019-04-19 19:45:01 +00:00
Ian Wienand
4d0eb4c55a Replace openstack.org git:// URLs with https://
This is a mechanically generated change to replace openstack.org
git:// URLs with https:// equivalents.

This is in aid of a planned future move of the git hosting
infrastructure to a self-hosted instance of gitea (https://gitea.io),
which does not support the git wire protocol at this stage.

This update should result in no functional change.

For more information see the thread at

 http://lists.openstack.org/pipermail/openstack-discuss/2019-March/003825.html

Change-Id: I289933e0a3c5703f4d972a3c76eccde1bdddce0b
2019-03-25 07:29:14 +00:00
Zuul
3e9b0d678e Merge "import zuul job settings from project-config" into stable/rocky 2018-08-28 07:52:59 +00:00
Alexander Chadin
b34b746d1c fix watcher actionplan show command
This patch set is intended for Python 3.x support and replace
itervalues() method with values().

Change-Id: I6852239d2d6634a4304c87a1694e64479f560de1
Closes-Bug: #1786784
(cherry picked from commit c1fd569b76)
2018-08-20 13:52:59 +00:00
Nguyen Hai
baf0133c5b import zuul job settings from project-config
This is a mechanically generated patch to complete step 1 of moving
the zuul job settings out of project-config and into each project
repository.

Because there will be a separate patch on each branch, the branch
specifiers for branch-specific jobs have been removed.

Because this patch is generated by a script, there may be some
cosmetic changes to the layout of the YAML file(s) as the contents are
normalized.

See the python3-first goal document for details:
https://governance.openstack.org/tc/goals/stein/python3-first.html

Change-Id: I217c5b98e4398d105c1f352db25fb9f084262582
Story: #2002586
Task: #24344
2018-08-19 00:59:19 +09:00
e5bd6e00cb Update UPPER_CONSTRAINTS_FILE for stable/rocky
The new stable upper-constraints file is only available
after the openstack/requirements repository is branched.
This will happen around the RC1 timeframe.

Recheck and merge this change once the requirements
repository has been branched.

The CI system will work with this patch before the requirements
repository is branched because zuul configues the job to run
with a local copy of the file and defaults to the master branch.
However, accepting the patch will break the test configuration
on developers' local systems, so please wait until after the
requirements repository is branched to merge the patch.

Change-Id: Iaef151a13d99a2eb975283a2f175245547c455e1
2018-08-08 09:36:22 +00:00
827d474cc4 Update .gitreview for stable/rocky
Change-Id: I4d2d7491072d4470300d175ecd9c9143e4b798f5
2018-08-08 09:36:21 +00:00
79 changed files with 1594 additions and 1506 deletions

View File

@@ -1,11 +0,0 @@
[run]
branch = True
source = watcherclient
omit =
watcherclient/tests/*
[report]
ignore_errors = True
exclude_lines =
@abc.abstract
raise NotImplementedError

9
.gitignore vendored
View File

@@ -23,12 +23,11 @@ lib64
pip-log.txt
# Unit test / coverage reports
.coverage*
.coverage
.tox
nosetests.xml
.stestr/
.testrepository
.venv
.testrepository/
# Translations
*.mo
@@ -57,10 +56,6 @@ ChangeLog
sftp-config.json
/.idea/
/cover/
# Desktop Service Store
*.DS_Store
# Atom
.remote-sync.json

View File

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

View File

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

7
.testr.conf Normal file
View File

@@ -0,0 +1,7 @@
[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

View File

@@ -1,10 +1,18 @@
- project:
templates:
- openstack-cover-jobs
- openstack-python3-jobs
- publish-openstack-docs-pti
- openstack-python-jobs
- openstack-python35-jobs
- publish-openstack-sphinx-docs
- check-requirements
- openstackclient-plugin-jobs
check:
jobs:
- watcherclient-tempest-functional
- watcherclient-tempest-functional:
voting: false
- openstack-tox-lower-constraints
- openstack-tox-cover:
voting: false
gate:
jobs:
- openstack-tox-lower-constraints

View File

@@ -24,7 +24,7 @@ migration, increased energy efficiency and more!
* Free software: Apache license
* Wiki: https://wiki.openstack.org/wiki/Watcher
* Source: https://opendev.org/openstack/python-watcherclient
* Source: https://git.openstack.org/cgit/openstack/python-watcher
* Bugs: https://bugs.launchpad.net/watcher
Installation

2
babel.cfg Normal file
View File

@@ -0,0 +1,2 @@
[python: **.py]

View File

@@ -1,8 +0,0 @@
# 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

View File

@@ -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_INFRA_OPTIM_API_VERSION]``.
Defaults to ``env[OS_WATCHER_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>] [--marker <marker>]
[--sort-dir <direction>]
List information on retrieved actions.
@@ -186,10 +186,6 @@ 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
@@ -299,8 +295,7 @@ watcher actionplan list
[--fit-width] [--print-empty] [--noindent]
[--quote {all,minimal,none,nonnumeric}]
[--audit <audit>] [--detail] [--limit <limit>]
[--marker <actionplan>] [--sort-key <field>]
[--sort-dir <direction>]
[--sort-key <field>] [--sort-dir <direction>]
List information on retrieved action plans.
@@ -320,10 +315,6 @@ 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.
@@ -475,7 +466,7 @@ Delete audit command.
**Positional arguments:**
``<audit>``
UUID or name of the audit
UUID of the audit
**Optional arguments:**
@@ -541,7 +532,7 @@ Show detailed information about a given audit.
**Positional arguments:**
``<audit>``
UUID or name of the audit
UUID of the audit
**Optional arguments:**
@@ -565,7 +556,7 @@ Update audit command.
**Positional arguments:**
``<audit>``
UUID or name of the audit.
UUID of the audit.
``<op>``
Operation: 'add', 'replace', or 'remove'.
@@ -620,43 +611,38 @@ 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:
@@ -694,7 +680,7 @@ watcher audittemplate list
[--detail] [--goal <goal>]
[--strategy <strategy>] [--limit <limit>]
[--sort-key <field>]
[--sort-dir <direction>][--marker <marker>]
[--sort-dir <direction>]
List information on retrieved audit templates.
@@ -723,10 +709,6 @@ 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
@@ -799,7 +781,7 @@ watcher goal list
[--print-empty] [--noindent]
[--quote {all,minimal,none,nonnumeric}] [--detail]
[--limit <limit>] [--sort-key <field>]
[--sort-dir <direction>][--marker <marker>]
[--sort-dir <direction>]
List information on retrieved goals.
@@ -822,10 +804,6 @@ 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
@@ -863,7 +841,7 @@ watcher scoringengine list
[--quote {all,minimal,none,nonnumeric}]
[--detail] [--limit <limit>]
[--sort-key <field>]
[--sort-dir <direction>][--marker <marker>]
[--sort-dir <direction>]
List information on retrieved scoring engines.
@@ -886,10 +864,6 @@ 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
@@ -974,40 +948,6 @@ 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
@@ -1021,7 +961,6 @@ 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.
@@ -1047,10 +986,6 @@ 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
@@ -1075,28 +1010,3 @@ 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

View File

@@ -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/v3/
export OS_AUTH_URL=http://auth.example.com:5000/v2.0
The command line tool will attempt to reauthenticate using your
provided credentials for every request. You can override this behavior

View File

@@ -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/v3/
$ export OS_AUTH_URL=http://auth.example.com:5000/v2.0
The command-line tool will attempt to reauthenticate using the provided
credentials for every request. You can override this behavior by manually

View File

@@ -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/v3/
$ export OS_AUTH_URL=http://auth.example.com:5000/v2.0
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://opendev.org/openstack/python-watcherclient/src/branch/master/tools/watcher.bash_completion)
https://git.openstack.org/cgit/openstack/python-watcherclient/tree/tools/watcher.bash_completion)
to your terminal and then bash completion should work::
$ . watcher.bash_completion

View File

@@ -12,26 +12,20 @@
# 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']
@@ -42,8 +36,18 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = 'python-watcherclient'
copyright = 'OpenStack Foundation'
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()
# A list of ignored prefixes for module index sorting.
modindex_common_prefix = ['watcherclient.']
@@ -56,7 +60,7 @@ add_function_parentheses = True
add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'native'
pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------
@@ -79,27 +83,17 @@ latex_documents = [
(
'index',
'%s.tex' % project,
'%s Documentation' % project,
'OpenStack Foundation', 'manual'
u'%s Documentation' % project,
u'OpenStack Foundation', 'manual'
),
]
# 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 = ''
repository_name = 'openstack/python-watcherclient'
bug_project = 'python-watcherclient'
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"}

View File

@@ -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-discuss
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
Code Hosting
https://opendev.org/openstack/python-watcherclient
https://git.openstack.org/cgit/openstack/python-watcherclient
Code Review
https://review.opendev.org/#/q/status:open+project:openstack/python-watcherclient,n,z
https://review.openstack.org/#/q/status:open+project:openstack/python-watcherclient,n,z

View File

@@ -5,6 +5,6 @@ Installation
If you have `virtualenvwrapper <https://virtualenvwrapper.readthedocs.org/en/latest/install.html>`_ installed::
$ mkvirtualenv python-watcherclient
$ git clone https://opendev.org/openstack/python-watcherclient
$ git clone https://git.openstack.org/openstack/python-watcherclient
$ cd python-watcherclient && python setup.py install
$ pip install -r ./requirements.txt

View File

@@ -0,0 +1,8 @@
======================
Python API Reference
======================
.. toctree::
:maxdepth: 2
autoindex

View File

@@ -10,5 +10,5 @@ done so, you can use the API like so.
.. toctree::
:maxdepth: 2
api/modules
api/index
api_v1

View File

@@ -1,13 +1,14 @@
alabaster==0.7.10
appdirs==1.4.3
asn1crypto==0.23.0
Babel==2.5.3
certifi==2018.1.18
cffi==1.14.0
cffi==1.7.0
chardet==3.0.4
cliff==2.11.0
cmd2==0.8.2
coverage==4.0
cryptography==2.7
cryptography==2.1
debtcollector==1.19.0
decorator==4.2.1
deprecation==2.0
@@ -18,6 +19,7 @@ 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
@@ -31,11 +33,14 @@ 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
@@ -53,6 +58,7 @@ 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
@@ -64,16 +70,19 @@ python-dateutil==2.5.3
python-mimeparse==1.6.0
python-subunit==1.0.0
pytz==2018.3
PyYAML==3.13
PyYAML==3.12
requests==2.18.4
requestsexceptions==1.4.0
rfc3986==0.3.1
simplejson==3.13.2
six==1.11.0
snowballstemmer==1.2.1
stestr==2.0.0
Sphinx==1.6.5
sphinxcontrib-websupport==1.0.1
stestr==1.0.0
stevedore==1.28.0
tempest==17.1.0
testrepository==0.0.18
testscenarios==0.4
testtools==2.2.0
traceback2==1.4.0

View File

@@ -1,6 +0,0 @@
---
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.

View File

@@ -2,12 +2,13 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
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
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
keystoneauth1>=3.4.0 # Apache-2.0
six>=1.11.0 # MIT
PyYAML>=3.13 # MIT
six>=1.10.0 # MIT
PyYAML>=3.12 # MIT

View File

@@ -1,12 +1,11 @@
[metadata]
name = python-watcherclient
summary = Python client library for Watcher API
description_file =
description-file =
README.rst
author = OpenStack
author_email = openstack-discuss@lists.openstack.org
home_page = https://docs.openstack.org/python-watcherclient/latest/
python_requires = >=3.8
author-email = openstack-dev@lists.openstack.org
home-page = https://docs.openstack.org/python-watcherclient/latest/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -14,9 +13,10 @@ 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.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.5
[files]
packages =
@@ -25,6 +25,8 @@ packages =
[entry_points]
console_scripts =
watcher = watcherclient.shell:main
tempest.test_plugins =
watcherclient_tests = watcherclient.plugin:WatcherClientTempestPlugin
openstack.cli.extension =
infra_optim = watcherclient.osc.plugin
@@ -51,7 +53,6 @@ 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_update = watcherclient.v1.action_plan_shell:UpdateActionPlan
optimize_actionplan_start = watcherclient.v1.action_plan_shell:StartActionPlan
@@ -66,8 +67,6 @@ 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
@@ -105,10 +104,31 @@ 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

View File

@@ -13,8 +13,17 @@
# 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)

View File

@@ -4,10 +4,16 @@
coverage!=4.4,>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
hacking>=3.0.1,<3.1.0 # Apache-2.0
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
mock>=2.0.0 # BSD
openstackdocstheme>=1.18.1 # Apache-2.0
oslotest>=3.2.0 # Apache-2.0
python-subunit>=1.0.0 # Apache-2.0/BSD
stestr>=2.0.0 # Apache-2.0
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=2.2.0 # MIT
tempest>=17.1.0 # Apache-2.0
# Needed for pypi packaging
wheel>=0.24.0 # MIT

75
tox.ini
View File

@@ -1,24 +1,19 @@
[tox]
minversion = 3.18.0
envlist = py3,pep8
minversion = 1.8
envlist = py35,py27,pep8
skipsdist = True
[testenv]
usedevelop = True
passenv = ZUUL_CACHE_DIR
REQUIREMENTS_PIP_LOCATION
install_command = pip install {opts} {packages}
install_command =
constraints: pip install -U --force-reinstall -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/rocky} {opts} {packages}
pip install -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2023.1}
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt
allowlist_externals =
rm
OS_TEST_PATH=./watcherclient/tests/unit
deps = -r{toxinidir}/test-requirements.txt
commands = rm -f .testrepository/times.dbm
# The --test-path is defined in .stestr.conf
stestr run {posargs}
stestr slowest
python setup.py testr --slowest --testr-args='{posargs}'
[testenv:pep8]
basepython = python3
@@ -30,44 +25,38 @@ commands = {posargs}
[testenv:cover]
basepython = python3
setenv =
PYTHON=coverage run --source watcherclient --parallel-mode
commands =
stestr run {posargs}
coverage combine
coverage html -d cover
coverage xml -o cover/coverage.xml
coverage report
python setup.py testr --coverage --testr-args='{posargs}'
coverage report
[testenv:docs]
basepython = python3
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2023.1}
-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
commands = python setup.py build_sphinx
[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,W504
ignore = E123,E125
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
@@ -77,3 +66,11 @@ commands = python setup.py bdist_wheel
[hacking]
import_exceptions = watcherclient._i18n
[testenv:lower-constraints]
basepython = python3
install_command = pip install -U {opts} {packages}
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt

View File

@@ -17,7 +17,6 @@
import pbr.version
from watcherclient import client
from watcherclient.common import api_versioning
from watcherclient import exceptions
@@ -25,11 +24,3 @@ __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")

View File

@@ -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_infra_optim_api_version=None, max_retries=None,
os_watcher_api_version=None, max_retries=None,
retry_interval=None, session=None, os_endpoint_override=None,
**ignored_kwargs):
"""Get an authenticated client, based on the credentials.
@@ -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_infra_optim_api_version: watcher API version to use
:param os_watcher_api_version: watcher API version to use
:param max_retries: Maximum number of retries in case of conflict error
:param retry_interval: Amount of time (in seconds) between retries in case
of conflict error
@@ -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_infra_optim_api_version': os_infra_optim_api_version,
'os_watcher_api_version': os_watcher_api_version,
'max_retries': max_retries,
'retry_interval': retry_interval,
}
@@ -182,11 +182,4 @@ 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)

View File

@@ -26,34 +26,14 @@ 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.

View File

@@ -31,7 +31,10 @@ 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.
"""
return getattr(obj, 'id', obj)
try:
return obj.id
except AttributeError:
return obj
class Manager(object):

View File

@@ -0,0 +1,278 @@
# 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)

View File

@@ -34,15 +34,16 @@ 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
# 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)
# NOTE(deva): Record the latest version that this client was tested with.
# We still have a lot of work to do in the client to implement
# microversion support in the client properly! See
# http://specs.openstack.org/openstack/watcher-specs/specs/kilo/api-microversions.html # noqa
# for full details.
DEFAULT_VER = '1.0'
LOG = logging.getLogger(__name__)
USER_AGENT = 'python-watcherclient'
@@ -103,8 +104,20 @@ 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':
@@ -113,33 +126,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_infra_optim_api_version,
% {'req': self.os_watcher_api_version,
'min': min_ver, 'max': max_ver}))
if self.api_version_select_state == 'negotiated':
raise exceptions.UnsupportedVersion(textwrap.fill(
_("No API version was specified and the requested operation "
"was not supported by the client's negotiated API version "
"%(req)s. Supported version range is: %(min)s to %(max)s")
% {'req': self.os_infra_optim_api_version,
% {'req': self.os_watcher_api_version,
'min': min_ver, 'max': max_ver}))
negotiated_ver = str(
min(version.StrictVersion(self.os_infra_optim_api_version),
min(version.StrictVersion(self.os_watcher_api_version),
version.StrictVersion(max_ver)))
if negotiated_ver < min_ver:
negotiated_ver = min_ver
# server handles microversions, but doesn't support
# the requested version, so try a negotiated version
self.api_version_select_state = 'negotiated'
self.os_infra_optim_api_version = negotiated_ver
self.os_watcher_api_version = negotiated_ver
LOG.debug('Negotiated API version is %s', negotiated_ver)
return negotiated_ver
def _generic_parse_version_headers(self, accessor_func):
min_ver = accessor_func('OpenStack-API-Minimum-Version',
min_ver = accessor_func('X-OpenStack-Watcher-API-Minimum-Version',
None)
max_ver = accessor_func('OpenStack-API-Maximum-Version',
max_ver = accessor_func('X-OpenStack-Watcher-API-Maximum-Version',
None)
return min_ver, max_ver
@@ -193,8 +206,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_infra_optim_api_version = kwargs.get(
'os_infra_optim_api_version', DEFAULT_VER)
self.os_watcher_api_version = kwargs.get('os_watcher_api_version',
DEFAULT_VER)
self.api_version_select_state = kwargs.get(
'api_version_select_state', 'default')
self.conflict_max_retries = kwargs.pop('max_retries',
@@ -276,7 +289,7 @@ class HTTPClient(VersionNegotiationMixin):
LOG.debug('\n'.join(dump))
def _make_connection_url(self, url):
return '%s/%s' % (self.endpoint_trimmed.rstrip('/'), url.lstrip('/'))
return urlparse.urljoin(self.endpoint_trimmed, url)
def _parse_version_headers(self, resp):
return self._generic_parse_version_headers(resp.headers.get)
@@ -294,15 +307,9 @@ 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_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.os_watcher_api_version:
kwargs['headers'].setdefault('X-OpenStack-Watcher-API-Version',
self.os_watcher_api_version)
if self.auth_token:
kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
@@ -327,8 +334,8 @@ class HTTPClient(VersionNegotiationMixin):
if resp.status_code == http_client.NOT_ACCEPTABLE:
negotiated_ver = self.negotiate_version(self.session, resp)
kwargs['headers']['OpenStack-API-Version'] = (
' '.join(['infra-optim', negotiated_ver]))
kwargs['headers']['X-OpenStack-Watcher-API-Version'] = (
negotiated_ver)
return self._http_request(url, method, **kwargs)
except requests.exceptions.RequestException as e:
@@ -347,15 +354,7 @@ 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':
# 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)
body_str = ''.join([chunk for chunk in body_iter])
self.log_http_response(resp, body_str)
body_iter = six.StringIO(body_str)
else:
@@ -388,8 +387,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:
@@ -478,13 +477,13 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter):
"""HTTP client based on Keystone client session."""
def __init__(self,
os_infra_optim_api_version,
os_watcher_api_version,
api_version_select_state,
max_retries,
retry_interval,
endpoint,
**kwargs):
self.os_infra_optim_api_version = os_infra_optim_api_version
self.os_watcher_api_version = os_watcher_api_version
self.api_version_select_state = api_version_select_state
self.conflict_max_retries = max_retries
self.conflict_retry_interval = retry_interval
@@ -509,16 +508,9 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter):
_trim_endpoint_api_version(self.endpoint_override)
)
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()]))
if getattr(self, 'os_watcher_api_version', None):
kwargs['headers'].setdefault('X-OpenStack-Watcher-API-Version',
self.os_watcher_api_version)
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
endpoint_filter.setdefault('interface', self.interface)
@@ -529,8 +521,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']['OpenStack-API-Version'] = (
' '.join(['infra-optim', negotiated_ver]))
kwargs['headers']['X-OpenStack-Watcher-API-Version'] = (
negotiated_ver)
return self._http_request(url, method, **kwargs)
if resp.status_code >= http_client.BAD_REQUEST:
error_json = _extract_error_json(resp.content)
@@ -582,7 +574,7 @@ def _construct_http_client(endpoint=None,
session=None,
token=None,
auth_ref=None,
os_infra_optim_api_version=DEFAULT_VER,
os_watcher_api_version=DEFAULT_VER,
api_version_select_state='default',
max_retries=DEFAULT_MAX_RETRIES,
retry_interval=DEFAULT_RETRY_INTERVAL,
@@ -613,29 +605,27 @@ def _construct_http_client(endpoint=None,
'the session to construct a client: %s',
', '.join(dvars))
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)
return SessionClient(session=session,
os_watcher_api_version=os_watcher_api_version,
api_version_select_state=api_version_select_state,
max_retries=max_retries,
retry_interval=retry_interval,
endpoint=endpoint,
**kwargs)
else:
if kwargs:
LOG.warning('The following arguments are being ignored when '
'constructing the client: %s', ', '.join(kwargs))
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)
return HTTPClient(endpoint=endpoint,
token=token,
auth_ref=auth_ref,
os_watcher_api_version=os_watcher_api_version,
api_version_select_state=api_version_select_state,
max_retries=max_retries,
retry_interval=retry_interval,
timeout=timeout,
ca_file=ca_file,
cert_file=cert_file,
key_file=key_file,
insecure=insecure)

View File

@@ -14,6 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import print_function
import argparse
import os
import uuid

23
watcherclient/config.py Normal file
View File

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

View File

@@ -71,7 +71,6 @@ class AmbiguousAuthSystem(exceptions.ClientException):
"""Could not obtain token and endpoint using provided credentials."""
pass
# Alias for backwards compatibility
AmbigiousAuthSystem = AmbiguousAuthSystem

View File

@@ -15,14 +15,9 @@ 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 = httpclient.LATEST_VERSION
DEFAULT_API_VERSION = '1'
API_VERSION_OPTION = 'os_infra_optim_api_version'
API_NAME = 'infra-optim'
API_VERSIONS = {
@@ -32,17 +27,14 @@ 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,
version.ver_major,
instance._api_version[API_NAME],
API_VERSIONS)
LOG.debug('Instantiating infraoptim client: %s', infraoptim_client_class)
client = infraoptim_client_class(
os_infra_optim_api_version=instance._api_version[API_NAME],
os_watcher_api_version=instance._api_version[API_NAME],
session=instance.session,
region_name=instance._region_name,
)
@@ -61,30 +53,3 @@ 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

34
watcherclient/plugin.py Normal file
View File

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

View File

@@ -39,7 +39,6 @@ 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']
@@ -48,6 +47,8 @@ _IDENTITY_API_VERSION_3 = ['3']
class WatcherShell(app.App):
"""Watcher command line interface."""
log = logging.getLogger(__name__)
def __init__(self, **kwargs):
self.client = None
@@ -148,13 +149,11 @@ class WatcherShell(app.App):
metavar='<auth-token>',
default=utils.env('OS_AUTH_TOKEN'),
help='Defaults to env[OS_AUTH_TOKEN].')
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-watcher-api-version',
metavar='<os-watcher-api-version>',
default=utils.env('OS_WATCHER_API_VERSION',
default='1'),
help='Defaults to env[OS_WATCHER_API_VERSION].')
parser.add_argument('--os-endpoint-type',
default=utils.env('OS_ENDPOINT_TYPE'),
help='Defaults to env[OS_ENDPOINT_TYPE] or '
@@ -195,18 +194,17 @@ class WatcherShell(app.App):
except Exception as e:
if not logging.getLogger('').handlers:
logging.basicConfig()
LOG.error('Exception raised: %s', str(e))
self.log.error('Exception raised: %s', str(e))
return ret_val
finally:
LOG.info("END return value: %s", ret_val)
self.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:]))

View File

@@ -0,0 +1,61 @@
..
Except where otherwise noted, this document is licensed under Creative
Commons Attribution 3.0 License. You can view the license at:
https://creativecommons.org/licenses/by/3.0/
=======
Testing
=======
.. _functional_tests:
Functional tests
================
The following procedure gets you started with Tempest testing but you can also
refer to the `Tempest documentation`_ for more details.
.. _Tempest documentation: 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

View File

@@ -0,0 +1,144 @@
# 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'

View File

@@ -0,0 +1,81 @@
# 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)

View File

@@ -0,0 +1,97 @@
# 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',
'Hostname']
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
))

View File

@@ -0,0 +1,136 @@
# 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', 'Hostname']
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)

View File

@@ -0,0 +1,89 @@
# Copyright (c) 2016 Servionica
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from oslo_utils import uuidutils
from watcherclient.tests.functional.v1 import base
class AuditTemplateTests(base.TestCase):
"""Functional tests for audit template."""
dummy_name = 'dummy'
list_fields = ['UUID', 'Name', 'Goal', 'Strategy']
detailed_list_fields = list_fields + ['Created At', 'Updated At',
'Deleted At', 'Description',
'Audit Scope']
audit_template_name = 'a' + uuidutils.generate_uuid()
@classmethod
def setUpClass(cls):
cls.watcher('audittemplate create %s dummy -s dummy'
% cls.audit_template_name)
@classmethod
def tearDownClass(cls):
cls.watcher('audittemplate delete %s' % cls.audit_template_name)
def test_audit_template_list(self):
raw_output = self.watcher('audittemplate list')
self.assert_table_structure([raw_output], self.list_fields)
def test_audit_template_detailed_list(self):
raw_output = self.watcher('audittemplate list --detail')
self.assert_table_structure([raw_output], self.detailed_list_fields)
def test_audit_template_show(self):
audit_template = self.watcher(
'audittemplate show %s' % self.audit_template_name)
self.assertIn(self.audit_template_name, audit_template)
self.assert_table_structure([audit_template],
self.detailed_list_fields)
def test_audit_template_update(self):
raw_output = self.watcher('audittemplate update %s replace '
'description="Updated Desc"'
% self.audit_template_name)
audit_template_output = self.parse_show_as_object(raw_output)
assert audit_template_output['Description'] == 'Updated Desc'
class AuditTemplateActiveTests(base.TestCase):
audit_template_name = 'b' + uuidutils.generate_uuid()
list_fields = ['UUID', 'Name', 'Goal', 'Strategy']
detailed_list_fields = list_fields + ['Created At', 'Updated At',
'Deleted At', 'Description',
'Audit Scope']
def _create_audit_template(self):
self.watcher('audittemplate create %s dummy -s dummy '
'-d "Test Audit Template"' % self.audit_template_name)
def _delete_audit_template(self):
self.watcher('audittemplate delete %s' % self.audit_template_name)
def test_create_audit_template(self):
raw_output = self.watcher('audittemplate create %s dummy '
'-s dummy -d "Test Audit Template"'
% self.audit_template_name)
self.assert_table_structure([raw_output], self.detailed_list_fields)
self._delete_audit_template()
def test_delete_audit_template(self):
self._create_audit_template()
raw_output = self.watcher('audittemplate delete %s'
% self.audit_template_name)
self.assertOutput('', raw_output)

View File

@@ -0,0 +1,41 @@
# Copyright (c) 2016 Servionica
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from watcherclient.tests.functional.v1 import base
class GoalTests(base.TestCase):
"""Functional tests for goal."""
dummy_name = 'dummy'
list_fields = ['UUID', 'Name', 'Display name']
def test_goal_list(self):
raw_output = self.watcher('goal list')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.list_fields)
def test_goal_detailed_list(self):
raw_output = self.watcher('goal list --detail')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure(
[raw_output], self.list_fields + ['Efficacy specification'])
def test_goal_show(self):
raw_output = self.watcher('goal show %s' % self.dummy_name)
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure(
[raw_output], self.list_fields + ['Efficacy specification'])
self.assertNotIn('server_consolidation', raw_output)

View File

@@ -0,0 +1,40 @@
# Copyright (c) 2016 Servionica
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from watcherclient.tests.functional.v1 import base
class ScoringEngineTests(base.TestCase):
"""Functional tests for scoring engine."""
dummy_name = 'dummy_scorer'
list_fields = ['UUID', 'Name', 'Description']
detailed_list_fields = list_fields + ['Metainfo']
def test_scoringengine_list(self):
raw_output = self.watcher('scoringengine list')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.list_fields)
def test_scoringengine_detailed_list(self):
raw_output = self.watcher('scoringengine list --detail')
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.detailed_list_fields)
def test_scoringengine_show(self):
raw_output = self.watcher('scoringengine show %s' % self.dummy_name)
self.assertIn(self.dummy_name, raw_output)
self.assert_table_structure([raw_output], self.detailed_list_fields)
self.assertNotIn('dummy_avg_scorer', raw_output)

View File

@@ -0,0 +1,47 @@
# 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)

View File

@@ -0,0 +1,48 @@
# 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)

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import mock
from watcherclient.common import api_versioning
from watcherclient import exceptions

View File

@@ -10,7 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
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_infra_optim_api_version': "latest",
'os_watcher_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_infra_optim_api_version': "1.4",
'os_watcher_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_infra_optim_api_version': 'latest',
'os_watcher_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_infra_optim_api_version': 'latest',
'os_watcher_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_infra_optim_api_version': None,
'os_watcher_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_infra_optim_api_version': None,
'os_watcher_api_version': None,
'max_retries': None,
'retry_interval': None,
'session': session,
@@ -350,11 +350,3 @@ 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)

View File

@@ -14,7 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import mock
from watcherclient.common.apiclient import exceptions as exc
from watcherclient.common import utils

View File

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

View File

@@ -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, os_infra_optim_api_version='1.0'):
def setUp(self):
super(CommandTestCase, self).setUp()
self.fake_env = {
@@ -38,7 +38,7 @@ class CommandTestCase(utils.BaseTestCase):
'os_username': 'test',
'os_password': 'test',
'timeout': 600,
'os_infra_optim_api_version': os_infra_optim_api_version}
'os_watcher_api_version': '1'}
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 and formatting != 'table':
if formatting:
formatter_arg = " -f %s" % formatting
formatter = jsonutils.loads
else:

View File

@@ -14,13 +14,10 @@
# 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
@@ -41,19 +38,9 @@ ACTION_PLAN2 = {
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':
{
@@ -142,31 +129,6 @@ 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):
@@ -253,54 +215,6 @@ 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,

View File

@@ -14,9 +14,7 @@
# limitations under the License.
import datetime
from unittest import mock
from oslo_utils.uuidutils import generate_uuid
import mock
import six
from watcherclient import exceptions
@@ -128,17 +126,6 @@ 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):
@@ -208,32 +195,6 @@ 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 = ''

View File

@@ -14,8 +14,7 @@
# under the License.
import datetime
from unittest import mock
import mock
import six
from watcherclient import exceptions

View File

@@ -14,13 +14,13 @@
# under the License.
import datetime
from unittest import mock
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,86 +52,76 @@ 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',
'hostname': '',
}
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': '',
}
class AuditShellTest(base.CommandTestCase):
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': '',
}
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_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)
def setUp(self):
super(self.__class__, self).setUp()
# goal mock
p_goal_manager = mock.patch.object(resource, 'GoalManager')
@@ -168,8 +158,8 @@ class AuditShellTest(base.CommandTestCase):
self.cmd = shell.WatcherShell(stdout=self.stdout)
def test_do_audit_list(self):
audit1 = resource.Audit(mock.Mock(), self.AUDIT_1)
audit2 = resource.Audit(mock.Mock(), self.AUDIT_2)
audit1 = resource.Audit(mock.Mock(), AUDIT_1)
audit2 = resource.Audit(mock.Mock(), AUDIT_2)
self.m_audit_mgr.list.return_value = [
audit1, audit2]
@@ -186,7 +176,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(), self.AUDIT_2)
audit2 = resource.Audit(mock.Mock(), AUDIT_2)
self.m_audit_mgr.list.return_value = [audit2]
exit_code, results = self.run_cmd(
@@ -203,8 +193,8 @@ class AuditShellTest(base.CommandTestCase):
marker='5869da81-4876-4687-a1ed-12cd64cf53d9')
def test_do_audit_list_detail(self):
audit1 = resource.Audit(mock.Mock(), self.AUDIT_1)
audit2 = resource.Audit(mock.Mock(), self.AUDIT_2)
audit1 = resource.Audit(mock.Mock(), AUDIT_1)
audit2 = resource.Audit(mock.Mock(), AUDIT_2)
self.m_audit_mgr.list.return_value = [
audit1, audit2]
@@ -221,7 +211,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(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.get.return_value = audit
exit_code, result = self.run_cmd(
@@ -235,7 +225,7 @@ class AuditShellTest(base.CommandTestCase):
'5869da81-4876-4687-a1ed-12cd64cf53d9')
def test_do_audit_show_by_name(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.get.return_value = audit
exit_code, result = self.run_cmd(
@@ -288,7 +278,7 @@ class AuditShellTest(base.CommandTestCase):
'5b157edd-5a7e-4aaa-b511-f7b33ec86e9f')
def test_do_audit_update(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.update.return_value = audit
exit_code, result = self.run_cmd(
@@ -304,7 +294,7 @@ class AuditShellTest(base.CommandTestCase):
[{'op': 'replace', 'path': '/state', 'value': 'PENDING'}])
def test_do_audit_update_by_name(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.update.return_value = audit
exit_code, result = self.run_cmd(
@@ -319,7 +309,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(), self.AUDIT_3)
audit = resource.Audit(mock.Mock(), 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
@@ -338,7 +328,7 @@ class AuditShellTest(base.CommandTestCase):
)
def test_do_audit_create_with_audit_template_name(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_3)
audit = resource.Audit(mock.Mock(), 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
@@ -356,7 +346,7 @@ class AuditShellTest(base.CommandTestCase):
)
def test_do_audit_create_with_goal(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd(
@@ -373,7 +363,7 @@ class AuditShellTest(base.CommandTestCase):
)
def test_do_audit_create_with_goal_and_strategy(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd(
@@ -392,7 +382,7 @@ class AuditShellTest(base.CommandTestCase):
)
def test_do_audit_create_with_type(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd(
@@ -409,7 +399,7 @@ class AuditShellTest(base.CommandTestCase):
)
def test_do_audit_create_with_parameter(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd(
@@ -427,25 +417,8 @@ 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(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd(
@@ -464,7 +437,7 @@ class AuditShellTest(base.CommandTestCase):
)
def test_do_audit_create_with_name(self):
audit = resource.Audit(mock.Mock(), self.AUDIT_1)
audit = resource.Audit(mock.Mock(), AUDIT_1)
self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd(
@@ -482,219 +455,3 @@ 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
)

View File

@@ -14,8 +14,7 @@
# under the License.
import datetime
from unittest import mock
import mock
import six
from watcherclient import shell

View File

@@ -1,75 +0,0 @@
# 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))

View File

@@ -1,131 +0,0 @@
# 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)

View File

@@ -98,16 +98,6 @@ fake_responses_sorting = {
},
}
fake_responses_marker = {
'/v1/goals/?marker=fc087747-61be-4aad-8126-b701731ae836':
{
'GET': (
{},
{"goals": [GOAL2]}
),
},
}
class GoalManagerTest(testtools.TestCase):
@@ -142,17 +132,6 @@ 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)

View File

@@ -14,8 +14,7 @@
# limitations under the License.
import datetime
from unittest import mock
import mock
import six
from watcherclient import shell
@@ -94,23 +93,6 @@ 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)

View File

@@ -14,8 +14,7 @@
# limitations under the License.
import datetime
from unittest import mock
import mock
import six
from watcherclient import shell

View File

@@ -14,8 +14,7 @@
# limitations under the License.
import datetime
from unittest import mock
import mock
import six
from watcherclient import shell

View File

@@ -107,16 +107,6 @@ fake_responses_sorting = {
},
}
fake_responses_marker = {
'/v1/strategies/?marker=2cf86250-d309-4b81-818e-1537f3dba6e5':
{
'GET': (
{},
{"strategies": [STRATEGY2]}
),
},
}
class StrategyManagerTest(testtools.TestCase):
@@ -141,19 +131,6 @@ 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)

View File

@@ -14,10 +14,10 @@
# limitations under the License.
import datetime
from unittest import mock
import mock
import six
from oslo_serialization import jsonutils
import six
from watcherclient import shell
from watcherclient.tests.unit.v1 import base
@@ -90,23 +90,6 @@ 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)

View File

@@ -17,7 +17,6 @@ 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
@@ -39,12 +38,9 @@ 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",
"DataModel", "DataModelManager")
"Service", "ServiceManager", "Strategy", "StrategyManager")

View File

@@ -20,7 +20,7 @@ from watcherclient import exceptions as exc
CREATION_ATTRIBUTES = ['audit_template_uuid', 'audit_type', 'interval',
'parameters', 'goal', 'strategy', 'auto_trigger',
'name', 'start_time', 'end_time', 'force']
'name']
class Audit(base.Resource):

View File

@@ -13,34 +13,16 @@
# 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."""
@@ -65,8 +47,6 @@ 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)
@@ -138,10 +118,6 @@ 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))
@@ -167,8 +143,8 @@ class CreateAudit(command.ShowOne):
dest='audit_type',
metavar='<audit_type>',
default='ONESHOT',
choices=['ONESHOT', 'CONTINUOUS', 'EVENT'],
help=_("Audit type. It must be ONESHOT, CONTINUOUS or EVENT. "
choices=['ONESHOT', 'CONTINUOUS'],
help=_("Audit type. It must be ONESHOT or CONTINUOUS. "
"Default is ONESHOT."))
parser.add_argument(
'-p', '--parameter',
@@ -182,7 +158,7 @@ class CreateAudit(command.ShowOne):
dest='interval',
metavar='<interval>',
help=_('Audit interval (in seconds or cron format). '
'Cron interval can be used like: ``*/5 * * * *``. '
'Cron inteval can be used like: "*/5 * * * *". '
'Only used if the audit is CONTINUOUS.'))
parser.add_argument(
'-g', '--goal',
@@ -211,24 +187,6 @@ 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
@@ -236,27 +194,25 @@ 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']
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')
'interval', 'goal', 'strategy', 'auto_trigger', 'name']
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:
@@ -264,8 +220,6 @@ 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)
@@ -307,9 +261,6 @@ 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)

View File

@@ -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,49 +201,48 @@ class CreateAuditTemplate(command.ShowOne):
" - uuid: UUID1\n"
" - uuid: UUID2\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"
"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"
" ]\n"
)
)

View File

@@ -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_infra_optim_api_version'):
if kwargs.get('os_watcher_api_version'):
kwargs['api_version_select_state'] = "user"
else:
if not endpoint:
raise exceptions.EndpointException(
_("Must provide 'endpoint' if os_infra_optim_api_version "
_("Must provide 'endpoint' if os_watcher_api_version "
"isn't specified"))
# If the user didn't specify a version, use a cached version if
# one has been stored
host, netport = httpclient.get_server(endpoint)
kwargs['api_version_select_state'] = "default"
kwargs['os_infra_optim_api_version'] = httpclient.DEFAULT_VER
kwargs['os_watcher_api_version'] = httpclient.DEFAULT_VER
self.http_client = httpclient._construct_http_client(
endpoint, *args, **kwargs)
@@ -56,4 +56,3 @@ 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)

View File

@@ -1,56 +0,0 @@
# 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]

View File

@@ -1,77 +0,0 @@
# 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))

View File

@@ -29,8 +29,7 @@ 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,
marker=None):
def list(self, limit=None, sort_key=None, sort_dir=None, detail=False):
"""Retrieve a list of goal.
:param limit: The maximum number of results to return per
@@ -50,15 +49,13 @@ class GoalManager(base.Manager):
:param detail: Optional, boolean whether to return detailed information
about audits.
:param marker: Optional, UUID of the last goal in the previous page.
:returns: A list of goals.
:returns: A list of audits.
"""
if limit is not None:
limit = int(limit)
filters = utils.common_filters(limit, sort_key, sort_dir, marker)
filters = utils.common_filters(limit, sort_key, sort_dir)
path = ''
if detail:
path += 'detail'

View File

@@ -96,13 +96,6 @@ 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

View File

@@ -33,13 +33,12 @@ AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Goal', 'Strategy']
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',
'hostname', 'start_time', 'end_time', 'force']
'hostname']
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', 'Hostname', 'Start Time', 'End Time',
'Force']
'Next Run Time', 'Hostname']
AUDIT_SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type',
'state', 'goal_name', 'strategy_name',
@@ -98,28 +97,6 @@ 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']

View File

@@ -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, marker=None):
sort_dir=None, detail=False):
"""Retrieve a list of strategy.
:param goal: The UUID of the goal to filter by
@@ -58,15 +58,14 @@ 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, marker)
filters = utils.common_filters(limit, sort_key, sort_dir)
if goal:
filters.append(parse.urlencode(dict(goal=goal)))

View File

@@ -123,13 +123,7 @@ 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):