Author: Pavlo Shchelokovskyy <shchelokovskyy@gmail.com>
Date: Thu, 13 Jun 2024 11:43:53 +0300
Description: Allow non-admins to cold migrate instances
 since [0] nova technically can allow non-admin users to do
 cold migrations without specifying the target host.
 .
 This patch makes the cold migration action available in project
 dashboard by moving MigrateInstance form to project dashboard.
 Its availability is determined by the same policy rule,
 and admin dashboard now just re-uses the from from project dashboard.
 .
 [0] https://review.opendev.org/c/openstack/nova/+/881562
Change-Id: Ic6a0c1c85cbe507b70ffdebeffb68297ca4338fe
Origin: upstream, https://review.opendev.org/c/openstack/horizon/+/922022
Last-Update: 2024-07-05

diff --git a/openstack_dashboard/dashboards/admin/instances/tables.py b/openstack_dashboard/dashboards/admin/instances/tables.py
index 0e8bac4..8d03c4f 100644
--- a/openstack_dashboard/dashboards/admin/instances/tables.py
+++ b/openstack_dashboard/dashboards/admin/instances/tables.py
@@ -16,7 +16,6 @@
 from django.template.defaultfilters import title
 from django import urls
 from django.utils.translation import gettext_lazy as _
-from django.utils.translation import ngettext_lazy
 from keystoneclient import exceptions as keystone_exceptions
 
 from horizon import tables
@@ -46,38 +45,6 @@
     url = "horizon:admin:instances:rescue"
 
 
-class MigrateInstance(policy.PolicyTargetMixin, tables.BatchAction):
-    name = "migrate"
-    classes = ("btn-migrate",)
-    policy_rules = (("compute", "os_compute_api:os-migrate-server:migrate"),)
-    help_text = _("Migrating instances may cause some unrecoverable results.")
-    action_type = "danger"
-
-    @staticmethod
-    def action_present(count):
-        return ngettext_lazy(
-            "Migrate Instance",
-            "Migrate Instances",
-            count
-        )
-
-    @staticmethod
-    def action_past(count):
-        return ngettext_lazy(
-            "Scheduled migration (pending confirmation) of Instance",
-            "Scheduled migration (pending confirmation) of Instances",
-            count
-        )
-
-    def allowed(self, request, instance):
-        return ((instance.status in project_tables.ACTIVE_STATES or
-                 instance.status == 'SHUTOFF') and
-                not project_tables.is_deleting(instance))
-
-    def action(self, request, obj_id):
-        api.nova.server_migrate(request, obj_id)
-
-
 class LiveMigrateInstance(policy.PolicyTargetMixin,
                           tables.LinkAction):
     name = "live_migrate"
@@ -204,7 +171,7 @@
                        project_tables.TogglePause,
                        project_tables.ToggleSuspend,
                        project_tables.ToggleShelve,
-                       MigrateInstance,
+                       project_tables.MigrateInstance,
                        LiveMigrateInstance,
                        project_tables.SoftRebootInstance,
                        project_tables.RebootInstance,
diff --git a/openstack_dashboard/dashboards/project/instances/tables.py b/openstack_dashboard/dashboards/project/instances/tables.py
index c281fcb..34f619d 100644
--- a/openstack_dashboard/dashboards/project/instances/tables.py
+++ b/openstack_dashboard/dashboards/project/instances/tables.py
@@ -1007,6 +1007,38 @@
         return urls.reverse(self.url, args=[instance_id])
 
 
+class MigrateInstance(policy.PolicyTargetMixin, tables.BatchAction):
+    name = "migrate"
+    classes = ("btn-migrate",)
+    policy_rules = (("compute", "os_compute_api:os-migrate-server:migrate"),)
+    help_text = _("Migrating instances may cause some unrecoverable results.")
+    action_type = "danger"
+
+    @staticmethod
+    def action_present(count):
+        return ngettext_lazy(
+            "Migrate Instance",
+            "Migrate Instances",
+            count
+        )
+
+    @staticmethod
+    def action_past(count):
+        return ngettext_lazy(
+            "Scheduled migration (pending confirmation) of Instance",
+            "Scheduled migration (pending confirmation) of Instances",
+            count
+        )
+
+    def allowed(self, request, instance):
+        return ((instance.status in ACTIVE_STATES or
+                 instance.status == 'SHUTOFF') and
+                not is_deleting(instance))
+
+    def action(self, request, obj_id):
+        api.nova.server_migrate(request, obj_id)
+
+
 def get_ips(instance):
     template_name = 'project/instances/_instance_ips.html'
     ip_groups = {}
@@ -1304,4 +1336,5 @@
                        TogglePause, ToggleSuspend, ToggleShelve,
                        ResizeLink, LockInstance, UnlockInstance,
                        SoftRebootInstance, RebootInstance,
+                       MigrateInstance,
                        StopInstance, RebuildInstance, DeleteInstance)
