From 827ed2d5965dfab456e9f4e26dff9b695d56b75d Mon Sep 17 00:00:00 2001
From: Ying Zuo <yinzuo@cisco.com>
Date: Wed, 18 Oct 2017 21:02:23 +0000
Subject: [PATCH] Revert "Remove the quota check for "Launch Instance" button"

This reverts commit 4083d08e863878fe7990556bae72852a019d39e1.

Change-Id: If583c69fce4182d4512e6377cef1558b192572be
---
 .../dashboards/project/instances/tables.py    | 31 +++++++++
 .../dashboards/project/instances/tests.py     | 64 +++++++++++++++++++
 2 files changed, 95 insertions(+)

diff --git a/openstack_dashboard/dashboards/project/instances/tables.py b/openstack_dashboard/dashboards/project/instances/tables.py
index 579d18c5d5..211b9ca8da 100644
--- a/openstack_dashboard/dashboards/project/instances/tables.py
+++ b/openstack_dashboard/dashboards/project/instances/tables.py
@@ -17,6 +17,7 @@ import logging
 
 from django.conf import settings
 from django.core import urlresolvers
+from django.http import HttpResponse
 from django import shortcuts
 from django import template
 from django.template.defaultfilters import title
@@ -390,6 +391,36 @@ class LaunchLink(tables.LinkAction):
         kwargs['preempt'] = True
         super(LaunchLink, self).__init__(attrs, **kwargs)
 
+    def allowed(self, request, datum):
+        try:
+            limits = api.nova.tenant_absolute_limits(request, reserved=True)
+
+            instances_available = limits['maxTotalInstances'] \
+                - limits['totalInstancesUsed']
+            cores_available = limits['maxTotalCores'] \
+                - limits['totalCoresUsed']
+            ram_available = limits['maxTotalRAMSize'] - limits['totalRAMUsed']
+
+            if instances_available <= 0 or cores_available <= 0 \
+                    or ram_available <= 0:
+                if "disabled" not in self.classes:
+                    self.classes = [c for c in self.classes] + ['disabled']
+                    self.verbose_name = string_concat(self.verbose_name, ' ',
+                                                      _("(Quota exceeded)"))
+            else:
+                self.verbose_name = _("Launch Instance")
+                classes = [c for c in self.classes if c != "disabled"]
+                self.classes = classes
+        except Exception:
+            LOG.exception("Failed to retrieve quota information")
+            # If we can't get the quota information, leave it to the
+            # API to check when launching
+        return True  # The action should always be displayed
+
+    def single(self, table, request, object_id=None):
+        self.allowed(request, None)
+        return HttpResponse(self.render(is_table_action=True))
+
 
 class LaunchLinkNG(LaunchLink):
     name = "launch-ng"
diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py
index 45b33a1019..9b57ef6b1b 100644
--- a/openstack_dashboard/dashboards/project/instances/tests.py
+++ b/openstack_dashboard/dashboards/project/instances/tests.py
@@ -149,6 +149,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
         api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
             .AndReturn([servers, False])
         api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+           .MultipleTimes().AndReturn(self.limits['absolute'])
         api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
             .MultipleTimes().AndReturn(True)
         api.neutron.floating_ip_simple_associate_supported(
@@ -182,6 +184,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
             .AndReturn(images)
         api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
             .AndRaise(self.exceptions.nova)
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+           .MultipleTimes().AndReturn(self.limits['absolute'])
 
         self.mox.ReplayAll()
 
@@ -217,6 +221,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
             .AndRaise(self.exceptions.nova)
         api.glance.image_list_detailed(IgnoreArg()) \
             .AndReturn((self.images.list(), False, False))
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+           .MultipleTimes().AndReturn(self.limits['absolute'])
         api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
             .MultipleTimes().AndReturn(True)
         api.neutron.floating_ip_simple_associate_supported(
@@ -261,6 +267,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
         api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
             .AndReturn([servers, False])
         api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+           .MultipleTimes().AndReturn(self.limits['absolute'])
         api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
             .MultipleTimes().AndReturn(True)
         api.neutron.floating_ip_simple_associate_supported(
@@ -1444,6 +1452,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
         api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
             .AndReturn([servers, False])
         api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+           .MultipleTimes().AndReturn(self.limits['absolute'])
         api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
             .MultipleTimes().AndReturn(True)
         api.neutron.floating_ip_simple_associate_supported(
@@ -3339,6 +3349,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
         api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
             .AndReturn([servers, False])
         api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+            .MultipleTimes().AndReturn(limits)
         api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
             .MultipleTimes().AndReturn(True)
         api.neutron.floating_ip_simple_associate_supported(
@@ -3358,6 +3370,54 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
         self.assertEqual((('compute', 'os_compute_api:servers:create'),),
                          launch_action.policy_rules)
 
+    @helpers.create_stubs({
+        api.nova: ('flavor_list', 'server_list', 'tenant_absolute_limits',
+                   'extension_supported', 'is_feature_available',),
+        api.glance: ('image_list_detailed',),
+        api.neutron: ('floating_ip_simple_associate_supported',
+                      'floating_ip_supported',),
+        api.network: ('servers_update_addresses',),
+    })
+    def test_launch_button_disabled_when_quota_exceeded(self):
+        servers = self.servers.list()
+        limits = self.limits['absolute']
+        limits['totalInstancesUsed'] = limits['maxTotalInstances']
+
+        api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
+            .MultipleTimes().AndReturn(True)
+        api.nova.is_feature_available(
+            IsA(http.HttpRequest), 'locked_attribute'
+        ).MultipleTimes().AndReturn(True)
+        api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
+            .MultipleTimes().AndReturn(True)
+        api.nova.flavor_list(IsA(http.HttpRequest)) \
+            .AndReturn(self.flavors.list())
+        api.glance.image_list_detailed(IgnoreArg()) \
+            .AndReturn((self.images.list(), False, False))
+        search_opts = {'marker': None, 'paginate': True}
+        api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
+            .AndReturn([servers, False])
+        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+            .MultipleTimes().AndReturn(limits)
+        api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
+            .MultipleTimes().AndReturn(True)
+        api.neutron.floating_ip_simple_associate_supported(
+            IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
+
+        self.mox.ReplayAll()
+
+        tables.LaunchLink()
+        res = self.client.get(INDEX_URL)
+
+        launch_action = self.getAndAssertTableAction(
+            res, 'instances', 'launch-ng')
+
+        self.assertIn('disabled', launch_action.classes,
+                      'The launch button should be disabled')
+        self.assertEqual('Launch Instance (Quota exceeded)',
+                         six.text_type(launch_action.verbose_name))
+
     @helpers.create_stubs({api.glance: ('image_list_detailed',),
                            api.neutron: ('network_list',
                                          'port_list_with_trunk_types',
@@ -3492,6 +3552,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
         api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
             .AndReturn([servers, False])
         api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+           .MultipleTimes().AndReturn(self.limits['absolute'])
         api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
             .MultipleTimes().AndReturn(True)
         api.neutron.floating_ip_simple_associate_supported(
@@ -3969,6 +4031,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
         api.network.servers_update_addresses(
             IsA(http.HttpRequest), servers[page_size:])
 
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
+           .MultipleTimes().AndReturn(self.limits['absolute'])
         api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
             .MultipleTimes().AndReturn(True)
         api.neutron.floating_ip_simple_associate_supported(