diff -Nru nova-2014.1.5/debian/changelog nova-2014.1.5/debian/changelog --- nova-2014.1.5/debian/changelog 2016-05-17 23:09:58.000000000 +0800 +++ nova-2014.1.5/debian/changelog 2016-09-09 18:21:04.000000000 +0800 @@ -1,3 +1,11 @@ +nova (1:2014.1.5-0ubuntu1.6) trusty; urgency=medium + + * Allow evacuate for an instance in the Error state Edit (LP: #1298061) + - d/p/remove_useless_state_check.patch remove unnecessary task_state check + - d/p/evacuate_error_vm.patch Allow evacuate from error state + + -- Liang Chen Fri, 09 Sep 2016 17:41:48 +0800 + nova (1:2014.1.5-0ubuntu1.5) trusty; urgency=medium * Fix live migration usage of the wrong connector (LP: #1475411) diff -Nru nova-2014.1.5/debian/patches/evacuate_error_vm.patch nova-2014.1.5/debian/patches/evacuate_error_vm.patch --- nova-2014.1.5/debian/patches/evacuate_error_vm.patch 1970-01-01 08:00:00.000000000 +0800 +++ nova-2014.1.5/debian/patches/evacuate_error_vm.patch 2016-09-09 18:09:50.000000000 +0800 @@ -0,0 +1,85 @@ +commit 4551896ced835b0cd89b9ff1ff17ef2bae2282f5 +Author: Chris Friesen +Date: Fri Mar 14 11:37:55 2014 -0600 + + Allow evacuate from vm_state=Error + + We currently allow reboot/rebuild/rescue for an instance in the Error state. + This commit allows "evacuate" as well, since it is essentially a "rebuild" + on a different compute node. + + This is useful in a number of cases, in particular if an initial evacuation + attempt fails. + + Change-Id: I3f513eb738c91fe71767308f57251629639efd6a + Closes-Bug: 1298061 + (cherry picked from commit 2f8dfc0da2fd7f13185c4638aa74013be617cf11) + +diff --git a/nova/compute/api.py b/nova/compute/api.py +index d939aaf..61dcdd0 100644 +--- a/nova/compute/api.py ++++ b/nova/compute/api.py +@@ -3040,7 +3040,8 @@ class API(base.Base): + host_name, block_migration=block_migration, + disk_over_commit=disk_over_commit) + +- @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED]) ++ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED, ++ vm_states.ERROR]) + def evacuate(self, context, instance, host, on_shared_storage, + admin_password=None): + """Running evacuate to target host. +diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py +index e1297a9..6c2333e 100644 +--- a/nova/tests/compute/test_compute.py ++++ b/nova/tests/compute/test_compute.py +@@ -9234,9 +9234,9 @@ class ComputeAPITestCase(BaseTestCase): + instance.refresh() + self.assertEqual(instance['task_state'], task_states.MIGRATING) + +- def test_evacuate(self): ++ def _check_evacuate(self, instance_params=None): + instance = jsonutils.to_primitive(self._create_fake_instance( +- services=True)) ++ instance_params, services=True)) + instance_uuid = instance['uuid'] + instance = db.instance_get_by_uuid(self.context, instance_uuid) + self.assertIsNone(instance['task_state']) +@@ -9265,6 +9265,12 @@ class ComputeAPITestCase(BaseTestCase): + + db.instance_destroy(self.context, instance['uuid']) + ++ def test_evacuate(self): ++ self._check_evacuate() ++ ++ def test_error_evacuate(self): ++ self._check_evacuate({'vm_state': vm_states.ERROR}) ++ + def test_fail_evacuate_from_non_existing_host(self): + inst = {} + inst['vm_state'] = vm_states.ACTIVE +@@ -9333,9 +9339,7 @@ class ComputeAPITestCase(BaseTestCase): + jsonutils.to_primitive(self._create_fake_instance( + {'vm_state': vm_states.SOFT_DELETED})), + jsonutils.to_primitive(self._create_fake_instance( +- {'vm_state': vm_states.DELETED})), +- jsonutils.to_primitive(self._create_fake_instance( +- {'vm_state': vm_states.ERROR})) ++ {'vm_state': vm_states.DELETED})) + ] + + for instance in instances: +diff --git a/nova/tests/compute/test_compute_cells.py b/nova/tests/compute/test_compute_cells.py +index 55f500f..9045246 100644 +--- a/nova/tests/compute/test_compute_cells.py ++++ b/nova/tests/compute/test_compute_cells.py +@@ -148,6 +148,9 @@ class CellsComputeAPITestCase(test_compute.ComputeAPITestCase): + def test_evacuate(self): + self.skipTest("Test is incompatible with cells.") + ++ def test_error_evacuate(self): ++ self.skipTest("Test is incompatible with cells.") ++ + def test_delete_instance_no_cell(self): + cells_rpcapi = self.compute_api.cells_rpcapi + self.mox.StubOutWithMock(cells_rpcapi, diff -Nru nova-2014.1.5/debian/patches/remove_useless_state_check.patch nova-2014.1.5/debian/patches/remove_useless_state_check.patch --- nova-2014.1.5/debian/patches/remove_useless_state_check.patch 1970-01-01 08:00:00.000000000 +0800 +++ nova-2014.1.5/debian/patches/remove_useless_state_check.patch 2016-09-09 18:09:36.000000000 +0800 @@ -0,0 +1,114 @@ +commit 9f9ea6301ca27a1d9f15021e9495196aac92a91a +Author: Chris Yeoh +Date: Fri Mar 14 14:41:30 2014 +1030 + + Remove unnecessary passing of task_state to check_instance_state + + Remove cases where task_state=[None] was passed to check_instance_state + when that is essentially the default value anyway + + Change-Id: I49b6449b9ae43a5cfcf5a1ccac5ee9a64d2b3f3c + (cherry picked from commit e7cbb7a28c50a1e4deb3111ab80e7475d0eca4e1) + +diff --git a/nova/compute/api.py b/nova/compute/api.py +index fd15df6..d939aaf 100644 +--- a/nova/compute/api.py ++++ b/nova/compute/api.py +@@ -1775,8 +1775,7 @@ class API(base.Base): + @check_instance_lock + @check_instance_host + @check_instance_cell +- @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.ERROR], +- task_state=[None]) ++ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.ERROR]) + def stop(self, context, instance, do_cast=True): + """Stop an instance.""" + self.force_stop(context, instance, do_cast) +@@ -2148,8 +2147,7 @@ class API(base.Base): + @check_instance_lock + @check_instance_cell + @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED, +- vm_states.ERROR], +- task_state=[None]) ++ vm_states.ERROR]) + def rebuild(self, context, instance, image_href, admin_password, + files_to_inject=None, **kwargs): + """Rebuild the given instance with the provided attributes.""" +@@ -2385,8 +2383,7 @@ class API(base.Base): + @wrap_check_policy + @check_instance_lock + @check_instance_cell +- @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED], +- task_state=[None]) ++ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED]) + def resize(self, context, instance, flavor_id=None, + **extra_instance_updates): + """Resize (ie, migrate) a running instance. +@@ -2486,8 +2483,7 @@ class API(base.Base): + @wrap_check_policy + @check_instance_lock + @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED, +- vm_states.PAUSED, vm_states.SUSPENDED], +- task_state=[None]) ++ vm_states.PAUSED, vm_states.SUSPENDED]) + def shelve(self, context, instance): + """Shelve an instance. + +@@ -2513,7 +2509,7 @@ class API(base.Base): + + @wrap_check_policy + @check_instance_lock +- @check_instance_state(vm_state=[vm_states.SHELVED], task_state=[None]) ++ @check_instance_state(vm_state=[vm_states.SHELVED]) + def shelve_offload(self, context, instance): + """Remove a shelved instance from the hypervisor.""" + instance.task_state = task_states.SHELVING_OFFLOADING +@@ -2524,7 +2520,7 @@ class API(base.Base): + @wrap_check_policy + @check_instance_lock + @check_instance_state(vm_state=[vm_states.SHELVED, +- vm_states.SHELVED_OFFLOADED], task_state=[None]) ++ vm_states.SHELVED_OFFLOADED]) + def unshelve(self, context, instance): + """Restore a shelved instance.""" + instance.task_state = task_states.UNSHELVING +@@ -2807,8 +2803,7 @@ class API(base.Base): + @check_instance_lock + @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED, + vm_states.STOPPED, vm_states.RESIZED, +- vm_states.SOFT_DELETED], +- task_state=[None]) ++ vm_states.SOFT_DELETED]) + def attach_volume(self, context, instance, volume_id, device=None, + disk_bus=None, device_type=None): + """Attach an existing volume to an existing instance.""" +@@ -2836,8 +2831,7 @@ class API(base.Base): + @check_instance_lock + @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED, + vm_states.STOPPED, vm_states.RESIZED, +- vm_states.SOFT_DELETED], +- task_state=[None]) ++ vm_states.SOFT_DELETED]) + def detach_volume(self, context, instance, volume): + """Detach a volume from an instance.""" + if volume['attach_status'] == 'detached': +@@ -2853,8 +2847,7 @@ class API(base.Base): + @check_instance_lock + @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED, + vm_states.SUSPENDED, vm_states.STOPPED, +- vm_states.RESIZED, vm_states.SOFT_DELETED], +- task_state=[None]) ++ vm_states.RESIZED, vm_states.SOFT_DELETED]) + def swap_volume(self, context, instance, old_volume, new_volume): + """Swap volume attached to an instance.""" + if old_volume['attach_status'] == 'detached': +@@ -3047,8 +3040,7 @@ class API(base.Base): + host_name, block_migration=block_migration, + disk_over_commit=disk_over_commit) + +- @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED], +- task_state=[None]) ++ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED]) + def evacuate(self, context, instance, host, on_shared_storage, + admin_password=None): + """Running evacuate to target host. diff -Nru nova-2014.1.5/debian/patches/series nova-2014.1.5/debian/patches/series --- nova-2014.1.5/debian/patches/series 2016-05-17 22:18:04.000000000 +0800 +++ nova-2014.1.5/debian/patches/series 2016-09-09 18:10:57.000000000 +0800 @@ -12,3 +12,5 @@ Fix-wrong-used-ProcessExecutionError-exception.patch Clean-up-iSCSI-multipath-devices-in-Post-Live-Migrat.patch Detach-iSCSI-latest-path-for-latest-disk.patch +remove_useless_state_check.patch +evacuate_error_vm.patch