api: extend evacuate instance to support target state
Start to v2.95 any evacuated instances will be stopped a destination Implements: bp/allowing-target-state-for-evacuate Signed-off-by: Sahid Orentino Ferdjaoui <sahid.ferdjaoui@industrialdiscipline.com> Change-Id: I141b6f057cc4eb9c541c2bc6eddae27270ede08d
This commit is contained in:
parent
8c2e765989
commit
d732ee38a1
@ -0,0 +1,5 @@
|
||||
{
|
||||
"evacuate": {
|
||||
"targetState": "stopped"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"evacuate": {
|
||||
"host": "testHost",
|
||||
"targetState": "stopped"
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.94",
|
||||
"version": "2.95",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.94",
|
||||
"version": "2.95",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -254,6 +254,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
in keypair name.
|
||||
* 2.93 - Add support for volume backed server rebuild.
|
||||
* 2.94 - Allow FQDN in server hostname.
|
||||
* 2.95 - Evacuate will now stop instance at destination.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -262,7 +263,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||
# support is fully merged. It does not affect the V2 API.
|
||||
_MIN_API_VERSION = '2.1'
|
||||
_MAX_API_VERSION = '2.94'
|
||||
_MAX_API_VERSION = '2.95'
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
# Almost all proxy APIs which are related to network, images and baremetal
|
||||
|
@ -23,9 +23,11 @@ from nova.api.openstack.compute.schemas import evacuate
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api import validation
|
||||
from nova.compute import api as compute
|
||||
from nova.compute import vm_states
|
||||
import nova.conf
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova import objects
|
||||
from nova.policies import evacuate as evac_policies
|
||||
from nova import utils
|
||||
|
||||
@ -33,6 +35,8 @@ CONF = nova.conf.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MIN_VER_NOVA_COMPUTE_EVACUATE_STOPPED = 62
|
||||
|
||||
|
||||
class EvacuateController(wsgi.Controller):
|
||||
def __init__(self):
|
||||
@ -77,7 +81,8 @@ class EvacuateController(wsgi.Controller):
|
||||
@validation.schema(evacuate.evacuate, "2.0", "2.13")
|
||||
@validation.schema(evacuate.evacuate_v214, "2.14", "2.28")
|
||||
@validation.schema(evacuate.evacuate_v2_29, "2.29", "2.67")
|
||||
@validation.schema(evacuate.evacuate_v2_68, "2.68")
|
||||
@validation.schema(evacuate.evacuate_v2_68, "2.68", "2.94")
|
||||
@validation.schema(evacuate.evacuate_v2_95, "2.95")
|
||||
def _evacuate(self, req, id, body):
|
||||
"""Permit admins to evacuate a server from a failed host
|
||||
to a new one.
|
||||
@ -92,6 +97,19 @@ class EvacuateController(wsgi.Controller):
|
||||
host = evacuate_body.get("host")
|
||||
force = None
|
||||
|
||||
target_state = None
|
||||
if api_version_request.is_supported(req, min_version='2.95'):
|
||||
min_ver = objects.service.get_minimum_version_all_cells(
|
||||
context, ['nova-compute'])
|
||||
if min_ver < MIN_VER_NOVA_COMPUTE_EVACUATE_STOPPED:
|
||||
raise exception.NotSupportedComputeForEvacuateV295(
|
||||
{'currently': min_ver,
|
||||
'expected': MIN_VER_NOVA_COMPUTE_EVACUATE_STOPPED})
|
||||
# Starts to 2.95 any evacuated instances will be stopped at
|
||||
# destination. Previously an active or stopped instance would have
|
||||
# kept its state.
|
||||
target_state = vm_states.STOPPED
|
||||
|
||||
on_shared_storage = self._get_on_shared_storage(req, evacuate_body)
|
||||
|
||||
if api_version_request.is_supported(req, min_version='2.29'):
|
||||
@ -120,7 +138,8 @@ class EvacuateController(wsgi.Controller):
|
||||
|
||||
try:
|
||||
self.compute_api.evacuate(context, instance, host,
|
||||
on_shared_storage, password, force)
|
||||
on_shared_storage, password, force,
|
||||
target_state)
|
||||
except exception.InstanceInvalidState as state_error:
|
||||
common.raise_http_conflict_for_instance_invalid_state(state_error,
|
||||
'evacuate', id)
|
||||
@ -130,6 +149,8 @@ class EvacuateController(wsgi.Controller):
|
||||
exception.ExtendedResourceRequestOldCompute,
|
||||
) as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
||||
except exception.UnsupportedRPCVersion as e:
|
||||
raise exc.HTTPConflict(explanation=e.format_message())
|
||||
|
||||
if (not api_version_request.is_supported(req, min_version='2.14') and
|
||||
CONF.api.enable_instance_password):
|
||||
|
@ -1237,3 +1237,11 @@ The ``hostname`` parameter to the ``POST /servers`` (create server), ``PUT
|
||||
/servers/{id}`` (update server) and ``POST /servers/{server_id}/action
|
||||
(rebuild)`` (rebuild server) APIs is now allowed to be a Fully Qualified Domain
|
||||
Name (FQDN).
|
||||
|
||||
|
||||
2.95
|
||||
---------------------
|
||||
|
||||
Any evacuated instances will be now stopped at destination. This
|
||||
requires minimun compute version 27.0.0 (antelope 2023.1). Operators
|
||||
can still use previous microversion for older behavior.
|
||||
|
@ -46,3 +46,7 @@ evacuate_v2_29['properties']['evacuate']['properties'][
|
||||
# v2.68 removes the 'force' parameter added in v2.29, meaning it is identical
|
||||
# to v2.14
|
||||
evacuate_v2_68 = copy.deepcopy(evacuate_v214)
|
||||
|
||||
# v2.95 keeps the same schema, evacuating an instance will now result its state
|
||||
# to be stopped at destination.
|
||||
evacuate_v2_95 = copy.deepcopy(evacuate_v2_68)
|
||||
|
@ -2510,3 +2510,10 @@ class ReimageException(NovaException):
|
||||
|
||||
class InvalidNodeConfiguration(NovaException):
|
||||
msg_fmt = _('Invalid node identity configuration: %(reason)s')
|
||||
|
||||
|
||||
class NotSupportedComputeForEvacuateV295(NotSupported):
|
||||
msg_fmt = _("Starting to microversion 2.95, evacuate API will stop "
|
||||
"instance on destination. To evacuate before upgrades are "
|
||||
"complete please use an older microversion. Required version "
|
||||
"for compute %(expected), current version %(currently)s")
|
||||
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"evacuate": {
|
||||
"adminPass": "%(adminPass)s"
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"evacuate": {
|
||||
"adminPass": "%(adminPass)s"
|
||||
}
|
||||
}
|
@ -216,3 +216,41 @@ class EvacuateJsonTestV268(EvacuateJsonTestV229):
|
||||
def test_server_evacuate_with_force(self):
|
||||
# doesn't apply to v2.68+, which removed the ability to force migrate
|
||||
pass
|
||||
|
||||
|
||||
class EvacuateJsonTestV295(EvacuateJsonTestV268):
|
||||
microversion = '2.95'
|
||||
scenarios = [('v2_95', {'api_major_version': 'v2.1'})]
|
||||
|
||||
@mock.patch('nova.conductor.manager.ComputeTaskManager.rebuild_instance')
|
||||
def test_server_evacuate(self, rebuild_mock):
|
||||
req_subs = {
|
||||
"adminPass": "MySecretPass",
|
||||
}
|
||||
self._test_evacuate(req_subs, 'server-evacuate-req',
|
||||
server_resp=None, expected_resp_code=200)
|
||||
|
||||
rebuild_mock.assert_called_once_with(mock.ANY, instance=mock.ANY,
|
||||
orig_image_ref=mock.ANY, image_ref=mock.ANY,
|
||||
injected_files=mock.ANY, new_pass="MySecretPass",
|
||||
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
|
||||
on_shared_storage=None, preserve_ephemeral=mock.ANY,
|
||||
host=None, request_spec=mock.ANY,
|
||||
reimage_boot_volume=False, target_state="stopped")
|
||||
|
||||
@mock.patch('nova.conductor.manager.ComputeTaskManager.rebuild_instance')
|
||||
def test_server_evacuate_find_host(self, rebuild_mock):
|
||||
req_subs = {
|
||||
'host': 'testHost',
|
||||
"adminPass": "MySecretPass",
|
||||
}
|
||||
self._test_evacuate(req_subs, 'server-evacuate-find-host-req',
|
||||
server_resp=None, expected_resp_code=200)
|
||||
|
||||
rebuild_mock.assert_called_once_with(mock.ANY, instance=mock.ANY,
|
||||
orig_image_ref=mock.ANY, image_ref=mock.ANY,
|
||||
injected_files=mock.ANY, new_pass="MySecretPass",
|
||||
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
|
||||
on_shared_storage=None, preserve_ephemeral=mock.ANY,
|
||||
host=None, request_spec=mock.ANY,
|
||||
reimage_boot_volume=False, target_state="stopped")
|
||||
|
@ -598,7 +598,7 @@ class InstanceHelperMixin:
|
||||
|
||||
def _evacuate_server(
|
||||
self, server, extra_post_args=None, expected_host=None,
|
||||
expected_state='ACTIVE', expected_task_state=NOT_SPECIFIED,
|
||||
expected_state='SHUTOFF', expected_task_state=NOT_SPECIFIED,
|
||||
expected_migration_status='done'):
|
||||
"""Evacuate a server."""
|
||||
api = getattr(self, 'admin_api', self.api)
|
||||
|
@ -10,6 +10,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from nova import objects
|
||||
from nova.tests import fixtures
|
||||
from nova.tests.functional.notification_sample_tests \
|
||||
import notification_sample_base
|
||||
@ -53,6 +56,10 @@ class TestComputeTaskNotificationSample(
|
||||
},
|
||||
actual=self.notifier.versioned_notifications[1])
|
||||
|
||||
@mock.patch.object(
|
||||
objects.service, 'get_minimum_version_all_cells',
|
||||
new=mock.Mock(return_value=62)
|
||||
)
|
||||
def test_rebuild_fault(self):
|
||||
server = self._boot_a_server(
|
||||
extra_params={'networks': [{'port': self.neutron.port_1['id']}]},
|
||||
|
@ -46,18 +46,18 @@ class TestInstanceNotificationSampleWithMultipleCompute(
|
||||
self.compute2 = self.start_service('compute', host='host2')
|
||||
|
||||
actions = [
|
||||
self._test_live_migration_rollback,
|
||||
self._test_live_migration_abort,
|
||||
self._test_live_migration_success,
|
||||
self._test_evacuate_server,
|
||||
self._test_live_migration_force_complete
|
||||
(self._test_live_migration_rollback, 'ACTIVE'),
|
||||
(self._test_live_migration_abort, 'ACTIVE'),
|
||||
(self._test_live_migration_success, 'ACTIVE'),
|
||||
(self._test_evacuate_server, 'SHUTOFF'),
|
||||
(self._test_live_migration_force_complete, 'ACTIVE'),
|
||||
]
|
||||
|
||||
for action in actions:
|
||||
for action, expected_state in actions:
|
||||
self.notifier.reset()
|
||||
action(server)
|
||||
# Ensure that instance is in active state after an action
|
||||
self._wait_for_state_change(server, 'ACTIVE')
|
||||
self._wait_for_state_change(server, expected_state)
|
||||
|
||||
@mock.patch('nova.compute.manager.ComputeManager.'
|
||||
'_live_migration_cleanup_flags', return_value=[True, False])
|
||||
@ -275,6 +275,12 @@ class TestInstanceNotificationSampleWithMultipleCompute(
|
||||
self.admin_api.put_service(service_id, {'forced_down': False})
|
||||
|
||||
def _test_live_migration_force_complete(self, server):
|
||||
# In the scenario evacuate happened before which stopped the
|
||||
# server.
|
||||
self._start_server(server)
|
||||
self._wait_for_state_change(server, 'ACTIVE')
|
||||
self.notifier.reset()
|
||||
|
||||
post = {
|
||||
'os-migrateLive': {
|
||||
'host': 'host2',
|
||||
|
@ -59,7 +59,8 @@ class ResizeEvacuateTestCase(integrated_helpers._IntegratedTestBase):
|
||||
# Now try to evacuate the server back to the original source compute.
|
||||
server = self._evacuate_server(
|
||||
server, {'onSharedStorage': 'False'},
|
||||
expected_host=self.compute.host, expected_migration_status='done')
|
||||
expected_host=self.compute.host, expected_migration_status='done',
|
||||
expected_state='ACTIVE')
|
||||
|
||||
# Assert the RequestSpec.ignore_hosts field is not populated.
|
||||
reqspec = objects.RequestSpec.get_by_instance_uuid(
|
||||
|
@ -13,9 +13,11 @@
|
||||
# limitations under the License.
|
||||
|
||||
import time
|
||||
from unittest import mock
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nova import objects
|
||||
from nova import test
|
||||
from nova.tests import fixtures as nova_fixtures
|
||||
from nova.tests.functional import fixtures as func_fixtures
|
||||
@ -81,6 +83,10 @@ class FailedEvacuateStateTests(test.TestCase,
|
||||
created_server = self.api.post_server({'server': server_req})
|
||||
return self._wait_for_state_change(created_server, 'ACTIVE')
|
||||
|
||||
@mock.patch.object(
|
||||
objects.service, 'get_minimum_version_all_cells',
|
||||
new=mock.Mock(return_value=62)
|
||||
)
|
||||
def test_evacuate_no_valid_host(self):
|
||||
# Boot a server
|
||||
server = self._boot_a_server()
|
||||
|
@ -95,7 +95,8 @@ class TestEvacuationWithSourceReturningDuringRebuild(
|
||||
|
||||
# Evacuate the instance from the source_host
|
||||
server = self._evacuate_server(
|
||||
server, expected_migration_status='done')
|
||||
server, expected_migration_status='done',
|
||||
expected_state='ACTIVE')
|
||||
host = server['OS-EXT-SRV-ATTR:host']
|
||||
migrations = self.api.get_migrations()
|
||||
|
||||
|
@ -66,4 +66,5 @@ class MultiCellEvacuateTestCase(integrated_helpers._IntegratedTestBase):
|
||||
# higher than host3.
|
||||
self._evacuate_server(
|
||||
server, {'onSharedStorage': 'False'}, expected_host='host3',
|
||||
expected_migration_status='done')
|
||||
expected_migration_status='done',
|
||||
expected_state='ACTIVE')
|
||||
|
@ -216,7 +216,7 @@ class TestEvacuateResourceTrackerRace(
|
||||
self._run_periodics()
|
||||
|
||||
self._wait_for_server_parameter(
|
||||
server, {'OS-EXT-SRV-ATTR:host': 'host2', 'status': 'ACTIVE'})
|
||||
server, {'OS-EXT-SRV-ATTR:host': 'host2', 'status': 'SHUTOFF'})
|
||||
|
||||
self._assert_pci_device_allocated(server['id'], self.compute1_id)
|
||||
self._assert_pci_device_allocated(server['id'], self.compute2_id)
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
@ -27,6 +28,7 @@ class ForceUpWithDoneEvacuations(integrated_helpers._IntegratedTestBase):
|
||||
|
||||
ADMIN_API = True
|
||||
microversion = 'latest'
|
||||
expected_state = 'SHUTOFF'
|
||||
|
||||
def _create_test_server(self, compute_host):
|
||||
return self._create_server(host=compute_host, networks='none')
|
||||
@ -59,7 +61,8 @@ class ForceUpWithDoneEvacuations(integrated_helpers._IntegratedTestBase):
|
||||
server = self._evacuate_server(
|
||||
server,
|
||||
expected_host='compute2',
|
||||
expected_migration_status='done'
|
||||
expected_migration_status='done',
|
||||
expected_state=self.expected_state
|
||||
)
|
||||
|
||||
# Assert that the request to force up the host is rejected
|
||||
@ -97,6 +100,7 @@ class ForceUpWithDoneEvacuationsv252(ForceUpWithDoneEvacuations):
|
||||
"""
|
||||
|
||||
microversion = '2.52'
|
||||
expected_state = 'ACTIVE'
|
||||
|
||||
def _create_test_server(self, compute_host):
|
||||
return self._create_server(az='nova:compute', networks='none')
|
||||
|
@ -444,7 +444,8 @@ class ServerGroupTestV21(ServerGroupTestBase):
|
||||
|
||||
evacuated_server = self._evacuate_server(
|
||||
servers[1], {'onSharedStorage': 'False'},
|
||||
expected_migration_status='done')
|
||||
expected_migration_status='done',
|
||||
expected_state='ACTIVE')
|
||||
|
||||
# check that the server is evacuated to another host
|
||||
self.assertNotEqual(evacuated_server['OS-EXT-SRV-ATTR:host'],
|
||||
@ -621,7 +622,8 @@ class ServerGroupTestV215(ServerGroupTestV21):
|
||||
compute3 = self.start_service('compute', host='host3')
|
||||
|
||||
evacuated_server = self._evacuate_server(
|
||||
servers[1], expected_migration_status='done')
|
||||
servers[1], expected_migration_status='done',
|
||||
expected_state='ACTIVE')
|
||||
|
||||
# check that the server is evacuated
|
||||
self.assertNotEqual(evacuated_server['OS-EXT-SRV-ATTR:host'],
|
||||
@ -800,7 +802,8 @@ class ServerGroupTestV215(ServerGroupTestV21):
|
||||
self._set_forced_down(host, True)
|
||||
|
||||
evacuated_server = self._evacuate_server(
|
||||
servers[1], expected_migration_status='done')
|
||||
servers[1], expected_migration_status='done',
|
||||
expected_state='ACTIVE')
|
||||
|
||||
# Note(gibi): need to get the server again as the state of the instance
|
||||
# goes to ACTIVE first then the host of the instance changes to the
|
||||
@ -870,6 +873,54 @@ class ServerGroupTestV264(ServerGroupTestV215):
|
||||
self.assertEqual(2, hosts.count(host))
|
||||
|
||||
|
||||
class ServerGroupTestV295(ServerGroupTestV264):
|
||||
microversion = '2.95'
|
||||
|
||||
def _evacuate_with_soft_anti_affinity_policies(self, group):
|
||||
created_group = self.api.post_server_groups(group)
|
||||
servers = self._boot_servers_to_group(created_group)
|
||||
|
||||
host = self._get_compute_service_by_host_name(
|
||||
servers[1]['OS-EXT-SRV-ATTR:host'])
|
||||
# Set forced_down on the host to ensure nova considers the host down.
|
||||
self._set_forced_down(host, True)
|
||||
|
||||
evacuated_server = self._evacuate_server(
|
||||
servers[1], expected_migration_status='done')
|
||||
|
||||
# Note(gibi): need to get the server again as the state of the instance
|
||||
# goes to ACTIVE first then the host of the instance changes to the
|
||||
# new host later
|
||||
evacuated_server = self.admin_api.get_server(evacuated_server['id'])
|
||||
|
||||
return [evacuated_server['OS-EXT-SRV-ATTR:host'],
|
||||
servers[0]['OS-EXT-SRV-ATTR:host']]
|
||||
|
||||
def test_evacuate_with_anti_affinity(self):
|
||||
created_group = self.api.post_server_groups(self.anti_affinity)
|
||||
servers = self._boot_servers_to_group(created_group)
|
||||
|
||||
host = self._get_compute_service_by_host_name(
|
||||
servers[1]['OS-EXT-SRV-ATTR:host'])
|
||||
# Set forced_down on the host to ensure nova considers the host down.
|
||||
self._set_forced_down(host, True)
|
||||
|
||||
# Start additional host to test evacuation
|
||||
compute3 = self.start_service('compute', host='host3')
|
||||
|
||||
evacuated_server = self._evacuate_server(
|
||||
servers[1], expected_migration_status='done')
|
||||
|
||||
# check that the server is evacuated
|
||||
self.assertNotEqual(evacuated_server['OS-EXT-SRV-ATTR:host'],
|
||||
servers[1]['OS-EXT-SRV-ATTR:host'])
|
||||
# check that policy is kept
|
||||
self.assertNotEqual(evacuated_server['OS-EXT-SRV-ATTR:host'],
|
||||
servers[0]['OS-EXT-SRV-ATTR:host'])
|
||||
|
||||
compute3.kill()
|
||||
|
||||
|
||||
class ServerGroupTestMultiCell(ServerGroupTestBase):
|
||||
|
||||
NUMBER_OF_CELLS = 2
|
||||
|
@ -2260,7 +2260,8 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
|
||||
}
|
||||
|
||||
server = self._evacuate_server(
|
||||
server, extra_post_args=post, expected_host=dest_hostname)
|
||||
server, extra_post_args=post, expected_host=dest_hostname,
|
||||
expected_state='ACTIVE')
|
||||
|
||||
# Run the periodics to show those don't modify allocations.
|
||||
self._run_periodics()
|
||||
@ -2437,7 +2438,8 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
|
||||
# stay ACTIVE and task_state will be set to None.
|
||||
server = self._evacuate_server(
|
||||
server, expected_task_state=None,
|
||||
expected_migration_status='failed')
|
||||
expected_migration_status='failed',
|
||||
expected_state='ACTIVE')
|
||||
|
||||
# Run the periodics to show those don't modify allocations.
|
||||
self._run_periodics()
|
||||
@ -5324,7 +5326,8 @@ class ServerMovingTestsWithNestedResourceRequests(
|
||||
|
||||
server = self._evacuate_server(
|
||||
server, extra_post_args=post, expected_migration_status='error',
|
||||
expected_host=source_hostname)
|
||||
expected_host=source_hostname,
|
||||
expected_state='ACTIVE')
|
||||
|
||||
self.assertIn('Unable to move instance %s to host host2. The instance '
|
||||
'has complex allocations on the source host so move '
|
||||
@ -5530,7 +5533,8 @@ class ServerMovingTestsFromFlatToNested(
|
||||
|
||||
self._evacuate_server(
|
||||
server, extra_post_args=post, expected_host='host1',
|
||||
expected_migration_status='error')
|
||||
expected_migration_status='error',
|
||||
expected_state='ACTIVE')
|
||||
|
||||
# We expect that the evacuation will fail as force evacuate tries to
|
||||
# blindly copy the source allocation to the destination but on the
|
||||
|
@ -2162,7 +2162,8 @@ class ServerMoveWithPortResourceRequestTest(
|
||||
# simply fail and the server remains on the source host
|
||||
server = self._evacuate_server(
|
||||
server, expected_host='host1', expected_task_state=None,
|
||||
expected_migration_status='failed')
|
||||
expected_migration_status='failed',
|
||||
expected_state="ACTIVE")
|
||||
|
||||
# As evacuation failed the resource allocation should be untouched
|
||||
self._check_allocation(
|
||||
@ -2979,6 +2980,7 @@ class ExtendedResourceRequestOldCompute(
|
||||
super().setUp()
|
||||
self.neutron = self.useFixture(
|
||||
ExtendedResourceRequestNeutronFixture(self))
|
||||
self.api.microversion = '2.72'
|
||||
|
||||
@mock.patch.object(
|
||||
objects.service, 'get_minimum_version_all_cells',
|
||||
|
@ -416,3 +416,32 @@ class EvacuateTestV268(EvacuateTestV229):
|
||||
def test_forced_evacuate_with_no_host_provided(self):
|
||||
# not applicable for v2.68, which removed the 'force' parameter
|
||||
pass
|
||||
|
||||
|
||||
class EvacuateTestV295(EvacuateTestV268):
|
||||
def setUp(self):
|
||||
super(EvacuateTestV268, self).setUp()
|
||||
self.admin_req = fakes.HTTPRequest.blank('', use_admin_context=True,
|
||||
version='2.95')
|
||||
self.req = fakes.HTTPRequest.blank('', version='2.95')
|
||||
self.mock_get_min_ver = self.useFixture(fixtures.MockPatch(
|
||||
'nova.objects.service.get_minimum_version_all_cells',
|
||||
return_value=62)).mock
|
||||
|
||||
def test_evacuate_version_error(self):
|
||||
self.mock_get_min_ver.return_value = 61
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self._get_evacuate_response,
|
||||
{'host': 'my-host', 'adminPass': 'foo'})
|
||||
|
||||
def test_evacuate_unsupported_rpc(self):
|
||||
def fake_evacuate(*args, **kwargs):
|
||||
raise exception.UnsupportedRPCVersion(
|
||||
api="fakeapi",
|
||||
required="x.xx")
|
||||
|
||||
self.stub_out('nova.compute.api.API.evacuate', fake_evacuate)
|
||||
self._check_evacuate_failure(webob.exc.HTTPConflict,
|
||||
{'host': 'my-host',
|
||||
'onSharedStorage': 'False',
|
||||
'adminPass': 'MyNewPass'})
|
||||
|
@ -103,7 +103,7 @@ class EvacuatePolicyTest(base.BasePolicyTest):
|
||||
evacuate_mock.assert_called_once_with(
|
||||
self.user_req.environ['nova.context'],
|
||||
mock.ANY, 'my-host', False,
|
||||
'MyNewPass', None)
|
||||
'MyNewPass', None, None)
|
||||
|
||||
|
||||
class EvacuateNoLegacyNoScopePolicyTest(EvacuatePolicyTest):
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Starting with v2.95 any evacuated instance will be stopped at
|
||||
destination. The required minimum version for Nova computes is
|
||||
27.0.0 (antelope 2023.1). Operator can still continue using
|
||||
previous behavior by selecting microversion below v2.95.
|
||||
upgrade:
|
||||
- |
|
||||
Operators will have to consider upgrading compute hosts to Nova
|
||||
27.0.0 (antelope 2023.1) in order to take advantage of the new
|
||||
(microversion v2.95) evacuate API behavior. An exception will be
|
||||
raised for older versions.
|
Loading…
Reference in New Issue
Block a user