From 0340275fa9b48cda5a5f4015534ca8cbca23b3d2 Mon Sep 17 00:00:00 2001
From: Huanxuan Ao <huanxuan.ao@easystack.cn>
Date: Sun, 15 Jan 2017 14:37:49 +0800
Subject: [PATCH] Fix quota set command error for SDK > 0.9.10

A bug in OpenStack SDK 0.9.11 and 0.9.12 that causes
quota set command to fail. This can be removed when
the proposed SDK fix (https://review.openstack.org/#/c/419911/)
is released and in the minimum SDK version in global requirements.

Closes-Bug: #1655445
Change-Id: I63132f5f762f0120282f8b92e72512763063e3c6
---
 openstackclient/common/quota.py               | 33 ++++++++-
 .../tests/functional/common/test_quota.py     |  3 -
 .../tests/unit/common/test_quota.py           | 70 +++++++++++--------
 .../notes/bug-1655445-96c787e3a51226e0.yaml   |  6 ++
 4 files changed, 77 insertions(+), 35 deletions(-)
 create mode 100644 releasenotes/notes/bug-1655445-96c787e3a51226e0.yaml

diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index fa6c576552..d86aec58fb 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -182,9 +182,36 @@ class SetQuota(command.Command):
                     project,
                     **volume_kwargs)
             if network_kwargs:
-                network_client.update_quota(
-                    project,
-                    **network_kwargs)
+                if hasattr(_quota.Quota, 'allow_get'):
+                    # TODO(huanxuan): Remove this block once the fixed
+                    # SDK Quota class is the minimum required version.
+                    # This is expected to be SDK release 0.9.13
+                    res = network_client._get_resource(
+                        _quota.Quota, project, **network_kwargs)
+                    if any([res._body.dirty, res._header.dirty]):
+                        request = res._prepare_request(prepend_key=True)
+                        # remove the id in the body
+                        if 'id' in request.body[res.resource_key]:
+                            del request.body[res.resource_key]['id']
+                        if res.patch_update:
+                            response = network_client.session.patch(
+                                request.uri,
+                                endpoint_filter=res.service,
+                                json=request.body,
+                                headers=request.headers
+                            )
+                        else:
+                            response = network_client.session.put(
+                                request.uri,
+                                endpoint_filter=res.service,
+                                json=request.body,
+                                headers=request.headers
+                            )
+                        res._translate_response(response, has_body=True)
+                else:
+                    network_client.update_quota(
+                        project,
+                        **network_kwargs)
 
 
 class ShowQuota(command.ShowOne):
diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py
index b2b198afb4..c1de9aa92d 100644
--- a/openstackclient/tests/functional/common/test_quota.py
+++ b/openstackclient/tests/functional/common/test_quota.py
@@ -10,8 +10,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from openstackclient.tests.functional import base
 
 
@@ -27,7 +25,6 @@ class QuotaTests(base.TestCase):
         cls.PROJECT_NAME =\
             cls.get_openstack_configuration_value('auth.project_name')
 
-    @testtools.skip('broken SDK testing')
     def test_quota_set(self):
         self.openstack('quota set --instances 11 --volumes 11 --networks 11 ' +
                        self.PROJECT_NAME)
diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py
index 7dd2337321..244d74d25d 100644
--- a/openstackclient/tests/unit/common/test_quota.py
+++ b/openstackclient/tests/unit/common/test_quota.py
@@ -13,6 +13,8 @@
 import copy
 import mock
 
+from openstack.network.v2 import quota as _quota
+
 from openstackclient.common import quota
 from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
 from openstackclient.tests.unit import fakes
