Skip to content

Commit 1f74441

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Fix volume-backed resize with a smaller disk flavor"
2 parents 6244366 + 8aae3e3 commit 1f74441

File tree

3 files changed

+61
-21
lines changed

3 files changed

+61
-21
lines changed

nova/compute/api.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,7 @@ def _validate_flavor_image_nostatus(context, image, instance_type,
572572
with each other.
573573
574574
:param context: A context.RequestContext
575-
:param image: a dict representation of the image including properties,
576-
enforces the image status is active.
575+
:param image: a dict representation of the image including properties
577576
:param instance_type: Flavor object
578577
:param root_bdm: BlockDeviceMapping for root disk. Will be None for
579578
the resize case.
@@ -666,6 +665,30 @@ def _validate_flavor_image_nostatus(context, image, instance_type,
666665
servers_policies.ZERO_DISK_FLAVOR, fatal=False):
667666
raise exception.BootFromVolumeRequiredForZeroDiskFlavor()
668667

668+
API._validate_flavor_image_numa_pci(
669+
image, instance_type, validate_numa=validate_numa,
670+
validate_pci=validate_pci)
671+
672+
@staticmethod
673+
def _validate_flavor_image_numa_pci(image, instance_type,
674+
validate_numa=True,
675+
validate_pci=False):
676+
"""Validate the flavor and image NUMA/PCI values.
677+
678+
This is called from the API service to ensure that the flavor
679+
extra-specs and image properties are self-consistent and compatible
680+
with each other.
681+
682+
:param image: a dict representation of the image including properties
683+
:param instance_type: Flavor object
684+
:param validate_numa: Flag to indicate whether or not to validate
685+
the NUMA-related metadata.
686+
:param validate_pci: Flag to indicate whether or not to validate
687+
the PCI-related metadata.
688+
:raises: Many different possible exceptions. See
689+
api.openstack.compute.servers.INVALID_FLAVOR_IMAGE_EXCEPTIONS
690+
for the full list.
691+
"""
669692
image_meta = _get_image_meta_obj(image)
670693

671694
# Only validate values of flavor/image so the return results of
@@ -3572,19 +3595,23 @@ def resize(self, context, instance, flavor_id=None, clean_shutdown=True,
35723595
current_instance_type = instance.get_flavor()
35733596

35743597
# If flavor_id is not provided, only migrate the instance.
3598+
volume_backed = None
35753599
if not flavor_id:
35763600
LOG.debug("flavor_id is None. Assuming migration.",
35773601
instance=instance)
35783602
new_instance_type = current_instance_type
35793603
else:
35803604
new_instance_type = flavors.get_flavor_by_flavor_id(
35813605
flavor_id, read_deleted="no")
3606+
# Check to see if we're resizing to a zero-disk flavor which is
3607+
# only supported with volume-backed servers.
35823608
if (new_instance_type.get('root_gb') == 0 and
3583-
current_instance_type.get('root_gb') != 0 and
3584-
not compute_utils.is_volume_backed_instance(context,
3585-
instance)):
3586-
reason = _('Resize to zero disk flavor is not allowed.')
3587-
raise exception.CannotResizeDisk(reason=reason)
3609+
current_instance_type.get('root_gb') != 0):
3610+
volume_backed = compute_utils.is_volume_backed_instance(
3611+
context, instance)
3612+
if not volume_backed:
3613+
reason = _('Resize to zero disk flavor is not allowed.')
3614+
raise exception.CannotResizeDisk(reason=reason)
35883615

35893616
if not new_instance_type:
35903617
raise exception.FlavorNotFound(flavor_id=flavor_id)
@@ -3617,10 +3644,23 @@ def resize(self, context, instance, flavor_id=None, clean_shutdown=True,
36173644
if not same_instance_type:
36183645
image = utils.get_image_from_system_metadata(
36193646
instance.system_metadata)
3620-
# Can skip root_bdm check since it will not change during resize.
3621-
self._validate_flavor_image_nostatus(
3622-
context, image, new_instance_type, root_bdm=None,
3623-
validate_pci=True)
3647+
# Figure out if the instance is volume-backed but only if we didn't
3648+
# already figure that out above (avoid the extra db hit).
3649+
if volume_backed is None:
3650+
volume_backed = compute_utils.is_volume_backed_instance(
3651+
context, instance)
3652+
# If the server is volume-backed, we still want to validate numa
3653+
# and pci information in the new flavor, but we don't call
3654+
# _validate_flavor_image_nostatus because how it handles checking
3655+
# disk size validation was not intended for a volume-backed
3656+
# resize case.
3657+
if volume_backed:
3658+
self._validate_flavor_image_numa_pci(
3659+
image, new_instance_type, validate_pci=True)
3660+
else:
3661+
self._validate_flavor_image_nostatus(
3662+
context, image, new_instance_type, root_bdm=None,
3663+
validate_pci=True)
36243664

36253665
filter_properties = {'ignore_hosts': []}
36263666

nova/tests/functional/regressions/test_bug_1825020.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@
1010
# License for the specific language governing permissions and limitations
1111
# under the License.
1212

13-
import six
14-
1513
from nova import test
1614
from nova.tests import fixtures as nova_fixtures
17-
from nova.tests.functional.api import client as api_client
1815
from nova.tests.functional import fixtures as func_fixtures
1916
from nova.tests.functional import integrated_helpers
2017
from nova.tests.unit.image import fake as fake_image
@@ -73,10 +70,11 @@ def test_volume_backed_resize_disk_down(self):
7370
self._wait_for_state_change(self.api, server, 'ACTIVE')
7471

7572
# Now try to resize the server with the flavor that has smaller disk.
73+
# This should be allowed since the server is volume-backed and the
74+
# disk size in the flavor shouldn't matter.
7675
data = {'resize': {'flavorRef': flavor1['id']}}
77-
# FIXME(mriedem): This will raise FlavorDiskSmallerThanMinDisk as a 500
78-
# error until bug 1825020 is fixed.
79-
ex = self.assertRaises(api_client.OpenStackApiException,
80-
self.api.post_server_action, server['id'], data)
81-
self.assertEqual(500, ex.response.status_code)
82-
self.assertIn('FlavorDiskSmallerThanMinDisk', six.text_type(ex))
76+
self.api.post_server_action(server['id'], data)
77+
self._wait_for_state_change(self.api, server, 'VERIFY_RESIZE')
78+
# Now confirm the resize just to complete the operation.
79+
self.api.post_server_action(server['id'], {'confirmResize': None})
80+
self._wait_for_state_change(self.api, server, 'ACTIVE')

nova/tests/unit/compute/test_compute_api.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,8 @@ def test_revert_resize_concurrent_fail(self, mock_reqspec, mock_elevated,
19331933
self.context, fake_inst['uuid'], 'finished')
19341934
mock_inst_save.assert_called_once_with(expected_task_state=[None])
19351935

1936+
@mock.patch('nova.compute.utils.is_volume_backed_instance',
1937+
return_value=False)
19361938
@mock.patch('nova.compute.api.API._validate_flavor_image_nostatus')
19371939
@mock.patch('nova.objects.Migration')
19381940
@mock.patch.object(compute_api.API, '_record_action_start')
@@ -1946,7 +1948,7 @@ def test_revert_resize_concurrent_fail(self, mock_reqspec, mock_elevated,
19461948
def _test_resize(self, mock_get_all_by_host,
19471949
mock_get_by_instance_uuid, mock_get_flavor, mock_upsize,
19481950
mock_inst_save, mock_count, mock_limit, mock_record,
1949-
mock_migration, mock_validate,
1951+
mock_migration, mock_validate, mock_is_vol_backed,
19501952
flavor_id_passed=True,
19511953
same_host=False, allow_same_host=False,
19521954
project_id=None,

0 commit comments

Comments
 (0)