Hello community,

here is the log from the commit of package python-magnumclient for 
openSUSE:Factory checked in at 2020-06-05 20:10:52
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-magnumclient (Old)
 and      /work/SRC/openSUSE:Factory/.python-magnumclient.new.3606 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-magnumclient"

Fri Jun  5 20:10:52 2020 rev:9 rq:803569 version:3.0.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-magnumclient/python-magnumclient.changes  
2020-03-24 22:33:11.413114133 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-magnumclient.new.3606/python-magnumclient.changes
        2020-06-05 20:14:03.798505562 +0200
@@ -1,0 +2,13 @@
+Tue May 12 10:03:21 UTC 2020 - [email protected]
+
+- update to version 3.0.0
+  - Drop py27 tests
+  - Update hacking for Python3
+  - Replace git.openstack.org URLs with opendev.org URLs
+  - Allow cluster config for any cluster state
+  - Update master for stable/train
+  - Update links in README
+  - Add nodegroup CRUD commands
+  - Bugfix: Use fields option for cluster template list
+
+-------------------------------------------------------------------

Old:
----
  python-magnumclient-2.15.0.tar.gz

New:
----
  python-magnumclient-3.0.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-magnumclient.spec ++++++
--- /var/tmp/diff_new_pack.Wtf12o/_old  2020-06-05 20:14:06.150514216 +0200
+++ /var/tmp/diff_new_pack.Wtf12o/_new  2020-06-05 20:14:06.154514231 +0200
@@ -17,13 +17,13 @@
 
 
 Name:           python-magnumclient
-Version:        2.15.0
+Version:        3.0.0
 Release:        0
 Summary:        Python API and CLI for OpenStack Magnum
 License:        Apache-2.0
 Group:          Development/Languages/Python
 URL:            https://launchpad.net/python-magnumclient
-Source0:        
https://files.pythonhosted.org/packages/source/p/python-magnumclient/python-magnumclient-2.15.0.tar.gz
+Source0:        
https://files.pythonhosted.org/packages/source/p/python-magnumclient/python-magnumclient-3.0.0.tar.gz
 BuildRequires:  openstack-macros
 BuildRequires:  python3-PrettyTable >= 0.7.2
 BuildRequires:  python3-cryptography >= 2.1
@@ -91,15 +91,15 @@
 This package contains the documentation.
 
 %prep
-%autosetup -p1 -n python-magnumclient-2.15.0
+%autosetup -p1 -n python-magnumclient-3.0.0
 %py_req_cleanup
 
 %build
 %{py3_build}
 
 # Build HTML docs and man page
-PBR_VERSION=2.15.0 %sphinx_build -b html doc/source doc/build/html
-PBR_VERSION=2.15.0 %sphinx_build -b man doc/source doc/build/man
+PBR_VERSION=3.0.0 %sphinx_build -b html doc/source doc/build/html
+PBR_VERSION=3.0.0 %sphinx_build -b man doc/source doc/build/man
 rm -r doc/build/html/.{doctrees,buildinfo}
 
 %install

++++++ _service ++++++
--- /var/tmp/diff_new_pack.Wtf12o/_old  2020-06-05 20:14:06.182514334 +0200
+++ /var/tmp/diff_new_pack.Wtf12o/_new  2020-06-05 20:14:06.182514334 +0200
@@ -1,8 +1,8 @@
 <services>
   <service mode="disabled" name="renderspec">
-    <param 
name="input-template">https://raw.githubusercontent.com/openstack/rpm-packaging/stable/train/openstack/python-magnumclient/python-magnumclient.spec.j2</param>
+    <param 
name="input-template">https://opendev.org/openstack/rpm-packaging/raw/branch/stable/ussuri/openstack/python-magnumclient/python-magnumclient.spec.j2</param>
     <param name="output-name">python-magnumclient.spec</param>
-    <param 
name="requirements">https://raw.githubusercontent.com/openstack/python-magnumclient/stable/train/requirements.txt</param>
+    <param 
name="requirements">https://opendev.org/openstack/python-magnumclient/raw/branch/stable/ussuri/requirements.txt</param>
     <param name="changelog-email">[email protected]</param>
     <param name="changelog-provider">gh,openstack,python-magnumclient</param>
   </service>

++++++ python-magnumclient-2.15.0.tar.gz -> python-magnumclient-3.0.0.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/.zuul.yaml 
new/python-magnumclient-3.0.0/.zuul.yaml
--- old/python-magnumclient-2.15.0/.zuul.yaml   2019-08-21 23:13:52.000000000 
+0200
+++ new/python-magnumclient-3.0.0/.zuul.yaml    2020-04-08 02:53:47.000000000 
+0200
@@ -1,9 +1,9 @@
 - project:
     templates:
       - check-requirements
+      - openstack-cover-jobs
       - openstack-lower-constraints-jobs
-      - openstack-python-jobs
-      - openstack-python3-train-jobs
+      - openstack-python3-ussuri-jobs
       - publish-openstack-docs-pti
     check:
       jobs:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/AUTHORS 
new/python-magnumclient-3.0.0/AUTHORS
--- old/python-magnumclient-2.15.0/AUTHORS      2019-08-21 23:14:32.000000000 
+0200
+++ new/python-magnumclient-3.0.0/AUTHORS       2020-04-08 02:54:55.000000000 
+0200
@@ -103,6 +103,7 @@
 Vu Cong Tuan <[email protected]>
 Wanlong Gao <[email protected]>
 Wenzhi Yu <[email protected]>
+XiaojueGuan <[email protected]>
 Yang Hongyang <[email protected]>
 Yolanda Robla <[email protected]>
 Zhenguo Niu <[email protected]>
@@ -115,6 +116,7 @@
 gecong1973 <[email protected]>
 guo yunxian <[email protected]>
 houming-wang <[email protected]>
+jacky06 <[email protected]>
 kavithahr <[email protected]>
 maliki <[email protected]>
 melissaml <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/CONTRIBUTING.rst 
new/python-magnumclient-3.0.0/CONTRIBUTING.rst
--- old/python-magnumclient-2.15.0/CONTRIBUTING.rst     2019-08-21 
23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/CONTRIBUTING.rst      2020-04-08 
02:53:47.000000000 +0200
@@ -13,4 +13,4 @@
 
 Bugs should be filed on Launchpad, not GitHub:
 
-   https://bugs.launchpad.net/python-magnumclient
\ No newline at end of file
+   https://bugs.launchpad.net/python-magnumclient
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/ChangeLog 
new/python-magnumclient-3.0.0/ChangeLog
--- old/python-magnumclient-2.15.0/ChangeLog    2019-08-21 23:14:32.000000000 
+0200
+++ new/python-magnumclient-3.0.0/ChangeLog     2020-04-08 02:54:55.000000000 
+0200
@@ -1,6 +1,21 @@
 CHANGES
 =======
 
+3.0.0
+-----
+
+* Update master for stable/train
+* Update hacking for Python3
+* Drop py27 tests
+* Bugfix: Use fields option for cluster template list
+
+2.17.0
+------
+
+* Allow cluster config for any cluster state
+* Add nodegroup CRUD commands
+* Replace git.openstack.org URLs with opendev.org URLs
+
 2.15.0
 ------
 
@@ -56,6 +71,7 @@
 
 * Switch to stestr
 * osc: Don't pass parameters with null value
+* Update links in README
 * fix tox python3 overrides
 * Fix entrypoints for quotas
 * Follow the new PTI for document build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/PKG-INFO 
