Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-senlinclient for
openSUSE:Factory checked in at 2022-05-25 20:34:42
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-senlinclient (Old)
and /work/SRC/openSUSE:Factory/.python-senlinclient.new.2254 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-senlinclient"
Wed May 25 20:34:42 2022 rev:13 rq:979086 version:2.4.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-senlinclient/python-senlinclient.changes
2021-05-10 15:39:58.489418198 +0200
+++
/work/SRC/openSUSE:Factory/.python-senlinclient.new.2254/python-senlinclient.changes
2022-05-25 20:35:10.544279009 +0200
@@ -1,0 +2,20 @@
+Tue May 24 21:37:24 UTC 2022 - [email protected]
+
+- update to version 2.4.0
+ - Use py3 as the default runtime for tox
+ - Update master for stable/wallaby
+ - Add Python3 xena unit tests
+ - Add Python3 yoga unit tests
+ - Update master for stable/xena
+
+-------------------------------------------------------------------
+Tue Oct 26 22:00:44 UTC 2021 - [email protected]
+
+- update to version 2.3.0
+ - Adds --wait argument for cluster CLI interactions
+ - Fix lower-constraints
+ - Fix config and metadata in cluster update
+ - trivial: Drop references to os-testr
+ - Update TOX_CONSTRAINTS_FILE
+
+-------------------------------------------------------------------
Old:
----
python-senlinclient-2.2.1.tar.gz
New:
----
python-senlinclient-2.4.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-senlinclient.spec ++++++
--- /var/tmp/diff_new_pack.qRvaex/_old 2022-05-25 20:35:11.520280371 +0200
+++ /var/tmp/diff_new_pack.qRvaex/_new 2022-05-25 20:35:11.524280377 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-senlinclient
#
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -17,21 +17,19 @@
Name: python-senlinclient
-Version: 2.2.1
+Version: 2.4.0
Release: 0
Summary: Python API and CLI for OpenStack Senlin
License: Apache-2.0
Group: Development/Languages/Python
URL: https://docs.openstack.org/python-senlinclient
-Source0:
https://files.pythonhosted.org/packages/source/p/python-senlinclient/python-senlinclient-2.2.1.tar.gz
+Source0:
https://files.pythonhosted.org/packages/source/p/python-senlinclient/python-senlinclient-2.4.0.tar.gz
BuildRequires: openstack-macros
-BuildRequires: python3-Babel
BuildRequires: python3-PrettyTable >= 0.7.2
-BuildRequires: python3-PyYAML >= 3.13
+BuildRequires: python3-PyYAML >= 5.3.1
BuildRequires: python3-devel
BuildRequires: python3-fixtures
BuildRequires: python3-heatclient >= 1.10.0
-BuildRequires: python3-mock
BuildRequires: python3-openstackclient
BuildRequires: python3-openstacksdk >= 0.24.0
BuildRequires: python3-osc-lib >= 1.11.0
@@ -53,11 +51,10 @@
%package -n python3-senlinclient
Summary: Python API and CLI for OpenStack Senlin
-Requires: python3-Babel
Requires: python3-PrettyTable >= 0.7.2
-Requires: python3-PyYAML >= 3.13
+Requires: python3-PyYAML >= 5.3.1
Requires: python3-heatclient >= 1.10.0
-Requires: python3-keystoneauth1 >= 3.4.0
+Requires: python3-keystoneauth1 >= 3.11.0
Requires: python3-openstackclient
Requires: python3-openstacksdk >= 0.24.0
Requires: python3-osc-lib >= 1.11.0
@@ -66,7 +63,6 @@
Requires: python3-oslo.utils >= 3.33.0
Requires: python3-pbr >= 2.0.0
Requires: python3-requests >= 2.14.2
-Requires: python3-six
%description -n python3-senlinclient
OpenStack Clustering service Provisioning API Client Library
@@ -89,7 +85,7 @@
auto-generated documentation.
%prep
-%autosetup -p1 -n python-senlinclient-2.2.1
+%autosetup -p1 -n python-senlinclient-2.4.0
%py_req_cleanup
%build
++++++ _service ++++++
--- /var/tmp/diff_new_pack.qRvaex/_old 2022-05-25 20:35:11.548280410 +0200
+++ /var/tmp/diff_new_pack.qRvaex/_new 2022-05-25 20:35:11.552280416 +0200
@@ -1,8 +1,8 @@
<services>
<service mode="disabled" name="renderspec">
- <param
name="input-template">https://opendev.org/openstack/rpm-packaging/raw/branch/stable/wallaby/openstack/python-senlinclient/python-senlinclient.spec.j2</param>
+ <param
name="input-template">https://opendev.org/openstack/rpm-packaging/raw/master/openstack/python-senlinclient/python-senlinclient.spec.j2</param>
<param name="output-name">python-senlinclient.spec</param>
- <param
name="requirements">https://opendev.org/openstack/python-senlinclient/raw/branch/stable/wallaby/requirements.txt</param>
+ <param
name="requirements">https://opendev.org/openstack/python-senlinclient/raw/branch/master/requirements.txt</param>
<param name="changelog-email">[email protected]</param>
<param name="changelog-provider">gh,openstack,python-senlinclient</param>
</service>
++++++ python-senlinclient-2.2.1.tar.gz -> python-senlinclient-2.4.0.tar.gz
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/.zuul.yaml
new/python-senlinclient-2.4.0/.zuul.yaml
--- old/python-senlinclient-2.2.1/.zuul.yaml 2021-03-11 12:03:35.000000000
+0100
+++ new/python-senlinclient-2.4.0/.zuul.yaml 2022-02-25 17:20:21.000000000
+0100
@@ -32,7 +32,7 @@
templates:
- check-requirements
- openstack-lower-constraints-jobs
- - openstack-python3-wallaby-jobs
+ - openstack-python3-yoga-jobs
- openstackclient-plugin-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/AUTHORS
new/python-senlinclient-2.4.0/AUTHORS
--- old/python-senlinclient-2.2.1/AUTHORS 2021-03-11 12:04:24.000000000
+0100
+++ new/python-senlinclient-2.4.0/AUTHORS 2022-02-25 17:21:06.000000000
+0100
@@ -44,6 +44,7 @@
Saju <[email protected]>
Sean McGinnis <[email protected]>
Sharat Sharma <[email protected]>
+Stephen Finucane <[email protected]>
Tang Chen <[email protected]>
Thomas Bechtold <[email protected]>
Thomas Herve <[email protected]>
@@ -67,6 +68,7 @@
dixiaoli <[email protected]>
gecong1973 <[email protected]>
gengchc2 <[email protected]>
+gugug <[email protected]>
howardlee <[email protected]>
huangtianhua <[email protected]>
jacky06 <[email protected]>
@@ -95,6 +97,7 @@
xiaozhuangqing <[email protected]>
xu-haiwei <[email protected]>
yanyanhu <[email protected]>
+zhangboye <[email protected]>
zhangguoqing <[email protected]>
zhangyanxian <[email protected]>
zhurong <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/ChangeLog
new/python-senlinclient-2.4.0/ChangeLog
--- old/python-senlinclient-2.2.1/ChangeLog 2021-03-11 12:04:24.000000000
+0100
+++ new/python-senlinclient-2.4.0/ChangeLog 2022-02-25 17:21:06.000000000
+0100
@@ -1,10 +1,27 @@
CHANGES
=======
+2.4.0
+-----
+
+* Add Python3 yoga unit tests
+* Update master for stable/xena
+
+2.3.0
+-----
+
+* Fix lower-constraints
+* Use py3 as the default runtime for tox
+* Add Python3 xena unit tests
+* Update master for stable/wallaby
+
2.2.1
-----
* Remove unicode from python client
+* Update TOX\_CONSTRAINTS\_FILE
+* Fix config and metadata in cluster update
+* Adds --wait argument for cluster CLI interactions
2.2.0
-----
@@ -13,6 +30,7 @@
* Remove install unnecessary packages
* Add Python3 wallaby unit tests
* Update master for stable/victoria
+* trivial: Drop references to os-testr
2.1.1
-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/PKG-INFO
new/python-senlinclient-2.4.0/PKG-INFO
--- old/python-senlinclient-2.2.1/PKG-INFO 2021-03-11 12:04:24.435627500
+0100
+++ new/python-senlinclient-2.4.0/PKG-INFO 2022-02-25 17:21:06.534387400
+0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: python-senlinclient
-Version: 2.2.1
+Version: 2.4.0
Summary: OpenStack Clustering API Client Library
Home-page: https://docs.openstack.org/python-senlinclient/latest/
Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/doc/source/contributor/index.rst
new/python-senlinclient-2.4.0/doc/source/contributor/index.rst
--- old/python-senlinclient-2.2.1/doc/source/contributor/index.rst
2021-03-11 12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/doc/source/contributor/index.rst
2022-02-25 17:20:21.000000000 +0100
@@ -28,7 +28,7 @@
-----------------
There are a number of ways to run unit tests currently, and there's a
combination of frameworks used depending on what commands you use. The
-preferred method is to use tox, which calls ostestr via the tox.ini file.
+preferred method is to use tox, which calls stestr via the tox.ini file.
To run all tests simply run::
tox
@@ -52,4 +52,4 @@
tox -epy27 senlinclient.tests.unit.v1.test_node
For more information on these options and how to run tests, please see the
-`ostestr documentation <https://docs.openstack.org/os-testr/latest/>`_.
+`stestr documentation <https://stestr.readthedocs.io/en/latest/index.html>`_.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/lower-constraints.txt
new/python-senlinclient-2.4.0/lower-constraints.txt
--- old/python-senlinclient-2.2.1/lower-constraints.txt 2021-03-11
12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/lower-constraints.txt 2022-02-25
17:20:21.000000000 +0100
@@ -15,9 +15,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
iso8601==0.1.11
@@ -26,10 +24,9 @@
jsonpatch==1.16
jsonpointer==1.13
jsonschema==2.6.0
-keystoneauth1==3.4.0
+keystoneauth1==3.11.0
linecache2==1.0.0
MarkupSafe==1.0
-mccabe==0.2.1
monotonic==0.6
mox3==0.20.0
msgpack-python==0.4.0
@@ -39,7 +36,6 @@
openstacksdk==0.24.0
os-client-config==1.28.0
os-service-types==1.2.0
-os-testr==1.0.0
osc-lib==1.11.0
oslo.concurrency==3.25.0
oslo.config==5.2.0
@@ -51,13 +47,10 @@
oslotest==3.2.0
paramiko==2.0.0
pbr==2.0.0
-pep8==1.5.7
positional==1.2.1
prettytable==0.7.2
pyasn1==0.1.8
pycparser==2.18
-pyflakes==0.8.1
-Pygments==2.2.0
pyinotify==0.9.6
pyOpenSSL==17.1.0
pyparsing==2.1.0
@@ -72,8 +65,8 @@
python-openstackclient==3.12.0
python-subunit==1.0.0
python-swiftclient==3.2.0
-pytz==2013.6
-PyYAML==3.13
+pytz==2015.7
+PyYAML==5.3.1
requests==2.14.2
requests-mock==1.2.0
requestsexceptions==1.2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/python_senlinclient.egg-info/PKG-INFO
new/python-senlinclient-2.4.0/python_senlinclient.egg-info/PKG-INFO
--- old/python-senlinclient-2.2.1/python_senlinclient.egg-info/PKG-INFO
2021-03-11 12:04:24.000000000 +0100
+++ new/python-senlinclient-2.4.0/python_senlinclient.egg-info/PKG-INFO
2022-02-25 17:21:06.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: python-senlinclient
-Version: 2.2.1
+Version: 2.4.0
Summary: OpenStack Clustering API Client Library
Home-page: https://docs.openstack.org/python-senlinclient/latest/
Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/python_senlinclient.egg-info/SOURCES.txt
new/python-senlinclient-2.4.0/python_senlinclient.egg-info/SOURCES.txt
--- old/python-senlinclient-2.2.1/python_senlinclient.egg-info/SOURCES.txt
2021-03-11 12:04:24.000000000 +0100
+++ new/python-senlinclient-2.4.0/python_senlinclient.egg-info/SOURCES.txt
2022-02-25 17:21:06.000000000 +0100
@@ -64,6 +64,8 @@
releasenotes/source/unreleased.rst
releasenotes/source/ussuri.rst
releasenotes/source/victoria.rst
+releasenotes/source/wallaby.rst
+releasenotes/source/xena.rst
releasenotes/source/_static/.placeholder
releasenotes/source/_templates/.placeholder
releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/python_senlinclient.egg-info/pbr.json
new/python-senlinclient-2.4.0/python_senlinclient.egg-info/pbr.json
--- old/python-senlinclient-2.2.1/python_senlinclient.egg-info/pbr.json
2021-03-11 12:04:24.000000000 +0100
+++ new/python-senlinclient-2.4.0/python_senlinclient.egg-info/pbr.json
2022-02-25 17:21:06.000000000 +0100
@@ -1 +1 @@
-{"git_version": "e6edaee", "is_release": true}
\ No newline at end of file
+{"git_version": "9fc2edc", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/python_senlinclient.egg-info/requires.txt
new/python-senlinclient-2.4.0/python_senlinclient.egg-info/requires.txt
--- old/python-senlinclient-2.2.1/python_senlinclient.egg-info/requires.txt
2021-03-11 12:04:24.000000000 +0100
+++ new/python-senlinclient-2.4.0/python_senlinclient.egg-info/requires.txt
2022-02-25 17:21:06.000000000 +0100
@@ -1,6 +1,6 @@
-PrettyTable<0.8,>=0.7.2
-PyYAML>=3.13
-keystoneauth1>=3.4.0
+PrettyTable>=0.7.2
+PyYAML>=5.3.1
+keystoneauth1>=3.11.0
openstacksdk>=0.24.0
osc-lib>=1.11.0
oslo.i18n>=3.15.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/releasenotes/source/index.rst
new/python-senlinclient-2.4.0/releasenotes/source/index.rst
--- old/python-senlinclient-2.2.1/releasenotes/source/index.rst 2021-03-11
12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/releasenotes/source/index.rst 2022-02-25
17:20:21.000000000 +0100
@@ -6,6 +6,8 @@
:maxdepth: 1
unreleased
+ xena
+ wallaby
victoria
ussuri
train
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/releasenotes/source/wallaby.rst
new/python-senlinclient-2.4.0/releasenotes/source/wallaby.rst
--- old/python-senlinclient-2.2.1/releasenotes/source/wallaby.rst
1970-01-01 01:00:00.000000000 +0100
+++ new/python-senlinclient-2.4.0/releasenotes/source/wallaby.rst
2022-02-25 17:20:21.000000000 +0100
@@ -0,0 +1,6 @@
+============================
+Wallaby Series Release Notes
+============================
+
+.. release-notes::
+ :branch: stable/wallaby
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/releasenotes/source/xena.rst
new/python-senlinclient-2.4.0/releasenotes/source/xena.rst
--- old/python-senlinclient-2.2.1/releasenotes/source/xena.rst 1970-01-01
01:00:00.000000000 +0100
+++ new/python-senlinclient-2.4.0/releasenotes/source/xena.rst 2022-02-25
17:20:21.000000000 +0100
@@ -0,0 +1,6 @@
+=========================
+Xena Series Release Notes
+=========================
+
+.. release-notes::
+ :branch: stable/xena
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/requirements.txt
new/python-senlinclient-2.4.0/requirements.txt
--- old/python-senlinclient-2.2.1/requirements.txt 2021-03-11
12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/requirements.txt 2022-02-25
17:20:21.000000000 +0100
@@ -3,13 +3,13 @@
# process, which may cause wedges in the gate later.
pbr!=2.1.0,>=2.0.0 # Apache-2.0
-PrettyTable<0.8,>=0.7.2 # BSD
-keystoneauth1>=3.4.0 # Apache-2.0
+PrettyTable>=0.7.2 # BSD
+keystoneauth1>=3.11.0 # Apache-2.0
openstacksdk>=0.24.0 # Apache-2.0
osc-lib>=1.11.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0
python-heatclient>=1.10.0 # Apache-2.0
-PyYAML>=3.13 # MIT
+PyYAML>=5.3.1 # MIT
requests>=2.14.2 # Apache-2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/senlinclient/common/exc.py
new/python-senlinclient-2.4.0/senlinclient/common/exc.py
--- old/python-senlinclient-2.2.1/senlinclient/common/exc.py 2021-03-11
12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/senlinclient/common/exc.py 2022-02-25
17:20:21.000000000 +0100
@@ -38,6 +38,10 @@
"""Illegal file format detected."""
+class PollingExceededError(BaseException):
+ """Desired resource state not achived within polling period."""
+
+
class HTTPException(BaseException):
"""Base exception for all HTTP-derived exceptions."""
code = 'N/A'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/senlinclient/common/utils.py
new/python-senlinclient-2.4.0/senlinclient/common/utils.py
--- old/python-senlinclient-2.2.1/senlinclient/common/utils.py 2021-03-11
12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/senlinclient/common/utils.py 2022-02-25
17:20:21.000000000 +0100
@@ -12,15 +12,21 @@
from heatclient.common import template_utils
+import logging
+from openstack import exceptions as sdk_exc
from oslo_serialization import jsonutils
from oslo_utils import importutils
import prettytable
+import time
import yaml
from senlinclient.common import exc
from senlinclient.common.i18n import _
+log = logging.getLogger(__name__)
+
+
def import_versioned_module(version, submodule=None):
module = 'senlinclient.v%s' % version
if submodule:
@@ -153,3 +159,78 @@
}
return new_spec
+
+
+def await_action(senlin_client, action_id,
+ poll_count_max=10, poll_interval=5):
+
+ def check_action():
+ try:
+ action = senlin_client.get_action(action_id)
+ except sdk_exc.ResourceNotFound:
+ raise exc.CommandError(_('Action not found: %s')
+ % action_id)
+ action_states = ['succeeded', 'failed', 'cancelled']
+ if action.status.lower() in action_states:
+ log.info("Action %s completed with status %s."
+ % (action.id, action.status))
+ return True
+ log.info("Awaiting action %s completion status (current: %s)."
+ % (action.id, action.status))
+ return False
+
+ _check(check_action, poll_count_max, poll_interval)
+
+
+def await_cluster_status(senlin_client, cluster_id, statuses=None,
+ poll_count_max=10, poll_interval=5):
+
+ if not statuses or len(statuses) <= 0:
+ statuses = ['ACTIVE', 'ERROR', 'WARNING']
+
+ def check_status():
+ try:
+ cluster = senlin_client.get_cluster(cluster_id)
+ except sdk_exc.ResourceNotFound:
+ raise exc.CommandError(_('Cluster not found: %s') % cluster_id)
+
+ if cluster.status.lower() in [fs.lower() for fs in statuses]:
+ return True
+ log.info("Awaiting cluster status (desired: %s - current: %s)." %
+ (', '.join(statuses), cluster.status))
+ return False
+
+ _check(check_status, poll_count_max, poll_interval)
+
+
+def await_cluster_delete(senlin_client, cluster_id,
+ poll_count_max=10, poll_interval=5):
+
+ def check_deleted():
+ try:
+ senlin_client.get_cluster(cluster_id)
+ except sdk_exc.ResourceNotFound:
+ log.info("Successfully deleted cluster %s." % cluster_id)
+ return True
+ log.info("Awaiting cluster deletion for %s." % cluster_id)
+ return False
+
+ _check(check_deleted, poll_count_max, poll_interval)
+
+
+def _check(check_func, poll_count_max=10, poll_interval=5):
+ # a negative poll_count_max is considered indefinite
+
+ poll_increment = 1
+ if poll_count_max < 0:
+ poll_count_max = 1
+ poll_increment = 0
+
+ poll_count = 0
+ while poll_count < poll_count_max:
+ if check_func():
+ return
+
+ time.sleep(poll_interval)
+ poll_count += poll_increment
+ raise exc.PollingExceededError()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/senlinclient/tests/unit/test_utils.py
new/python-senlinclient-2.4.0/senlinclient/tests/unit/test_utils.py
--- old/python-senlinclient-2.2.1/senlinclient/tests/unit/test_utils.py
2021-03-11 12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/senlinclient/tests/unit/test_utils.py
2022-02-25 17:20:21.000000000 +0100
@@ -14,6 +14,7 @@
from unittest import mock
import testtools
+import time
from senlinclient.common import exc
from senlinclient.common.i18n import _
@@ -97,3 +98,40 @@
def test_list_formatter_with_empty_list(self):
params = []
self.assertEqual('', utils.list_formatter(params))
+
+ @mock.patch.object(utils, '_check')
+ def test_await_cluster_action(self, mock_check):
+ utils.await_action('fake-client', 'test-action-id')
+ mock_check.assert_called_once()
+
+ @mock.patch.object(utils, '_check')
+ def test_await_cluster_status(self, mock_check):
+ utils.await_cluster_status('fake-client', 'ACTIVE')
+ mock_check.assert_called_once()
+
+ @mock.patch.object(utils, '_check')
+ def test_await_cluster_delete(self, mock_check):
+ utils.await_cluster_delete('fake-client', 'test-cluster-id')
+ mock_check.assert_called_once()
+
+ def test_check(self):
+ check_func = mock.Mock(return_value=True)
+
+ try:
+ utils._check(check_func)
+ except Exception:
+ self.fail("_check() unexpectedly raised an exception")
+
+ check_func.assert_called()
+
+ @mock.patch.object(time, 'sleep')
+ def test_check_raises(self, mock_sleep):
+ mock_check_func = mock.Mock(return_value=False)
+
+ poll_count = 2
+ poll_interval = 1
+
+ self.assertRaises(exc.PollingExceededError, utils._check,
+ mock_check_func, poll_count, poll_interval)
+ mock_check_func.assert_called()
+ mock_sleep.assert_called()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-senlinclient-2.2.1/senlinclient/tests/unit/v1/test_cluster.py
new/python-senlinclient-2.4.0/senlinclient/tests/unit/v1/test_cluster.py
--- old/python-senlinclient-2.2.1/senlinclient/tests/unit/v1/test_cluster.py
2021-03-11 12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/senlinclient/tests/unit/v1/test_cluster.py
2022-02-25 17:20:21.000000000 +0100
@@ -18,6 +18,7 @@
from openstack import exceptions as sdk_exc
from osc_lib import exceptions as exc
+from senlinclient.common import utils as senlin_utils
from senlinclient.tests.unit.v1 import fakes
from senlinclient.v1 import cluster as osc_cluster
@@ -202,13 +203,14 @@
def setUp(self):
super(TestClusterCreate, self).setUp()
self.cmd = osc_cluster.CreateCluster(self.app, None)
+ self.cluster_id = '7d85f602-a948-4a30-afd4-e84f47471c15'
fake_cluster = mock.Mock(
config={},
created_at="2015-02-11T15:13:20",
data={},
desired_capacity=0,
domain_id=None,
- id="7d85f602-a948-4a30-afd4-e84f47471c15",
+ id=self.cluster_id,
init_time="2015-02-10T14:26:11",
max_size=-1,
metadata={},
@@ -265,6 +267,24 @@
self.cmd.take_action(parsed_args)
self.mock_client.create_cluster.assert_called_with(**kwargs)
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_create_with_wait(self, mock_await):
+ arglist = ['test_cluster', '--profile', 'mystack',
+ '--min-size', '1', '--max-size', '10',
+ '--desired-capacity', '2', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await.assert_called_once_with(self.mock_client, self.cluster_id)
+
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_create_without_wait(self, mock_await):
+ arglist = ['test_cluster', '--profile', 'mystack',
+ '--min-size', '1', '--max-size', '10',
+ '--desired-capacity', '2']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await.assert_not_called()
+
class TestClusterUpdate(TestCluster):
@@ -334,6 +354,24 @@
parsed_args)
self.assertIn('Cluster not found: c6b8b252', str(error))
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_update_with_wait(self, mock_await):
+ arglist = ['--name', 'new_cluster', '--metadata', 'nk1=nv1;nk2=nv2',
+ '--profile', 'new_profile', '--timeout', '30', '45edadcb',
+ '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await.assert_called_once_with(self.mock_client,
+ self.fake_cluster.id)
+
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_update_without_wait(self, mock_await):
+ arglist = ['--name', 'new_cluster', '--metadata', 'nk1=nv1;nk2=nv2',
+ '--profile', 'new_profile', '--timeout', '30', '45edadcb']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await.assert_not_called()
+
class TestClusterDelete(TestCluster):
def setUp(self):
@@ -422,6 +460,45 @@
mock_stdin.readline.assert_called_with()
self.mock_client.delete_cluster.assert_not_called()
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_delete')
+ def test_cluster_delete_with_wait(self, mock_await_cluster,
+ mock_await_action):
+ fake_action = {'id': 'fake-action-id'}
+ self.mock_client.delete_cluster = mock.Mock(return_value=fake_action)
+ arglist = ['my_cluster', '--force', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_called_once_with(self.mock_client,
+ fake_action['id'])
+ mock_await_cluster.assert_called_once_with(self.mock_client,
+ 'my_cluster')
+
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_delete')
+ def test_cluster_delete_without_wait(self, mock_await_cluster,
+ mock_await_action):
+ fake_action = {'id': 'fake-action-id'}
+ self.mock_client.delete_cluster = mock.Mock(return_value=fake_action)
+ arglist = ['my_cluster', '--force']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_cluster.assert_not_called()
+
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_delete')
+ def test_cluster_delete_with_wait_bad_action(self, mock_await_cluster,
+ mock_await_action):
+ self.mock_client.delete_cluster.side_effect = (
+ Exception('test exception')
+ )
+ arglist = ['my_cluster', '--force', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_cluster.assert_not_called()
+
class TestClusterResize(TestCluster):
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
@@ -565,6 +642,46 @@
self.assertEqual('Max size cannot be less than the specified '
'capacity.', str(error))
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_resize_with_wait(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--capacity', '2', 'my_cluster', "--wait"]
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_called_once_with(self.mock_client,
+ self.response['action'])
+ mock_await_status.assert_called_once_with(self.mock_client,
+ 'my_cluster')
+ mock_show.assert_called_once()
+
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_resize_without_wait(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--capacity', '2', 'my_cluster']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_show.assert_not_called()
+
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_resize_with_wait_no_action(self, mock_await_status,
+ mock_await_action, mock_show):
+ error = 'test error'
+ self.mock_client.resize_cluster = mock.Mock(return_value=error)
+ arglist = ['--capacity', '2', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_show.assert_not_called()
+
class TestClusterScaleIn(TestCluster):
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
@@ -582,6 +699,48 @@
self.mock_client.scale_in_cluster.assert_called_with('my_cluster',
'2')
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_scale_in_with_wait(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--count', '2', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_called_once_with(self.mock_client,
+ self.response['action'])
+ mock_await_status.assert_called_once_with(self.mock_client,
+ 'my_cluster')
+ mock_show.assert_called_once()
+
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_scale_in_without_wait(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--count', '2', 'my_cluster']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_show.assert_not_called()
+
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_scale_in_with_wait_no_action(self, mock_await_status,
+ mock_await_action,
+ mock_show):
+ arglist = ['--count', '2', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.scale_in_cluster = mock.Mock(return_value=error)
+
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_show.assert_not_called()
+
class TestClusterScaleOut(TestCluster):
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
@@ -599,6 +758,48 @@
self.mock_client.scale_out_cluster.assert_called_with('my_cluster',
'2')
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_scale_out_with_wait(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--count', '2', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_called_once_with(self.mock_client,
+ self.response['action'])
+ mock_await_status.assert_called_once_with(self.mock_client,
+ 'my_cluster')
+ mock_show.assert_called_once()
+
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_scale_out_without_wait(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--count', '2', 'my_cluster']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_show.assert_not_called()
+
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_scale_out_with_wait_no_action(self, mock_await_status,
+ mock_await_action,
+ mock_show):
+ arglist = ['--count', '2', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.scale_out_cluster = mock.Mock(return_value=error)
+
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_show.assert_not_called()
+
class TestClusterPolicyAttach(TestCluster):
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
@@ -618,6 +819,32 @@
'my_policy',
enabled=True)
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_policy_attach_with_wait(self, mock_await_action):
+ arglist = ['--policy', 'my_policy', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_called_once_with(self.mock_client,
+ self.response['action'])
+
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_policy_attach_without_wait(self, mock_await_action):
+ arglist = ['--policy', 'my_policy', 'my_cluster']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_policy_attach_with_wait_no_action(self,
+ mock_await_action):
+ arglist = ['--policy', 'my_policy', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.attach_policy_to_cluster = \
+ mock.Mock(return_value=error)
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+
class TestClusterPolicyDetach(TestCluster):
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
@@ -636,6 +863,32 @@
'my_cluster',
'my_policy')
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_policy_dettach_with_wait(self, mock_await_action):
+ arglist = ['--policy', 'my_policy', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_called_once_with(self.mock_client,
+ self.response['action'])
+
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_policy_dettach_without_wait(self, mock_await_action):
+ arglist = ['--policy', 'my_policy', 'my_cluster']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_policy_dettach_with_wait_no_action(self,
+ mock_await_action):
+ arglist = ['--policy', 'my_policy', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.detach_policy_from_cluster = \
+ mock.Mock(return_value=error)
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+
class TestClusterNodeList(TestCluster):
columns = ['id', 'name', 'index', 'status', 'physical_id', 'created_at']
@@ -734,6 +987,40 @@
'my_cluster',
['node1', 'node2'])
+ @mock.patch.object(osc_cluster, "_show_cluster")
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_node_add_with_wait(self, mock_await_action, mock_show):
+ arglist = ['--nodes', 'node1', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_called_once_with(self.mock_client,
+ self.response['action'])
+ mock_show.assert_called_once_with(self.mock_client, 'my_cluster')
+
+ @mock.patch.object(osc_cluster, "_show_cluster")
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_node_add_without_wait(self, mock_await_action, mock_show):
+ arglist = ['--nodes', 'node1', 'my_cluster']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_not_called()
+ mock_show.assert_not_called()
+
+ @mock.patch.object(osc_cluster, "_show_cluster")
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_node_add_with_wait_no_action(self, mock_await_action,
+ mock_show):
+ arglist = ['--nodes', 'node1', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.add_nodes_to_cluster = mock.Mock(return_value=error)
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_not_called()
+ mock_show.assert_not_called()
+
class TestClusterNodeDel(TestCluster):
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
@@ -771,6 +1058,43 @@
['node1', 'node2'],
destroy_after_deletion=False)
+ @mock.patch.object(osc_cluster, "_show_cluster")
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_node_delete_with_wait(self, mock_await_action, mock_show):
+ arglist = ['--nodes', 'node1', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_called_once_with(self.mock_client,
+ self.response['action'])
+ mock_show.assert_called_once_with(self.mock_client, 'my_cluster')
+
+ @mock.patch.object(osc_cluster, "_show_cluster")
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_node_delete_without_wait(self, mock_await_action,
+ mock_show):
+ arglist = ['--nodes', 'node1', 'my_cluster']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_not_called()
+ mock_show.assert_not_called()
+
+ @mock.patch.object(osc_cluster, "_show_cluster")
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_node_delete_with_wait_no_action(self, mock_await_action,
+ mock_show):
+ arglist = ['--nodes', 'node1', 'my_cluster', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.remove_nodes_from_cluster = \
+ mock.Mock(return_value=error)
+
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_not_called()
+ mock_show.assert_not_called()
+
class TestClusterCheck(TestCluster):
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
@@ -798,6 +1122,48 @@
parsed_args)
self.assertIn('Cluster not found: cluster1', str(error))
+ @mock.patch.object(osc_cluster, "_list_cluster_summaries")
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_check_with_wait(self, mock_await_action,
+ mock_await_status, mock_list):
+ arglist = ['cluster1', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_called_with(self.mock_client,
+ self.response['action'])
+ mock_await_status.assert_called_with(self.mock_client, 'cluster1')
+ mock_list.assert_called_with(self.mock_client, {'cluster1'})
+
+ @mock.patch.object(osc_cluster, "_list_cluster_summaries")
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_check_without_wait(self, mock_await_action,
+ mock_await_status, mock_list):
+ arglist = ['cluster1']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_list.assert_not_called()
+
+ @mock.patch.object(osc_cluster, "_list_cluster_summaries")
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_check_with_wait_no_action(self, mock_await_action,
+ mock_await_status, mock_list):
+ arglist = ['cluster1', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.check_cluster = mock.Mock(return_value=error)
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_list.assert_not_called()
+
class TestClusterRecover(TestCluster):
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
@@ -826,6 +1192,48 @@
parsed_args)
self.assertIn('Cluster not found: cluster1', str(error))
+ @mock.patch.object(osc_cluster, "_list_cluster_summaries")
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_recover_with_wait(self, mock_await_action,
+ mock_await_status, mock_list):
+ arglist = ['cluster1', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_called_with(self.mock_client,
+ self.response['action'])
+ mock_await_status.assert_called_with(self.mock_client, 'cluster1')
+ mock_list.assert_called_with(self.mock_client, {'cluster1'})
+
+ @mock.patch.object(osc_cluster, "_list_cluster_summaries")
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_recover_without_wait(self, mock_await_action,
+ mock_await_status, mock_list):
+ arglist = ['cluster1']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_list.assert_not_called()
+
+ @mock.patch.object(osc_cluster, "_list_cluster_summaries")
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ @mock.patch.object(senlin_utils, 'await_action')
+ def test_cluster_recover_with_wait_no_action(self, mock_await_action,
+ mock_await_status, mock_list):
+ arglist = ['cluster1', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.recover_cluster = mock.Mock(return_value=error)
+ self.cmd.take_action(parsed_args)
+
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_list.assert_not_called()
+
class TestClusterOp(TestCluster):
@@ -856,6 +1264,48 @@
parsed_args)
self.assertIn('Cluster not found: cluster1', str(error))
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_op_with_wait(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--operation', 'dance', 'cluster1', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_called_once_with(self.mock_client,
+ self.response['action'])
+ mock_await_status.assert_called_once_with(self.mock_client,
+ 'cluster1')
+ mock_show.assert_called_once()
+
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_op_without_wait(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--operation', 'dance', 'cluster1']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_show.assert_not_called()
+
+ @mock.patch.object(osc_cluster, '_show_cluster')
+ @mock.patch.object(senlin_utils, 'await_action')
+ @mock.patch.object(senlin_utils, 'await_cluster_status')
+ def test_cluster_op_with_wait_no_action(self, mock_await_status,
+ mock_await_action, mock_show):
+ arglist = ['--operation', 'dance', 'cluster1', '--wait']
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ error = {'error': 'test-error'}
+ self.mock_client.perform_operation_on_cluster = \
+ mock.Mock(return_value=error)
+
+ self.cmd.take_action(parsed_args)
+ mock_await_action.assert_not_called()
+ mock_await_status.assert_not_called()
+ mock_show.assert_not_called()
+
class TestClusterCollect(TestCluster):
response = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/senlinclient/v1/cluster.py
new/python-senlinclient-2.4.0/senlinclient/v1/cluster.py
--- old/python-senlinclient-2.2.1/senlinclient/v1/cluster.py 2021-03-11
12:03:35.000000000 +0100
+++ new/python-senlinclient-2.4.0/senlinclient/v1/cluster.py 2022-02-25
17:20:21.000000000 +0100
@@ -210,6 +210,11 @@
metavar='<cluster-name>',
help=_('Name of the cluster to create')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster creation to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -230,6 +235,8 @@
}
cluster = senlin_client.create_cluster(**attrs)
+ if parsed_args.wait:
+ senlin_utils.await_cluster_status(senlin_client, cluster.id)
return _show_cluster(senlin_client, cluster.id)
@@ -262,7 +269,6 @@
"If false, it will be applied to all existing nodes. "
"If true, any newly created nodes will use the new profile,"
"but existing nodes will not be changed. Default is False.")
-
)
parser.add_argument(
'--timeout',
@@ -288,6 +294,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to be updated')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster update to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -304,12 +315,23 @@
parsed_args.profile_only,
strict=True,
),
- 'metadata': senlin_utils.format_parameters(parsed_args.metadata),
- 'config': senlin_utils.format_parameters(parsed_args.config),
'timeout': parsed_args.timeout,
}
+ if parsed_args.config is not None:
+ attrs['config'] = senlin_utils.format_parameters(
+ parsed_args.config)
+ if parsed_args.metadata is not None:
+ attrs['metadata'] = senlin_utils.format_parameters(
+ parsed_args.metadata)
+
senlin_client.update_cluster(cluster, **attrs)
+ if parsed_args.wait:
+ # PATCH operations do not currently return an action to await.
+ # introducing a delay to allow the cluster to transition state
+ # out of ACTIVE before inspection.
+ time.sleep(1)
+ senlin_utils.await_cluster_status(senlin_client, cluster.id)
return _show_cluster(senlin_client, cluster.id)
@@ -336,6 +358,11 @@
action='store_true',
help=_('Skip yes/no prompt (assume yes).')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster delete to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -361,17 +388,21 @@
result = {}
for cid in parsed_args.cluster:
try:
- cluster_delete_action = senlin_client.delete_cluster(
+ action = senlin_client.delete_cluster(
cid, False, parsed_args.force_delete)
- result[cid] = ('OK', cluster_delete_action['id'])
+ result[cid] = ('OK', action['id'])
except Exception as ex:
result[cid] = ('ERROR', str(ex))
- for rid, res in result.items():
- senlin_utils.print_action_result(rid, res)
+ for cid, a in result.items():
+ senlin_utils.print_action_result(cid, a)
+ if parsed_args.wait:
+ if a[0] == 'OK':
+ senlin_utils.await_action(senlin_client, a[1])
+ senlin_utils.await_cluster_delete(senlin_client, cid)
-class ResizeCluster(command.Command):
+class ResizeCluster(command.ShowOne):
"""Resize a cluster."""
log = logging.getLogger(__name__ + ".ResizeCluster")
@@ -432,6 +463,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to operate on')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster resize to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -446,6 +482,7 @@
min_size = parsed_args.min_size
max_size = parsed_args.max_size
min_step = parsed_args.min_step
+ wait = parsed_args.wait
if sum(v is not None for v in (capacity, adjustment, percentage,
min_size, max_size)) == 0:
@@ -507,13 +544,21 @@
action_args['strict'] = parsed_args.strict
resp = senlin_client.resize_cluster(parsed_args.cluster, **action_args)
+
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
+ senlin_utils.await_cluster_status(senlin_client,
+ parsed_args.cluster)
+ return _show_cluster(senlin_client, parsed_args.cluster)
else:
print('Request error: %s' % resp)
+ return '', ''
-class ScaleInCluster(command.Command):
+
+class ScaleInCluster(command.ShowOne):
"""Scale in a cluster by the specified number of nodes."""
log = logging.getLogger(__name__ + ".ScaleInCluster")
@@ -530,6 +575,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to operate on')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster scale-in to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -544,11 +594,18 @@
'Unable to scale in cluster: %s') % resp['error']['message'])
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if parsed_args.wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
+ senlin_utils.await_cluster_status(senlin_client,
+ parsed_args.cluster)
+ return _show_cluster(senlin_client, parsed_args.cluster)
else:
print('Request error: %s' % resp)
+ return '', ''
-class ScaleOutCluster(command.Command):
+
+class ScaleOutCluster(command.ShowOne):
"""Scale out a cluster by the specified number of nodes."""
log = logging.getLogger(__name__ + ".ScaleOutCluster")
@@ -565,6 +622,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to operate on')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster scale-out to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -579,9 +641,16 @@
'Unable to scale out cluster: %s') % resp['error']['message'])
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if parsed_args.wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
+ senlin_utils.await_cluster_status(senlin_client,
+ parsed_args.cluster)
+ return _show_cluster(senlin_client, parsed_args.cluster)
else:
print('Request error: %s' % resp)
+ return '', ''
+
class ClusterPolicyAttach(command.Command):
"""Attach policy to cluster."""
@@ -608,6 +677,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to operate on')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster policy-attach to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -624,6 +698,8 @@
**kwargs)
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if parsed_args.wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
else:
print('Request error: %s' % resp)
@@ -646,6 +722,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to operate on')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster policy-detach to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -655,6 +736,8 @@
parsed_args.policy)
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if parsed_args.wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
else:
print('Request error: %s' % resp)
@@ -737,7 +820,7 @@
)
-class ClusterNodeAdd(command.Command):
+class ClusterNodeAdd(command.ShowOne):
"""Add specified nodes to cluster."""
log = logging.getLogger(__name__ + ".ClusterNodeAdd")
@@ -755,6 +838,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to operate on')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster members add to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -765,11 +853,16 @@
node_ids)
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if parsed_args.wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
+ return _show_cluster(senlin_client, parsed_args.cluster)
else:
print('Request error: %s' % resp)
+ return '', ''
+
-class ClusterNodeDel(command.Command):
+class ClusterNodeDel(command.ShowOne):
"""Delete specified nodes from cluster."""
log = logging.getLogger(__name__ + ".ClusterNodeDel")
@@ -795,6 +888,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to operate on')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster members delete to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -808,11 +906,16 @@
parsed_args.cluster, node_ids, **kwargs)
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if parsed_args.wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
+ return _show_cluster(senlin_client, parsed_args.cluster)
else:
print('Request error: %s' % resp)
+ return '', ''
+
-class ClusterNodeReplace(command.Command):
+class ClusterNodeReplace(command.ShowOne):
"""Replace the nodes in a cluster with specified nodes."""
log = logging.getLogger(__name__ + ".ClusterNodeReplace")
@@ -833,6 +936,11 @@
metavar='<cluster>',
help=_('Name or ID of cluster to operate on')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster members replace to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -847,11 +955,16 @@
nodepairs)
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if parsed_args.wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
+ return _show_cluster(senlin_client, parsed_args.cluster)
else:
print('Request error: %s' % resp)
+ return '', ''
+
-class CheckCluster(command.Command):
+class CheckCluster(command.Lister):
"""Check the cluster(s)."""
log = logging.getLogger(__name__ + ".CheckCluster")
@@ -863,11 +976,19 @@
nargs='+',
help=_('ID or name of cluster(s) to operate on.')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster check to complete')
+ )
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
senlin_client = self.app.client_manager.clustering
+
+ cluster_actions = {}
+
for cid in parsed_args.cluster:
try:
resp = senlin_client.check_cluster(cid)
@@ -877,11 +998,39 @@
print('Cluster check request on cluster %(cid)s is '
'accepted by action %(action)s.'
% {'cid': cid, 'action': resp['action']})
+ cluster_actions[cid] = resp['action']
else:
print('Request error: %s' % resp)
+ # generate the output after all actions have been accepted/rejected
+ if parsed_args.wait and len(cluster_actions) > 0:
+ for cid, action in cluster_actions.items():
+ senlin_utils.await_action(senlin_client, action)
+ senlin_utils.await_cluster_status(senlin_client, cid)
+ return _list_cluster_summaries(senlin_client,
+ cluster_actions.keys())
+
+ return '', ''
+
-class RecoverCluster(command.Command):
+def _list_cluster_summaries(senlin_client, cluster_ids):
+ clusters = []
+ for cluster_id in cluster_ids:
+ try:
+ cluster = senlin_client.get_cluster(cluster_id)
+ except sdk_exc.ResourceNotFound:
+ raise exc.CommandError(_('Cluster not found: %s') % cluster_id)
+
+ clusters.append(cluster)
+
+ columns = ['ID', 'Name', 'Status', 'Status Reason']
+ formatters = {}
+ props = (utils.get_item_properties(c, columns, formatters=formatters)
+ for c in clusters)
+ return columns, props
+
+
+class RecoverCluster(command.Lister):
"""Recover the cluster(s)."""
log = logging.getLogger(__name__ + ".RecoverCluster")
@@ -893,7 +1042,6 @@
nargs='+',
help=_('ID or name of cluster(s) to operate on.')
)
-
parser.add_argument(
'--check',
metavar='<boolean>',
@@ -901,6 +1049,11 @@
help=_("Whether the cluster should check it's nodes status before "
"doing cluster recover. Default is false")
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster recover to complete')
+ )
return parser
@@ -912,6 +1065,7 @@
'check': strutils.bool_from_string(parsed_args.check, strict=True)
}
+ cluster_actions = {}
for cid in parsed_args.cluster:
try:
resp = senlin_client.recover_cluster(cid, **params)
@@ -921,9 +1075,20 @@
print('Cluster recover request on cluster %(cid)s is '
'accepted by action %(action)s.'
% {'cid': cid, 'action': resp['action']})
+ cluster_actions[cid] = resp['action']
else:
print('Request error: %s' % resp)
+ # generate the output after all actions have been accepted/rejected
+ if parsed_args.wait and len(cluster_actions) > 0:
+ for cid, action in cluster_actions.items():
+ senlin_utils.await_action(senlin_client, action)
+ senlin_utils.await_cluster_status(senlin_client, cid)
+ return _list_cluster_summaries(senlin_client,
+ cluster_actions.keys())
+
+ return '', ''
+
class ClusterCollect(command.Lister):
"""Collect attributes across a cluster."""
@@ -966,7 +1131,7 @@
for a in attrs))
-class ClusterOp(command.Lister):
+class ClusterOp(command.ShowOne):
"""Perform an operation on all nodes across a cluster."""
log = logging.getLogger(__name__ + ".ClusterOp")
@@ -991,6 +1156,11 @@
metavar='<cluster>',
help=_('ID or name of cluster to operate on.')
)
+ parser.add_argument(
+ '--wait',
+ action='store_true',
+ help=_('Wait for cluster operation to complete')
+ )
return parser
def take_action(self, parsed_args):
@@ -1009,9 +1179,15 @@
raise exc.CommandError(_('Cluster not found: %s') % cid)
if 'action' in resp:
print('Request accepted by action: %s' % resp['action'])
+ if parsed_args.wait:
+ senlin_utils.await_action(senlin_client, resp['action'])
+ senlin_utils.await_cluster_status(senlin_client, cid)
+ return _show_cluster(senlin_client, cid)
else:
print('Request error: %s' % resp)
+ return '', ''
+
class ClusterRun(command.Command):
"""Run scripts on cluster."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-senlinclient-2.2.1/tox.ini
new/python-senlinclient-2.4.0/tox.ini
--- old/python-senlinclient-2.2.1/tox.ini 2021-03-11 12:03:35.000000000
+0100
+++ new/python-senlinclient-2.4.0/tox.ini 2022-02-25 17:20:21.000000000
+0100
@@ -1,5 +1,5 @@
[tox]
-envlist = py38,pep8,releasenotes
+envlist = py3,pep8,releasenotes
minversion = 3.1.1
skipsdist = True
ignore_basepython_conflict = True
@@ -11,7 +11,7 @@
usedevelop = True
install_command = pip install {opts} {packages}
deps =
-
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
@@ -54,13 +54,13 @@
[testenv:docs]
deps =
-
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build -W -b html doc/source doc/build/html
[testenv:releasenotes]
deps =
-
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html
releasenotes/source releasenotes/build/html