Merge "Fix volume-backed resize with a smaller disk flavor"

This commit is contained in:
Zuul
2019-05-01 15:17:14 +00:00
committed by Gerrit Code Review
3 changed files with 61 additions and 21 deletions

View File

@@ -572,8 +572,7 @@ class API(base.Base):
with each other.
:param context: A context.RequestContext
:param image: a dict representation of the image including properties,
enforces the image status is active.
:param image: a dict representation of the image including properties
:param instance_type: Flavor object
:param root_bdm: BlockDeviceMapping for root disk. Will be None for
the resize case.
@@ -666,6 +665,30 @@ class API(base.Base):
servers_policies.ZERO_DISK_FLAVOR, fatal=False):
raise exception.BootFromVolumeRequiredForZeroDiskFlavor()
API._validate_flavor_image_numa_pci(
image, instance_type, validate_numa=validate_numa,
validate_pci=validate_pci)
@staticmethod
def _validate_flavor_image_numa_pci(image, instance_type,
validate_numa=True,
validate_pci=False):
"""Validate the flavor and image NUMA/PCI values.
This is called from the API service to ensure that the flavor
extra-specs and image properties are self-consistent and compatible
with each other.
:param image: a dict representation of the image including properties
:param instance_type: Flavor object
:param validate_numa: Flag to indicate whether or not to validate
the NUMA-related metadata.
:param validate_pci: Flag to indicate whether or not to validate
the PCI-related metadata.
:raises: Many different possible exceptions. See
api.openstack.compute.servers.INVALID_FLAVOR_IMAGE_EXCEPTIONS
for the full list.
"""
image_meta = _get_image_meta_obj(image)
# Only validate values of flavor/image so the return results of
@@ -3572,6 +3595,7 @@ class API(base.Base):
current_instance_type = instance.get_flavor()
# If flavor_id is not provided, only migrate the instance.
volume_backed = None
if not flavor_id:
LOG.debug("flavor_id is None. Assuming migration.",
instance=instance)
@@ -3579,12 +3603,15 @@ class API(base.Base):
else:
new_instance_type = flavors.get_flavor_by_flavor_id(
flavor_id, read_deleted="no")
# Check to see if we're resizing to a zero-disk flavor which is
# only supported with volume-backed servers.
if (new_instance_type.get('root_gb') == 0 and
current_instance_type.get('root_gb') != 0 and
not compute_utils.is_volume_backed_instance(context,
instance)):
reason = _('Resize to zero disk flavor is not allowed.')
raise exception.CannotResizeDisk(reason=reason)
current_instance_type.get('root_gb') != 0):
volume_backed = compute_utils.is_volume_backed_instance(
context, instance)
if not volume_backed:
reason = _('Resize to zero disk flavor is not allowed.')
raise exception.CannotResizeDisk(reason=reason)
if not new_instance_type:
raise exception.FlavorNotFound(flavor_id=flavor_id)
@@ -3617,10 +3644,23 @@ class API(base.Base):
if not same_instance_type:
image = utils.get_image_from_system_metadata(
instance.system_metadata)
# Can skip root_bdm check since it will not change during resize.
self._validate_flavor_image_nostatus(
context, image, new_instance_type, root_bdm=None,
validate_pci=True)
# Figure out if the instance is volume-backed but only if we didn't
# already figure that out above (avoid the extra db hit).
if volume_backed is None:
volume_backed = compute_utils.is_volume_backed_instance(
context, instance)
# If the server is volume-backed, we still want to validate numa
# and pci information in the new flavor, but we don't call
# _validate_flavor_image_nostatus because how it handles checking
# disk size validation was not intended for a volume-backed
# resize case.
if volume_backed:
self._validate_flavor_image_numa_pci(
image, new_instance_type, validate_pci=True)
else:
self._validate_flavor_image_nostatus(
context, image, new_instance_type, root_bdm=None,
validate_pci=True)
filter_properties = {'ignore_hosts': []}

View File

@@ -10,11 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import six
from nova import test
from nova.tests import fixtures as nova_fixtures
from nova.tests.functional.api import client as api_client
from nova.tests.functional import fixtures as func_fixtures
from nova.tests.functional import integrated_helpers
from nova.tests.unit.image import fake as fake_image
@@ -73,10 +70,11 @@ class VolumeBackedResizeDiskDown(test.TestCase,
self._wait_for_state_change(self.api, server, 'ACTIVE')
# Now try to resize the server with the flavor that has smaller disk.
# This should be allowed since the server is volume-backed and the
# disk size in the flavor shouldn't matter.
data = {'resize': {'flavorRef': flavor1['id']}}
# FIXME(mriedem): This will raise FlavorDiskSmallerThanMinDisk as a 500
# error until bug 1825020 is fixed.
ex = self.assertRaises(api_client.OpenStackApiException,
self.api.post_server_action, server['id'], data)
self.assertEqual(500, ex.response.status_code)
self.assertIn('FlavorDiskSmallerThanMinDisk', six.text_type(ex))
self.api.post_server_action(server['id'], data)
self._wait_for_state_change(self.api, server, 'VERIFY_RESIZE')
# Now confirm the resize just to complete the operation.
self.api.post_server_action(server['id'], {'confirmResize': None})
self._wait_for_state_change(self.api, server, 'ACTIVE')

View File

@@ -1933,6 +1933,8 @@ class _ComputeAPIUnitTestMixIn(object):
self.context, fake_inst['uuid'], 'finished')
mock_inst_save.assert_called_once_with(expected_task_state=[None])
@mock.patch('nova.compute.utils.is_volume_backed_instance',
return_value=False)
@mock.patch('nova.compute.api.API._validate_flavor_image_nostatus')
@mock.patch('nova.objects.Migration')
@mock.patch.object(compute_api.API, '_record_action_start')
@@ -1946,7 +1948,7 @@ class _ComputeAPIUnitTestMixIn(object):
def _test_resize(self, mock_get_all_by_host,
mock_get_by_instance_uuid, mock_get_flavor, mock_upsize,
mock_inst_save, mock_count, mock_limit, mock_record,
mock_migration, mock_validate,
mock_migration, mock_validate, mock_is_vol_backed,
flavor_id_passed=True,
same_host=False, allow_same_host=False,
project_id=None,