new/python-magnumclient-3.0.0/PKG-INFO
--- old/python-magnumclient-2.15.0/PKG-INFO     2019-08-21 23:14:32.000000000 
+0200
+++ new/python-magnumclient-3.0.0/PKG-INFO      2020-04-08 02:54:55.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: python-magnumclient
-Version: 2.15.0
+Version: 3.0.0
 Summary: Client library for Magnum API
 Home-page: https://docs.openstack.org/python-magnumclient/latest/
 Author: OpenStack
@@ -10,8 +10,8 @@
         Team and repository tags
         ========================
         
-        .. image:: 
http://governance.openstack.org/badges/python-magnumclient.svg
-            :target: http://governance.openstack.org/reference/tags/index.html
+        .. image:: 
https://governance.openstack.org/tc/badges/python-magnumclient.svg
+            :target: 
https://governance.openstack.org/tc/reference/tags/index.html
         
         .. Change things from this point on
         
@@ -45,7 +45,7 @@
         .. _Online Documentation: 
https://docs.openstack.org/python-magnumclient/latest/
         .. _Launchpad project: https://launchpad.net/python-magnumclient
         .. _Bugs: https://bugs.launchpad.net/python-magnumclient
-        .. _Source: 
https://git.openstack.org/cgit/openstack/python-magnumclient
+        .. _Source: https://opendev.org/openstack/python-magnumclient
         
         
 Platform: UNKNOWN
@@ -56,7 +56,6 @@
 Classifier: Operating System :: POSIX :: Linux
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/README.rst 
new/python-magnumclient-3.0.0/README.rst
--- old/python-magnumclient-2.15.0/README.rst   2019-08-21 23:13:52.000000000 
+0200
+++ new/python-magnumclient-3.0.0/README.rst    2020-04-08 02:53:47.000000000 
+0200
@@ -2,8 +2,8 @@
 Team and repository tags
 ========================
 
-.. image:: http://governance.openstack.org/badges/python-magnumclient.svg
-    :target: http://governance.openstack.org/reference/tags/index.html
+.. image:: https://governance.openstack.org/tc/badges/python-magnumclient.svg
+    :target: https://governance.openstack.org/tc/reference/tags/index.html
 
 .. Change things from this point on
 
@@ -37,4 +37,4 @@
 .. _Online Documentation: 
https://docs.openstack.org/python-magnumclient/latest/
 .. _Launchpad project: https://launchpad.net/python-magnumclient
 .. _Bugs: https://bugs.launchpad.net/python-magnumclient
-.. _Source: https://git.openstack.org/cgit/openstack/python-magnumclient
+.. _Source: https://opendev.org/openstack/python-magnumclient
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/exceptions.py 
new/python-magnumclient-3.0.0/magnumclient/exceptions.py
--- old/python-magnumclient-2.15.0/magnumclient/exceptions.py   2019-08-21 
23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/exceptions.py    2020-04-08 
02:53:47.000000000 +0200
@@ -18,15 +18,15 @@
 
 # NOTE(akurilin): This alias is left here since v.0.1.3 to support backwards
 # compatibility.
-InvalidEndpoint = EndpointException
-CommunicationError = ConnectionRefused
-HTTPBadRequest = BadRequest
-HTTPInternalServerError = InternalServerError
-HTTPNotFound = NotFound
-HTTPServiceUnavailable = ServiceUnavailable
+InvalidEndpoint = exceptions.EndpointException
+CommunicationError = exceptions.ConnectionRefused
+HTTPBadRequest = exceptions.BadRequest
+HTTPInternalServerError = exceptions.InternalServerError
+HTTPNotFound = exceptions.NotFound
+HTTPServiceUnavailable = exceptions.ServiceUnavailable
 
 
-class AmbiguousAuthSystem(ClientException):
+class AmbiguousAuthSystem(exceptions.ClientException):
     """Could not obtain token and endpoint using provided credentials."""
     pass
 
@@ -35,7 +35,7 @@
 AmbigiousAuthSystem = AmbiguousAuthSystem
 
 
-class InvalidAttribute(ClientException):
+class InvalidAttribute(exceptions.ClientException):
     pass
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/osc/v1/cluster_templates.py 
new/python-magnumclient-3.0.0/magnumclient/osc/v1/cluster_templates.py
--- old/python-magnumclient-2.15.0/magnumclient/osc/v1/cluster_templates.py     
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/osc/v1/cluster_templates.py      
2020-04-08 02:53:47.000000000 +0200
@@ -354,6 +354,8 @@
 
         mag_client = self.app.client_manager.container_infra
         columns = ['uuid', 'name']
+        if parsed_args.fields:
+            columns += parsed_args.fields.split(',')
         cts = mag_client.cluster_templates.list(limit=parsed_args.limit,
                                                 sort_key=parsed_args.sort_key,
                                                 sort_dir=parsed_args.sort_dir)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/osc/v1/clusters.py 
new/python-magnumclient-3.0.0/magnumclient/osc/v1/clusters.py
--- old/python-magnumclient-2.15.0/magnumclient/osc/v1/clusters.py      
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/osc/v1/clusters.py       
2020-04-08 02:53:47.000000000 +0200
@@ -382,10 +382,10 @@
 
         parsed_args.dir = os.path.abspath(parsed_args.dir)
         cluster = mag_client.clusters.get(parsed_args.cluster)
-        if cluster.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE',
-                                  'ROLLBACK_COMPLETE'):
-            raise exceptions.CommandError("cluster in status %s" %
-                                          cluster.status)
+        if cluster.api_address is None:
+            self.log.warning("WARNING: The cluster's api_address is"
+                             " not known yet.")
+
         cluster_template = mag_client.cluster_templates.get(
             cluster.cluster_template_id)
         opts = {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/osc/v1/nodegroups.py 
new/python-magnumclient-3.0.0/magnumclient/osc/v1/nodegroups.py
--- old/python-magnumclient-2.15.0/magnumclient/osc/v1/nodegroups.py    
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/osc/v1/nodegroups.py     
2020-04-08 02:53:47.000000000 +0200
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from magnumclient.common import utils as magnum_utils
 from magnumclient.i18n import _
 
 from osc_lib.command import command
@@ -33,10 +34,133 @@
     'role',
     'max_node_count',
     'min_node_count',
-    'is_default'
+    'is_default',
+    'stack_id',
+    'status',
+    'status_reason'
 ]
 
 