@@ -282,27 +284,32 @@ class TestQuotaSet(TestQuota):
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
-        result = self.cmd.take_action(parsed_args)
-        kwargs = {
-            'subnet': network_fakes.QUOTA['subnet'],
-            'network': network_fakes.QUOTA['network'],
-            'floatingip': network_fakes.QUOTA['floatingip'],
-            'subnetpool': network_fakes.QUOTA['subnetpool'],
-            'security_group_rule':
-                network_fakes.QUOTA['security_group_rule'],
-            'security_group': network_fakes.QUOTA['security_group'],
-            'router': network_fakes.QUOTA['router'],
-            'rbac_policy': network_fakes.QUOTA['rbac_policy'],
-            'port': network_fakes.QUOTA['port'],
-            'vip': network_fakes.QUOTA['vip'],
-            'healthmonitor': network_fakes.QUOTA['healthmonitor'],
-            'l7policy': network_fakes.QUOTA['l7policy'],
-        }
-        self.network_mock.update_quota.assert_called_once_with(
-            identity_fakes.project_id,
-            **kwargs
-        )
-        self.assertIsNone(result)
+        # TODO(huanxuan): Remove this if condition once the fixed
+        # SDK Quota class is the minimum required version.
+        # This is expected to be SDK release 0.9.13
+        if not hasattr(_quota.Quota, 'allow_get'):
+            # Just run this when sdk <= 0.9.10
+            result = self.cmd.take_action(parsed_args)
+            kwargs = {
+                'subnet': network_fakes.QUOTA['subnet'],
+                'network': network_fakes.QUOTA['network'],
+                'floatingip': network_fakes.QUOTA['floatingip'],
+                'subnetpool': network_fakes.QUOTA['subnetpool'],
+                'security_group_rule':
+                    network_fakes.QUOTA['security_group_rule'],
+                'security_group': network_fakes.QUOTA['security_group'],
+                'router': network_fakes.QUOTA['router'],
+                'rbac_policy': network_fakes.QUOTA['rbac_policy'],
+                'port': network_fakes.QUOTA['port'],
+                'vip': network_fakes.QUOTA['vip'],
+                'healthmonitor': network_fakes.QUOTA['healthmonitor'],
+                'l7policy': network_fakes.QUOTA['l7policy'],
+            }
+            self.network_mock.update_quota.assert_called_once_with(
+                identity_fakes.project_id,
+                **kwargs
+            )
+            self.assertIsNone(result)
 
     def test_quota_set_with_class(self):
         arglist = [
@@ -476,15 +483,20 @@ class TestQuotaShow(TestQuota):
 
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
-        self.cmd.take_action(parsed_args)
+        # TODO(huanxuan): Remove this if condition once the fixed
+        # SDK QuotaDefault class is the minimum required version.
+        # This is expected to be SDK release 0.9.13
+        if not hasattr(_quota.QuotaDefault, 'project'):
+            # Just run this when sdk <= 0.9.10
+            self.cmd.take_action(parsed_args)
 
-        self.quotas_mock.defaults.assert_called_once_with(
-            identity_fakes.project_id)
-        self.volume_quotas_mock.defaults.assert_called_once_with(
-            identity_fakes.project_id)
-        self.network.get_quota_default.assert_called_once_with(
-            identity_fakes.project_id)
-        self.assertNotCalled(self.network.get_quota)
+            self.quotas_mock.defaults.assert_called_once_with(
+                identity_fakes.project_id)
+            self.volume_quotas_mock.defaults.assert_called_once_with(
+                identity_fakes.project_id)
+            self.network.get_quota_default.assert_called_once_with(
+                identity_fakes.project_id)
+            self.assertNotCalled(self.network.get_quota)
 
     def test_quota_show_with_class(self):
         arglist = [
diff --git a/releasenotes/notes/bug-1655445-96c787e3a51226e0.yaml b/releasenotes/notes/bug-1655445-96c787e3a51226e0.yaml
new file mode 100644
index 0000000000..0f8f1ad18f
--- /dev/null
+++ b/releasenotes/notes/bug-1655445-96c787e3a51226e0.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    Work around a bug in OpenStackSDK 0.9.11 and 0.9.12 that causes
+    ``quota set --network`` to fail.
+    [Bug `1655445 <https://bugs.launchpad.net/python-openstackclient/+bug/1655445>`_]