+class CreateNodeGroup(command.Command):
+    _description = _("Create a nodegroup")
+
+    def get_parser(self, prog_name):
+        parser = super(CreateNodeGroup, self).get_parser(prog_name)
+        # NOTE: All arguments are positional and, if not provided
+        # with a default, required.
+        parser.add_argument('--docker-volume-size',
+                            dest='docker_volume_size',
+                            type=int,
+                            metavar='<docker-volume-size>',
+                            help=('The size in GB for the docker volume to '
+                                  'use.'))
+        parser.add_argument('--labels',
+                            metavar='<KEY1=VALUE1,KEY2=VALUE2;KEY3=VALUE3...>',
+                            action='append',
+                            help=_('Arbitrary labels in the form of key=value'
+                                   'pairs to associate with a nodegroup. '
+                                   'May be used multiple times.'))
+        parser.add_argument('cluster',
+                            metavar='<cluster>',
+                            help='Name of the nodegroup to create.')
+        parser.add_argument('name',
+                            metavar='<name>',
+                            help='Name of the nodegroup to create.')
+        parser.add_argument('--node-count',
+                            dest='node_count',
+                            type=int,
+                            default=1,
+                            metavar='<node-count>',
+                            help='The nodegroup node count.')
+        parser.add_argument('--min-nodes',
+                            dest='min_node_count',
+                            type=int,
+                            default=1,
+                            metavar='<min-nodes>',
+                            help='The nodegroup minimum node count.')
+        parser.add_argument('--max-nodes',
+                            dest='max_node_count',
+                            type=int,
+                            default=None,
+                            metavar='<max-nodes>',
+                            help='The nodegroup maximum node count.')
+        parser.add_argument('--role',
+                            dest='role',
+                            type=str,
+                            default='worker',
+                            metavar='<role>',
+                            help=('The role of the nodegroup'))
+        parser.add_argument(
+            '--image',
+            metavar='<image>',
+            help=_('The name or UUID of the base image to customize for the '
+                   'NodeGroup.'))
+        parser.add_argument(
+            '--flavor',
+            metavar='<flavor>',
+            help=_('The nova flavor name or UUID to use when launching the '
+                   'nodes in this NodeGroup.'))
+
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action(%s)", parsed_args)
+
+        mag_client = self.app.client_manager.container_infra
+        args = {
+            'name': parsed_args.name,
+            'node_count': parsed_args.node_count,
+            'max_node_count': parsed_args.max_node_count,
+            'min_node_count': parsed_args.min_node_count,
+            'role': parsed_args.role,
+        }
+
+        if parsed_args.labels is not None:
+            args['labels'] = magnum_utils.handle_labels(parsed_args.labels)
+
+        if parsed_args.docker_volume_size is not None:
+            args['docker_volume_size'] = parsed_args.docker_volume_size
+
+        if parsed_args.flavor is not None:
+            args['flavor_id'] = parsed_args.flavor
+
+        if parsed_args.image is not None:
+            args['image_id'] = parsed_args.image
+
+        cluster_id = parsed_args.cluster
+        nodegroup = mag_client.nodegroups.create(cluster_id, **args)
+        print("Request to create nodegroup %s accepted"
+              % nodegroup.uuid)
+
+
+class DeleteNodeGroup(command.Command):
+    _description = _("Delete a nodegroup")
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteNodeGroup, self).get_parser(prog_name)
+        parser.add_argument(
+            'cluster',
+            metavar='<cluster>',
+            help=_('ID or name of the cluster where the nodegroup(s) '
+                   'belong(s).'))
+        parser.add_argument(
+            'nodegroup',
+            nargs='+',
+            metavar='<nodegroup>',
+            help='ID or name of the nodegroup(s) to delete.')
+
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action(%s)", parsed_args)
+
+        mag_client = self.app.client_manager.container_infra
+        cluster_id = parsed_args.cluster
+        for ng in parsed_args.nodegroup:
+            mag_client.nodegroups.delete(cluster_id, ng)
+            print("Request to delete nodegroup %s has been accepted." % ng)
+
+
 class ListNodeGroup(command.Lister):
     _description = _("List nodegroups")
 
@@ -72,7 +196,8 @@
         self.log.debug("take_action(%s)", parsed_args)
 
         mag_client = self.app.client_manager.container_infra
-        columns = ['uuid', 'name', 'flavor_id', 'node_count', 'role']
+        columns = ['uuid', 'name', 'flavor_id', 'image_id', 'node_count',
+                   'status', 'role']
         cluster_id = parsed_args.cluster
         nodegroups = mag_client.nodegroups.list(cluster_id,
                                                 limit=parsed_args.limit,
@@ -112,3 +237,50 @@
                                               parsed_args.nodegroup)
 
         return (columns, utils.get_item_properties(nodegroup, columns))
+
+
+class UpdateNodeGroup(command.Command):
+    _description = _("Update a Nodegroup")
+
+    def get_parser(self, prog_name):
+        parser = super(UpdateNodeGroup, self).get_parser(prog_name)
+        parser.add_argument(
+            'cluster',
+            metavar='<cluster>',
+            help=_('ID or name of the cluster where the nodegroup belongs.'))
+        parser.add_argument(
+            'nodegroup',
+            metavar='<nodegroup>',
+            help=_('The name or UUID of cluster to update'))
+
+        parser.add_argument(
+            'op',
+            metavar='<op>',
+            choices=['add', 'replace', 'remove'],
+            help=_("Operations: one of 'add', 'replace' or 'remove'"))
+
+        parser.add_argument(
+            'attributes',
+            metavar='<path=value>',
+            nargs='+',
+            action='append',
+            default=[],
+            help=_(
+                "Attributes to add/replace or remove (only PATH is necessary "
+                "on remove)"))
+
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action(%s)", parsed_args)
+
+        mag_client = self.app.client_manager.container_infra
+
+        patch = magnum_utils.args_array_to_patch(parsed_args.op,
+                                                 parsed_args.attributes[0])
+
+        cluster_id = parsed_args.cluster
+        mag_client.nodegroups.update(cluster_id, parsed_args.nodegroup,
+                                     patch)
+        print("Request to update nodegroup %s has been accepted." %
+              parsed_args.nodegroup)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/magnumclient/shell.py 
new/python-magnumclient-3.0.0/magnumclient/shell.py
--- old/python-magnumclient-2.15.0/magnumclient/shell.py        2019-08-21 
23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/shell.py 2020-04-08 
02:53:47.000000000 +0200
@@ -33,6 +33,13 @@
 from oslo_utils import strutils
 import six
 
+from magnumclient.common import cliutils
+from magnumclient import exceptions as exc
+from magnumclient.i18n import _
+from magnumclient.v1 import client as client_v1
+from magnumclient.v1 import shell as shell_v1
+from magnumclient import version
+
 profiler = importutils.try_import("osprofiler.profiler")
 
 HAS_KEYRING = False
@@ -51,12 +58,6 @@
 except ImportError:
     pass
 
-from magnumclient.common import cliutils
-from magnumclient import exceptions as exc
-from magnumclient.i18n import _
-from magnumclient.v1 import client as client_v1
-from magnumclient.v1 import shell as shell_v1
-from magnumclient import version
 
 LATEST_API_VERSION = ('1', 'latest')
 DEFAULT_INTERFACE = 'public'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/osc/unit/v1/fakes.py 
new/python-magnumclient-3.0.0/magnumclient/tests/osc/unit/v1/fakes.py
--- old/python-magnumclient-2.15.0/magnumclient/tests/osc/unit/v1/fakes.py      
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/tests/osc/unit/v1/fakes.py       
2020-04-08 02:53:47.000000000 +0200
@@ -332,7 +332,10 @@
             'role': 'worker',
             'max_node_count': 10,
             'min_node_count': 1,
-            'is_default': False
+            'is_default': False,
+            'stack_id': '3a369884-b6ba-484f-fake-stackb718aff',
+            'status': 'CREATE_COMPLETE',
+            'status_reason': 'None'
         }
 
         # Overwrite default attributes.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/osc/unit/v1/test_cluster_templates.py
 
new/python-magnumclient-3.0.0/magnumclient/tests/osc/unit/v1/test_cluster_templates.py
--- 
old/python-magnumclient-2.15.0/magnumclient/tests/osc/unit/v1/test_cluster_templates.py
     2019-08-21 23:13:52.000000000 +0200
+++ 
new/python-magnumclient-3.0.0/magnumclient/tests/osc/unit/v1/test_cluster_templates.py
      2020-04-08 02:53:47.000000000 +0200
@@ -271,22 +271,24 @@
             '--limit', '1',
             '--sort-key', 'key',
             '--sort-dir', 'asc',
-            '--fields', 'fields'
+            '--fields', 'field1,field2'
         ]
         verifylist = [
             ('limit', 1),
             ('sort_key', 'key'),
             ('sort_dir', 'asc'),
-            ('fields', 'fields'),
+            ('fields', 'field1,field2'),
         ]
+        verifycolumns = self.columns + ['field1', 'field2']
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
-        self.cmd.take_action(parsed_args)
+        columns, data = self.cmd.take_action(parsed_args)
         self.cluster_templates_mock.list.assert_called_with(
             limit=1,
             sort_dir='asc',
             sort_key='key',
         )
+        self.assertEqual(verifycolumns, columns)
 
     def test_cluster_template_list_bad_sort_dir_fail(self):
         arglist = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/osc/unit/v1/test_clusters.py 
new/python-magnumclient-3.0.0/magnumclient/tests/osc/unit/v1/test_clusters.py
--- 
old/python-magnumclient-2.15.0/magnumclient/tests/osc/unit/v1/test_clusters.py  
    2019-08-21 23:13:52.000000000 +0200
+++ 
new/python-magnumclient-3.0.0/magnumclient/tests/osc/unit/v1/test_clusters.py   
    2020-04-08 02:53:47.000000000 +0200
@@ -468,18 +468,6 @@
 
         self.clusters_mock.get.assert_called_with('fake-cluster')
 
-    def test_cluster_config_with_in_progress_status(self):
-        self._cluster.status = 'CREATE_IN_PROGRESS'
-
-        arglist = ['fake-cluster-1']
-        verifylist = [
-            ('cluster', 'fake-cluster-1')
-        ]
-
-        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-        self.assertRaises(exceptions.CommandError,
-                          self.cmd.take_action, parsed_args)
-
 
 class TestClusterResize(TestCluster):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/osc/unit/v1/test_nodegroups.py
 new/python-magnumclient-3.0.0/magnumclient/tests/osc/unit/v1/test_nodegroups.py
--- 
old/python-magnumclient-2.15.0/magnumclient/tests/osc/unit/v1/test_nodegroups.py
    2019-08-21 23:13:52.000000000 +0200
+++ 
new/python-magnumclient-3.0.0/magnumclient/tests/osc/unit/v1/test_nodegroups.py 
    2020-04-08 02:53:47.000000000 +0200
@@ -13,7 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import copy
 import mock
+from mock import call
 
 from magnumclient.osc.v1 import nodegroups as osc_nodegroups
 from magnumclient.tests.osc.unit.v1 import fakes as magnum_fakes
@@ -26,6 +28,176 @@
         self.ng_mock = self.app.client_manager.container_infra.nodegroups
 
 
+class TestNodeGroupCreate(TestNodeGroup):
+
+    def setUp(self):
+        super(TestNodeGroupCreate, self).setUp()
+        self.nodegroup = magnum_fakes.FakeNodeGroup.create_one_nodegroup()
+
+        self.ng_mock.create = mock.Mock()
+        self.ng_mock.create.return_value = self.nodegroup
+
+        self.ng_mock.get = mock.Mock()
+        self.ng_mock.get.return_value = copy.deepcopy(self.nodegroup)
+
+        self.ng_mock.update = mock.Mock()
+        self.ng_mock.update.return_value = self.nodegroup
+
+        self._default_args = {
+            'name': 'fake-nodegroup',
+            'node_count': 1,
+            'role': 'worker',
+            'min_node_count': 1,
+            'max_node_count': None,
+        }
+
+        # Get the command object to test
+        self.cmd = osc_nodegroups.CreateNodeGroup(self.app, None)
+
+        self.data = tuple(map(lambda x: getattr(self.nodegroup, x),
+                              osc_nodegroups.NODEGROUP_ATTRIBUTES))
+
+    def test_nodegroup_create_required_args_pass(self):
+        """Verifies required arguments."""
+
+        arglist = [
+            self.nodegroup.cluster_id,
+            self.nodegroup.name
+        ]
+        verifylist = [
+            ('cluster', self.nodegroup.cluster_id),
+            ('name', self.nodegroup.name)
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        self.cmd.take_action(parsed_args)
+        self.ng_mock.create.assert_called_with(self.nodegroup.cluster_id,
+                                               **self._default_args)
+
+    def test_nodegroup_create_missing_required_arg(self):
+        """Verifies missing required arguments."""
+
+        arglist = [
+            self.nodegroup.name
+        ]
+        verifylist = [
+            ('name', self.nodegroup.name)
+        ]
+        self.assertRaises(magnum_fakes.MagnumParseException,
+                          self.check_parser, self.cmd, arglist, verifylist)
+
+    def test_nodegroup_create_with_labels(self):
+        """Verifies labels are properly parsed when given as argument."""
+
+        expected_args = self._default_args
+        expected_args['labels'] = {
+            'arg1': 'value1', 'arg2': 'value2'
+        }
+
+        arglist = [
+            '--labels', 'arg1=value1',
+            '--labels', 'arg2=value2',
+            self.nodegroup.cluster_id,
+            self.nodegroup.name
+        ]
+        verifylist = [
+            ('labels', ['arg1=value1', 'arg2=value2']),
+            ('name', self.nodegroup.name),
+            ('cluster', self.nodegroup.cluster_id)
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        self.cmd.take_action(parsed_args)
+        self.ng_mock.create.assert_called_with(self.nodegroup.cluster_id,
+                                               **expected_args)
+
+
+class TestNodeGroupDelete(TestNodeGroup):
+
+    def setUp(self):
+        super(TestNodeGroupDelete, self).setUp()
+
+        self.ng_mock.delete = mock.Mock()
+        self.ng_mock.delete.return_value = None
+
+        # Get the command object to test
+        self.cmd = osc_nodegroups.DeleteNodeGroup(self.app, None)
+
+    def test_nodegroup_delete_one(self):
+        arglist = ['foo', 'fake-nodegroup']
+        verifylist = [
+            ('cluster', 'foo'),
+            ('nodegroup', ['fake-nodegroup'])
+        ]
+
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        self.cmd.take_action(parsed_args)
+        self.ng_mock.delete.assert_called_with('foo', 'fake-nodegroup')
+
+    def test_nodegroup_delete_multiple(self):
+        arglist = ['foo', 'fake-nodegroup1', 'fake-nodegroup2']
+        verifylist = [
+            ('cluster', 'foo'),
+            ('nodegroup', ['fake-nodegroup1', 'fake-nodegroup2'])
+        ]
+
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        self.cmd.take_action(parsed_args)
+        self.ng_mock.delete.assert_has_calls(
+            [call('foo', 'fake-nodegroup1'), call('foo', 'fake-nodegroup2')]
+        )
+
+    def test_nodegroup_delete_no_args(self):
+        arglist = []
+        verifylist = [
+            ('cluster', ''),
+            ('nodegroup', [])
+        ]
+
+        self.assertRaises(magnum_fakes.MagnumParseException,
+                          self.check_parser, self.cmd, arglist, verifylist)
+
+
+class TestNodeGroupUpdate(TestNodeGroup):
+
+    def setUp(self):
+        super(TestNodeGroupUpdate, self).setUp()
+
+        self.ng_mock.update = mock.Mock()
+        self.ng_mock.update.return_value = None
+
+        # Get the command object to test
+        self.cmd = osc_nodegroups.UpdateNodeGroup(self.app, None)
+
+    def test_nodegroup_update_pass(self):
+        arglist = ['foo', 'ng1', 'remove', 'bar']
+        verifylist = [
+            ('cluster', 'foo'),
+            ('nodegroup', 'ng1'),
+            ('op', 'remove'),
+            ('attributes', [['bar']])
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        self.cmd.take_action(parsed_args)
+        self.ng_mock.update.assert_called_with(
+            'foo', 'ng1',
+            [{'op': 'remove', 'path': '/bar'}]
+        )
+
+    def test_nodegroup_update_bad_op(self):
+        arglist = ['cluster', 'ng1', 'foo', 'bar']
+        verifylist = [
+            ('cluster', 'cluster'),
+            ('nodegroup', 'ng1'),
+            ('op', 'foo'),
+            ('attributes', ['bar'])
+        ]
+
+        self.assertRaises(magnum_fakes.MagnumParseException,
+                          self.check_parser, self.cmd, arglist, verifylist)
+
+
 class TestNodeGroupShow(TestNodeGroup):
 
     def setUp(self):
@@ -81,14 +253,17 @@
 
     nodegroup = magnum_fakes.FakeNodeGroup.create_one_nodegroup()
 
-    columns = ['uuid', 'name', 'flavor_id', 'node_count', 'role']
+    columns = ['uuid', 'name', 'flavor_id', 'image_id', 'node_count',
+               'status', 'role']
 
     datalist = (
         (
             nodegroup.uuid,
             nodegroup.name,
             nodegroup.flavor_id,
+            nodegroup.image_id,
             nodegroup.node_count,
+            nodegroup.status,
             nodegroup.role,
         ),
     )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/test_utils.py 
new/python-magnumclient-3.0.0/magnumclient/tests/test_utils.py
--- old/python-magnumclient-2.15.0/magnumclient/tests/test_utils.py     
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/tests/test_utils.py      
2020-04-08 02:53:47.000000000 +0200
@@ -123,7 +123,7 @@
         self.assertEqual({}, utils.format_labels(None))
 
     def test_format_labels(self):
-        l = utils.format_labels([
+        la = utils.format_labels([
             'K1=V1,K2=V2,'
             'K3=V3,K4=V4,'
             'K5=V5'])
@@ -132,10 +132,10 @@
                           'K3': 'V3',
                           'K4': 'V4',
                           'K5': 'V5'
-                          }, l)
+                          }, la)
 
     def test_format_labels_semicolon(self):
-        l = utils.format_labels([
+        la = utils.format_labels([
             'K1=V1;K2=V2;'
             'K3=V3;K4=V4;'
             'K5=V5'])
@@ -144,10 +144,10 @@
                           'K3': 'V3',
                           'K4': 'V4',
                           'K5': 'V5'
-                          }, l)
+                          }, la)
 
     def test_format_labels_mix_commas_semicolon(self):
-        l = utils.format_labels([
+        la = utils.format_labels([
             'K1=V1,K2=V2,'
             'K3=V3;K4=V4,'
             'K5=V5'])
@@ -156,10 +156,10 @@
                           'K3': 'V3',
                           'K4': 'V4',
                           'K5': 'V5'
-                          }, l)
+                          }, la)
 
     def test_format_labels_split(self):
-        l = utils.format_labels([
+        la = utils.format_labels([
             'K1=V1,'
             'K2=V22222222222222222222222222222'
             '222222222222222222222222222,'
@@ -167,10 +167,10 @@
         self.assertEqual({'K1': 'V1',
                           'K2': 'V22222222222222222222222222222'
                           '222222222222222222222222222',
-                          'K3': '3.3.3.3'}, l)
+                          'K3': '3.3.3.3'}, la)
 
     def test_format_labels_multiple(self):
-        l = utils.format_labels([
+        la = utils.format_labels([
             'K1=V1',
             'K2=V22222222222222222222222222222'
             '222222222222222222222222222',
@@ -178,35 +178,35 @@
         self.assertEqual({'K1': 'V1',
                           'K2': 'V22222222222222222222222222222'
                           '222222222222222222222222222',
-                          'K3': '3.3.3.3'}, l)
+                          'K3': '3.3.3.3'}, la)
 
     def test_format_labels_multiple_colon_values(self):
-        l = utils.format_labels([
+        la = utils.format_labels([
             'K1=V1',
             'K2=V2,V22,V222,V2222',
             'K3=3.3.3.3'])
         self.assertEqual({'K1': 'V1',
                           'K2': 'V2,V22,V222,V2222',
-                          'K3': '3.3.3.3'}, l)
+                          'K3': '3.3.3.3'}, la)
 
     def test_format_labels_parse_comma_false(self):
-        l = utils.format_labels(
+        la = utils.format_labels(
             ['K1=V1,K2=2.2.2.2,K=V'],
             parse_comma=False)
-        self.assertEqual({'K1': 'V1,K2=2.2.2.2,K=V'}, l)
+        self.assertEqual({'K1': 'V1,K2=2.2.2.2,K=V'}, la)
 
     def test_format_labels_multiple_values_per_labels(self):
-        l = utils.format_labels([
+        la = utils.format_labels([
             'K1=V1',
             'K1=V2'])
-        self.assertEqual({'K1': 'V1,V2'}, l)
+        self.assertEqual({'K1': 'V1,V2'}, la)
 
     def test_format_label_special_label(self):
         labels = ['K1=V1,K22.2.2.2']
-        l = utils.format_labels(
+        la = utils.format_labels(
             labels,
             parse_comma=True)
-        self.assertEqual({'K1': 'V1,K22.2.2.2'}, l)
+        self.assertEqual({'K1': 'V1,K22.2.2.2'}, la)
 
     def test_format_multiple_bad_label(self):
         labels = ['K1=V1', 'K22.2.2.2']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/utils.py 
new/python-magnumclient-3.0.0/magnumclient/tests/utils.py
--- old/python-magnumclient-2.15.0/magnumclient/tests/utils.py  2019-08-21 
23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/tests/utils.py   2020-04-08 
02:53:47.000000000 +0200
@@ -95,7 +95,7 @@
         self.reason = reason
 
     def __getitem__(self, key):
-        if key is 'location':
+        if key == 'location':
             return 'fake_url'
         else:
             return None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/v1/shell_test_base.py 
new/python-magnumclient-3.0.0/magnumclient/tests/v1/shell_test_base.py
--- old/python-magnumclient-2.15.0/magnumclient/tests/v1/shell_test_base.py     
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/tests/v1/shell_test_base.py      
2020-04-08 02:53:47.000000000 +0200
@@ -28,56 +28,56 @@
 
 class TestCommandLineArgument(utils.TestCase):
     _unrecognized_arg_error = [
-        '.*?^usage: ',
-        '.*?^error: unrecognized arguments:',
-        ".*?^Try 'magnum help ' for more information.",
+        r'.*?^usage: ',
+        r'.*?^error: unrecognized arguments:',
+        r".*?^Try 'magnum help ' for more information.",
         ]
 
     _mandatory_group_arg_error = [
-        '.*?^usage: ',
-        '.*?^error: one of the arguments',
-        ".*?^Try 'magnum help ",
+        r'.*?^usage: ',
+        r'.*?^error: one of the arguments',
+        r".*?^Try 'magnum help ",
     ]
 
     _too_many_group_arg_error = [
-        '.*?^usage: ',
-        '.*?^error: (argument \-\-[a-z\-]*: not allowed with argument )',
-        ".*?^Try 'magnum help ",
+        r'.*?^usage: ',
+        r'.*?^error: (argument \-\-[a-z\-]*: not allowed with argument )',
+        r".*?^Try 'magnum help ",
     ]
 
     _mandatory_arg_error = [
-        '.*?^usage: ',
-        '.*?^error: (the following arguments|argument)',
-        ".*?^Try 'magnum help ",
-        ]
+        r'.*?^usage: ',
+        r'.*?^error: (the following arguments|argument)',
+        r".*?^Try 'magnum help ",
+    ]
 
     _duplicate_arg_error = [
-        '.*?^usage: ',
-        '.*?^error: (Duplicate "<.*>" arguments:)',
-        ".*?^Try 'magnum help ",
-        ]
+        r'.*?^usage: ',
+        r'.*?^error: (Duplicate "<.*>" arguments:)',
+        r".*?^Try 'magnum help ",
+    ]
 
     _deprecated_warning = [
-        '.*(WARNING: The \-\-[a-z\-]* parameter is deprecated)+',
-        ('.*(Use the [\<\-a-z\-\>]* (positional )*parameter to avoid seeing '
+        r'.*(WARNING: The \-\-[a-z\-]* parameter is deprecated)+',
+        (r'.*(Use the [\<\-a-z\-\>]* (positional )*parameter to avoid seeing '
          'this message)+')
     ]
 
     _few_argument_error = [
-        '.*?^usage: magnum ',
-        '.*?^error: (the following arguments|too few arguments)',
-        ".*?^Try 'magnum help ",
-        ]
+        r'.*?^usage: magnum ',
+        r'.*?^error: (the following arguments|too few arguments)',
+        r".*?^Try 'magnum help ",
+    ]
 
     _invalid_value_error = [
-        '.*?^usage: ',
-        '.*?^error: argument .*: invalid .* value:',
-        ".*?^Try 'magnum help ",
-        ]
+        r'.*?^usage: ',
+        r'.*?^error: argument .*: invalid .* value:',
+        r".*?^Try 'magnum help ",
+    ]
 
     _bay_status_error = [
-        '.*?^Bay status for',
-        ]
+        r'.*?^Bay status for',
+    ]
 
     def setUp(self):
         super(TestCommandLineArgument, self).setUp()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/v1/test_bays_shell.py 
new/python-magnumclient-3.0.0/magnumclient/tests/v1/test_bays_shell.py
--- old/python-magnumclient-2.15.0/magnumclient/tests/v1/test_bays_shell.py     
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/tests/v1/test_bays_shell.py      
2020-04-08 02:53:47.000000000 +0200
@@ -329,15 +329,6 @@
         self._test_arg_success('bay-config --dir /tmp --force xxx')
         mock_bay.assert_called_with('xxx')
 
-    @mock.patch('magnumclient.v1.baymodels.BayModelManager.get')
-    @mock.patch('magnumclient.v1.bays.BayManager.get')
-    def test_bay_config_failure_wrong_status(self, mock_bay, mock_baymodel):
-        mock_bay.return_value = FakeBay(status='CREATE_IN_PROGRESS')
-        self.assertRaises(exceptions.CommandError,
-                          self._test_arg_failure,
-                          'bay-config xxx',
-                          ['.*?^Bay in status: '])
-
     @mock.patch('magnumclient.v1.bays.BayManager.get')
     def test_bay_config_failure_no_arg(self, mock_bay):
         self._test_arg_failure('bay-config', self._few_argument_error)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/v1/test_clusters_shell.py 
new/python-magnumclient-3.0.0/magnumclient/tests/v1/test_clusters_shell.py
--- old/python-magnumclient-2.15.0/magnumclient/tests/v1/test_clusters_shell.py 
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/tests/v1/test_clusters_shell.py  
2020-04-08 02:53:47.000000000 +0200
@@ -447,17 +447,6 @@
         self._test_arg_success('cluster-config --dir /tmp --force xxx')
         mock_cluster.assert_called_with('xxx')
 
-    @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get')
-    @mock.patch('magnumclient.v1.clusters.ClusterManager.get')
-    def test_cluster_config_failure_wrong_status(self,
-                                                 mock_cluster,
-                                                 mock_clustertemplate):
-        mock_cluster.return_value = FakeCluster(status='CREATE_IN_PROGRESS')
-        self.assertRaises(exceptions.CommandError,
-                          self._test_arg_failure,
-                          'cluster-config xxx',
-                          ['.*?^Cluster in status: '])
-
     @mock.patch('magnumclient.v1.clusters.ClusterManager.get')
     def test_cluster_config_failure_no_arg(self, mock_cluster):
         self._test_arg_failure('cluster-config', self._few_argument_error)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/tests/v1/test_nodegroups.py 
new/python-magnumclient-3.0.0/magnumclient/tests/v1/test_nodegroups.py
--- old/python-magnumclient-2.15.0/magnumclient/tests/v1/test_nodegroups.py     
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/tests/v1/test_nodegroups.py      
2020-04-08 02:53:47.000000000 +0200
@@ -13,9 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import copy
+
 import testtools
 from testtools import matchers
 
+from magnumclient import exceptions
 from magnumclient.tests import utils
 from magnumclient.v1 import nodegroups
 
@@ -53,6 +56,17 @@
     'min_node_count': 1
 }
 
+CREATE_NODEGROUP = copy.deepcopy(NODEGROUP1)
+del CREATE_NODEGROUP['id']
+del CREATE_NODEGROUP['uuid']
+del CREATE_NODEGROUP['node_addresses']
+del CREATE_NODEGROUP['is_default']
+del CREATE_NODEGROUP['cluster_id']
+
+UPDATED_NODEGROUP = copy.deepcopy(NODEGROUP1)
+NEW_NODE_COUNT = 9
+UPDATED_NODEGROUP['node_count'] = NEW_NODE_COUNT
+
 
 fake_responses = {
     '/v1/clusters/test/nodegroups/':
@@ -61,6 +75,10 @@
             {},
             {'nodegroups': [NODEGROUP1, NODEGROUP2]},
         ),
+        'POST': (
+            {},
+            CREATE_NODEGROUP,
+        ),
     },
     '/v1/clusters/test/nodegroups/%s' % NODEGROUP1['id']:
     {
@@ -68,13 +86,36 @@
             {},
             NODEGROUP1
         ),
+        'DELETE': (
+            {},
+            None,
+        ),
+        'PATCH': (
+            {},
+            UPDATED_NODEGROUP,
+        ),
     },
+    '/v1/clusters/test/nodegroups/%s/?rollback=True' % NODEGROUP1['id']:
+        {
+            'PATCH': (
+                {},
+                UPDATED_NODEGROUP,
+            ),
+        },
     '/v1/clusters/test/nodegroups/%s' % NODEGROUP1['name']:
     {
         'GET': (
             {},
             NODEGROUP1
         ),
+        'DELETE': (
+            {},
+            None,
+        ),
+        'PATCH': (
+            {},
+            UPDATED_NODEGROUP,
+        ),
     },
     '/v1/clusters/test/nodegroups/?limit=2':
     {
@@ -222,3 +263,71 @@
         ]
         self.assertEqual(expect, self.api.calls)
         self.assertEqual(NODEGROUP1['name'], nodegroup.name)
+
+    def test_nodegroup_delete_by_id(self):
+        nodegroup = self.mgr.delete(self.cluster_id, NODEGROUP1['id'])
+        expect = [
+            ('DELETE', self.base_path + '%s' % NODEGROUP1['id'], {}, None),
+        ]
+        self.assertEqual(expect, self.api.calls)
+        self.assertIsNone(nodegroup)
+
+    def test_nodegroup_delete_by_name(self):
+        nodegroup = self.mgr.delete(self.cluster_id, NODEGROUP1['name'])
+        expect = [
+            ('DELETE', self.base_path + '%s' % NODEGROUP1['name'], {}, None),
+        ]
+        self.assertEqual(expect, self.api.calls)
+        self.assertIsNone(nodegroup)
+
+    def test_nodegroup_update(self):
+        patch = {'op': 'replace',
+                 'value': NEW_NODE_COUNT,
+                 'path': '/node_count'}
+        nodegroup = self.mgr.update(self.cluster_id, id=NODEGROUP1['id'],
+                                    patch=patch)
+        expect = [
+            ('PATCH', self.base_path + '%s' % NODEGROUP1['id'], {}, patch),
+        ]
+        self.assertEqual(expect, self.api.calls)
+        self.assertEqual(NEW_NODE_COUNT, nodegroup.node_count)
+
+    def test_nodegroup_create(self):
+        nodegroup = self.mgr.create(self.cluster_id, **CREATE_NODEGROUP)
+        expect = [
+            ('POST', self.base_path, {}, CREATE_NODEGROUP),
+        ]
+        self.assertEqual(expect, self.api.calls)
+        self.assertTrue(nodegroup)
+
+    def test_nodegroup_create_with_docker_volume_size(self):
+        ng_with_volume_size = dict()
+        ng_with_volume_size.update(CREATE_NODEGROUP)
+        ng_with_volume_size['docker_volume_size'] = 20
+        nodegroup = self.mgr.create(self.cluster_id, **ng_with_volume_size)
+        expect = [
+            ('POST', self.base_path, {}, ng_with_volume_size),
+        ]
+        self.assertEqual(expect, self.api.calls)
+        self.assertTrue(nodegroup)
+
+    def test_nodegroup_create_with_labels(self):
+        ng_with_labels = dict()
+        ng_with_labels.update(CREATE_NODEGROUP)
+        ng_with_labels['labels'] = "key=val"
+        nodegroup = self.mgr.create(self.cluster_id, **ng_with_labels)
+        expect = [
+            ('POST', self.base_path, {}, ng_with_labels),
+        ]
+        self.assertEqual(expect, self.api.calls)
+        self.assertTrue(nodegroup)
+
+    def test_nodegroup_create_fail(self):
+        CREATE_NODEGROUP_FAIL = copy.deepcopy(CREATE_NODEGROUP)
+        CREATE_NODEGROUP_FAIL["wrong_key"] = "wrong"
+        self.assertRaisesRegex(exceptions.InvalidAttribute,
+                               ("Key must be in %s" %
+                                ','.join(nodegroups.CREATION_ATTRIBUTES)),
+                               self.mgr.create, self.cluster_id,
+                               **CREATE_NODEGROUP_FAIL)
+        self.assertEqual([], self.api.calls)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/v1/bays_shell.py 
new/python-magnumclient-3.0.0/magnumclient/v1/bays_shell.py
--- old/python-magnumclient-2.15.0/magnumclient/v1/bays_shell.py        
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/v1/bays_shell.py 2020-04-08 
02:53:47.000000000 +0200
@@ -228,8 +228,8 @@
     """
     args.dir = os.path.abspath(args.dir)
     bay = cs.bays.get(args.bay)
-    if bay.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE'):
-        raise exceptions.CommandError("Bay in status %s" % bay.status)
+    if (hasattr(bay, 'api_address') and bay.api_address is None):
+        print("WARNING: The bay's api_address is not known yet.")
     baymodel = cs.baymodels.get(bay.baymodel_id)
     opts = {
         'cluster_uuid': bay.uuid,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/v1/clusters_shell.py 
new/python-magnumclient-3.0.0/magnumclient/v1/clusters_shell.py
--- old/python-magnumclient-2.15.0/magnumclient/v1/clusters_shell.py    
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/v1/clusters_shell.py     
2020-04-08 02:53:47.000000000 +0200
@@ -262,9 +262,8 @@
     """
     args.dir = os.path.abspath(args.dir)
     cluster = cs.clusters.get(args.cluster)
-    if cluster.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE',
-                              'ROLLBACK_COMPLETE'):
-        raise exceptions.CommandError("cluster in status %s" % cluster.status)
+    if (hasattr(cluster, 'api_address') and cluster.api_address is None):
+        print("WARNING: The cluster's api_address is not known yet.")
     cluster_template = cs.cluster_templates.get(cluster.cluster_template_id)
     opts = {
         'cluster_uuid': cluster.uuid,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/magnumclient/v1/nodegroups.py 
new/python-magnumclient-3.0.0/magnumclient/v1/nodegroups.py
--- old/python-magnumclient-2.15.0/magnumclient/v1/nodegroups.py        
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/magnumclient/v1/nodegroups.py 2020-04-08 
02:53:47.000000000 +0200
@@ -14,6 +14,7 @@
 #    under the License.
 
 from magnumclient.common import utils
+from magnumclient import exceptions
 from magnumclient.v1 import baseunit
 
 
@@ -65,3 +66,19 @@
             return self._list(self._path(cluster_id, id=id))[0]
         except IndexError:
             return None
+
+    def create(self, cluster_id, **kwargs):
+        new = {}
+        for (key, value) in kwargs.items():
+            if key in CREATION_ATTRIBUTES:
+                new[key] = value
+            else:
+                raise exceptions.InvalidAttribute(
+                    "Key must be in %s" % ",".join(CREATION_ATTRIBUTES))
+        return self._create(self._path(cluster_id), new)
+
+    def delete(self, cluster_id, id):
+        return self._delete(self._path(cluster_id, id=id))
+
+    def update(self, cluster_id, id, patch):
+        return self._update(self._path(cluster_id, id=id), patch)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/python_magnumclient.egg-info/PKG-INFO 
new/python-magnumclient-3.0.0/python_magnumclient.egg-info/PKG-INFO
--- old/python-magnumclient-2.15.0/python_magnumclient.egg-info/PKG-INFO        
2019-08-21 23:14:32.000000000 +0200
+++ new/python-magnumclient-3.0.0/python_magnumclient.egg-info/PKG-INFO 
2020-04-08 02:54:55.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: python-magnumclient
-Version: 2.15.0
+Version: 3.0.0
 Summary: Client library for Magnum API
 Home-page: https://docs.openstack.org/python-magnumclient/latest/
 Author: OpenStack
@@ -10,8 +10,8 @@
         Team and repository tags
         ========================
         
-        .. image:: 
http://governance.openstack.org/badges/python-magnumclient.svg
-            :target: http://governance.openstack.org/reference/tags/index.html
+        .. image:: 
https://governance.openstack.org/tc/badges/python-magnumclient.svg
+            :target: 
https://governance.openstack.org/tc/reference/tags/index.html
         
         .. Change things from this point on
         
@@ -45,7 +45,7 @@
         .. _Online Documentation: 
https://docs.openstack.org/python-magnumclient/latest/
         .. _Launchpad project: https://launchpad.net/python-magnumclient
         .. _Bugs: https://bugs.launchpad.net/python-magnumclient
-        .. _Source: 
https://git.openstack.org/cgit/openstack/python-magnumclient
+        .. _Source: https://opendev.org/openstack/python-magnumclient
         
         
 Platform: UNKNOWN
@@ -56,7 +56,6 @@
 Classifier: Operating System :: POSIX :: Linux
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/python_magnumclient.egg-info/SOURCES.txt 
new/python-magnumclient-3.0.0/python_magnumclient.egg-info/SOURCES.txt
--- old/python-magnumclient-2.15.0/python_magnumclient.egg-info/SOURCES.txt     
2019-08-21 23:14:32.000000000 +0200
+++ new/python-magnumclient-3.0.0/python_magnumclient.egg-info/SOURCES.txt      
2020-04-08 02:54:55.000000000 +0200
@@ -123,5 +123,6 @@
 releasenotes/source/index.rst
 releasenotes/source/rocky.rst
 releasenotes/source/stein.rst
+releasenotes/source/train.rst
 releasenotes/source/unreleased.rst
 tools/magnum.bash_completion
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/python_magnumclient.egg-info/entry_points.txt 
new/python-magnumclient-3.0.0/python_magnumclient.egg-info/entry_points.txt
--- 
old/python-magnumclient-2.15.0/python_magnumclient.egg-info/entry_points.txt    
    2019-08-21 23:14:32.000000000 +0200
+++ new/python-magnumclient-3.0.0/python_magnumclient.egg-info/entry_points.txt 
2020-04-08 02:54:55.000000000 +0200
@@ -21,8 +21,11 @@
 coe_cluster_template_update = 
magnumclient.osc.v1.cluster_templates:UpdateClusterTemplate
 coe_cluster_update = magnumclient.osc.v1.clusters:UpdateCluster
 coe_cluster_upgrade = magnumclient.osc.v1.clusters:UpgradeCluster
+coe_nodegroup_create = magnumclient.osc.v1.nodegroups:CreateNodeGroup
+coe_nodegroup_delete = magnumclient.osc.v1.nodegroups:DeleteNodeGroup
 coe_nodegroup_list = magnumclient.osc.v1.nodegroups:ListNodeGroup
 coe_nodegroup_show = magnumclient.osc.v1.nodegroups:ShowNodeGroup
+coe_nodegroup_update = magnumclient.osc.v1.nodegroups:UpdateNodeGroup
 coe_quotas_create = magnumclient.osc.v1.quotas:CreateQuotas
 coe_quotas_delete = magnumclient.osc.v1.quotas:DeleteQuotas
 coe_quotas_list = magnumclient.osc.v1.quotas:ListQuotas
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/python_magnumclient.egg-info/pbr.json 
new/python-magnumclient-3.0.0/python_magnumclient.egg-info/pbr.json
--- old/python-magnumclient-2.15.0/python_magnumclient.egg-info/pbr.json        
2019-08-21 23:14:32.000000000 +0200
+++ new/python-magnumclient-3.0.0/python_magnumclient.egg-info/pbr.json 
2020-04-08 02:54:55.000000000 +0200
@@ -1 +1 @@
-{"git_version": "93acc92", "is_release": true}
\ No newline at end of file
+{"git_version": "11d2b72", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/releasenotes/source/index.rst 
new/python-magnumclient-3.0.0/releasenotes/source/index.rst
--- old/python-magnumclient-2.15.0/releasenotes/source/index.rst        
2019-08-21 23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/releasenotes/source/index.rst 2020-04-08 
02:53:47.000000000 +0200
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   train
    stein
    rocky
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-magnumclient-2.15.0/releasenotes/source/train.rst 
new/python-magnumclient-3.0.0/releasenotes/source/train.rst
--- old/python-magnumclient-2.15.0/releasenotes/source/train.rst        
1970-01-01 01:00:00.000000000 +0100
+++ new/python-magnumclient-3.0.0/releasenotes/source/train.rst 2020-04-08 
02:53:47.000000000 +0200
@@ -0,0 +1,6 @@
+==========================
+Train Series Release Notes
+==========================
+
+.. release-notes::
+   :branch: stable/train
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/setup.cfg 
new/python-magnumclient-3.0.0/setup.cfg
--- old/python-magnumclient-2.15.0/setup.cfg    2019-08-21 23:14:32.000000000 
+0200
+++ new/python-magnumclient-3.0.0/setup.cfg     2020-04-08 02:54:55.000000000 
+0200
@@ -14,7 +14,6 @@
        Operating System :: POSIX :: Linux
        Programming Language :: Python
        Programming Language :: Python :: 2
-       Programming Language :: Python :: 2.7
        Programming Language :: Python :: 3
        Programming Language :: Python :: 3.6
        Programming Language :: Python :: 3.7
@@ -57,6 +56,9 @@
        
        coe_nodegroup_list = magnumclient.osc.v1.nodegroups:ListNodeGroup
        coe_nodegroup_show = magnumclient.osc.v1.nodegroups:ShowNodeGroup
+       coe_nodegroup_create = magnumclient.osc.v1.nodegroups:CreateNodeGroup
+       coe_nodegroup_delete = magnumclient.osc.v1.nodegroups:DeleteNodeGroup
+       coe_nodegroup_update = magnumclient.osc.v1.nodegroups:UpdateNodeGroup
 
 [compile_catalog]
 directory = magnumclient/locale
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/test-requirements.txt 
new/python-magnumclient-3.0.0/test-requirements.txt
--- old/python-magnumclient-2.15.0/test-requirements.txt        2019-08-21 
23:13:52.000000000 +0200
+++ new/python-magnumclient-3.0.0/test-requirements.txt 2020-04-08 
02:53:47.000000000 +0200
@@ -1,7 +1,7 @@
 # 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.
-hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
+hacking>=2.0,<2.1 # Apache-2.0
 bandit!=1.6.0,>=1.1.0 # Apache-2.0
 coverage!=4.4,>=4.0 # Apache-2.0
 fixtures>=3.0.0 # Apache-2.0/BSD
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-magnumclient-2.15.0/tox.ini 
new/python-magnumclient-3.0.0/tox.ini
--- old/python-magnumclient-2.15.0/tox.ini      2019-08-21 23:13:52.000000000 
+0200
+++ new/python-magnumclient-3.0.0/tox.ini       2020-04-08 02:53:47.000000000 
+0200
@@ -1,6 +1,6 @@
 [tox]
-minversion = 1.6
-envlist = py27,py37,pypy,pep8
+minversion = 2.0
+envlist = py37,pypy,pep8
 skipsdist = True
 
 [testenv]
@@ -11,7 +11,7 @@
    VIRTUAL_ENV={envdir}
    PYTHONWARNINGS=default::DeprecationWarning
 deps =
-       
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+       
-c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt}
        -r{toxinidir}/requirements.txt
        -r{toxinidir}/test-requirements.txt
 commands =
@@ -21,14 +21,14 @@
 [testenv:bandit]
 basepython = python3
 deps =
-  
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+  
-c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt}
   -r{toxinidir}/test-requirements.txt
 commands = bandit -r magnumclient -x tests -n5 -ll
 
 [testenv:pypy]
 basepython = python3
 deps =
-       
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+       
-c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt}
        setuptools<3.2
        -r{toxinidir}/requirements.txt
        -r{toxinidir}/test-requirements.txt
@@ -37,10 +37,6 @@
 basepython = python3
 commands = oslo_debug_helper -t magnumclient/tests {posargs}
 
-[testenv:debug-py27]
-basepython = python2.7
-commands = oslo_debug_helper -t magnumclient/tests {posargs}
-
 [testenv:pep8]
 basepython = python3
 commands =
@@ -73,7 +69,7 @@
 # E123, E125 skipped as they are invalid PEP-8.
 
 show-source = True
-ignore = E123,E125
+ignore = E123,E125,W503,W504
 builtins = _
 exclude=.venv,.git,.tox,dist,doc,,*lib/python*,*egg,build
 


Reply via email to