Add cross-cell resize policy rule and enable in API

This adds the "compute:servers:resize:cross_cell" policy
rule which is now used in the API to determine if a resize
or cold migrate operation can be performed across cells.

The check in the API is based on:

- The policy check passing for the request.
- The minimum nova-compute service version being high
  enough across all cells to perform a cross-cell resize.

If either of those conditions fail a traditional same-cell
resize will be performed.

A docs stub is added here and will be fleshed out in an
upcoming patch.

Implements blueprint cross-cell-resize

Change-Id: Ie8a0f79a3b16e02b7a34a1b81f547013a3d88996
This commit is contained in:
Matt Riedemann 2019-02-20 16:13:05 -05:00
parent 7e15762c0a
commit 6ebee92445
10 changed files with 141 additions and 27 deletions

View File

@ -0,0 +1,26 @@
=================
Cross-cell resize
=================
Train spec: https://specs.openstack.org/openstack/nova-specs/specs/train/approved/cross-cell-resize.html
.. todo:: Flesh this out to describe what cross-cell resize is, how it is
triggered (policy), related configuration (long_rpc_timeout) including
the CrossCellWeigher, limitations and known issues, recovering from failures
during a cross-cell resize, maybe a flow chart for the overall process in
the code, minimum upgrade requirements and supported drivers (libvirt-only
at this time).
Limitations
~~~~~~~~~~~
These are known to not yet be supported in the code:
* Instances with ports attached that have bandwidth-aware resource provider
allocations.
* Reschedules
These are likely not to work since they have not been validated by testing:
* Instances with PCI devices attached.
* Instances with a NUMA topology.

View File

@ -21,6 +21,7 @@ A list of config options based on different topics can be found below:
/admin/configuration/api
/admin/configuration/resize
/admin/configuration/cross-cell-resize
/admin/configuration/fibre-channel
/admin/configuration/iscsi-offload
/admin/configuration/hypervisors

View File

@ -6,6 +6,8 @@ Resize (or Server resize) is the ability to change the flavor of a server, thus
allowing it to upscale or downscale according to user needs. For this feature
to work properly, you might need to configure some underlying virt layers.
For cross-cell resize, refer to :doc:`/admin/configuration/cross-cell-resize`.
Virt drivers
------------

View File

@ -105,6 +105,7 @@ AGGREGATE_ACTION_DELETE = 'Delete'
AGGREGATE_ACTION_ADD = 'Add'
MIN_COMPUTE_SYNC_COMPUTE_STATUS_DISABLED = 38
MIN_COMPUTE_CROSS_CELL_RESIZE = 47
# FIXME(danms): Keep a global cache of the cells we find the
# first time we look. This needs to be refreshed on a timer or
@ -3724,20 +3725,30 @@ class API(base.Base):
:param instance: Instance object being resized
:returns: True if cross-cell resize is allowed, False otherwise
"""
# TODO(mriedem): Uncomment this when confirm/revert are done. For now
# this method can just be used to internally enable the feature for
# functional testing.
# TODO(mriedem): If true, we should probably check if all of the
# computes are running a high enough service version and if not, do
# what? Raise an exception or just log something and return False?
# return context.can(
# server_policies.CROSS_CELL_RESIZE,
# target={'user_id': instance.user_id,
# 'project_id': instance.project_id},
# fatal=False)
return False
# First check to see if the requesting project/user is allowed by
# policy to perform cross-cell resize.
allowed = context.can(
servers_policies.CROSS_CELL_RESIZE,
target={'user_id': instance.user_id,
'project_id': instance.project_id},
fatal=False)
# If the user is allowed by policy, check to make sure the deployment
# is upgraded to the point of supporting cross-cell resize on all
# compute services.
if allowed:
# TODO(mriedem): We can remove this minimum compute version check
# in the 21.0.0 "U" release.
min_compute_version = (
objects.service.get_minimum_version_all_cells(
context, ['nova-compute']))
if min_compute_version < MIN_COMPUTE_CROSS_CELL_RESIZE:
LOG.debug('Request is allowed by policy to perform cross-cell '
'resize but the minimum nova-compute service '
'version in the deployment %s is less than %s so '
'cross-cell resize is not allowed at this time.',
min_compute_version, MIN_COMPUTE_CROSS_CELL_RESIZE)
allowed = False
return allowed
@staticmethod
def _validate_host_for_cold_migrate(

View File

@ -12,9 +12,10 @@
from oslo_policy import policy
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
RULE_ADMIN_API = 'rule:admin_api'
RULE_ANY = '@'
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner' # Admins or owners of the resource
RULE_ADMIN_API = 'rule:admin_api' # Allow only users with the admin role
RULE_ANY = '@' # Any user is allowed to perform the action.
RULE_NOBODY = '!' # No users are allowed to perform the action.
# TODO(gmann): # Special string ``system_scope:all`` is added for system
# scoped policies for backwards compatibility where ``nova.conf [oslo_policy]

View File

@ -21,6 +21,7 @@ SERVERS = 'os_compute_api:servers:%s'
NETWORK_ATTACH_EXTERNAL = 'network:attach_external_network'
ZERO_DISK_FLAVOR = SERVERS % 'create:zero_disk_flavor'
REQUESTED_DESTINATION = 'compute:servers:create:requested_destination'
CROSS_CELL_RESIZE = 'compute:servers:resize:cross_cell'
rules = [
policy.DocumentedRuleDefault(
@ -319,6 +320,19 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
'path': '/servers/{server_id}/action (resize)'
}
]),
policy.DocumentedRuleDefault(
CROSS_CELL_RESIZE,
base.RULE_NOBODY,
"Resize a server across cells. By default, this is disabled for all "
"users and recommended to be tested in a deployment for admin users "
"before opening it up to non-admin users. Resizing within a cell is "
"the default preferred behavior even if this is enabled. ",
[
{
'method': 'POST',
'path': '/servers/{server_id}/action (resize)'
}
]),
policy.DocumentedRuleDefault(
SERVERS % 'rebuild',
RULE_AOO,

View File

@ -20,6 +20,8 @@ from nova import context as nova_context
from nova.db import api as db_api
from nova import exception
from nova import objects
from nova.policies import base as base_policies
from nova.policies import servers as servers_policies
from nova.scheduler import utils as scheduler_utils
from nova.scheduler import weights
from nova.tests import fixtures as nova_fixtures
@ -90,15 +92,10 @@ class TestMultiCellMigrate(integrated_helpers.ProviderUsageBaseTestCase):
# Enable cross-cell resize policy since it defaults to not allow
# anyone to perform that type of operation. For these tests we'll
# just allow admins to perform cross-cell resize.
# TODO(mriedem): Uncomment this when the policy rule is added and
# used in the compute API _allow_cross_cell_resize method. For now
# we just stub that method to return True.
# self.policy_fixture.set_rules({
# servers_policies.CROSS_CELL_RESIZE:
# base_policies.RULE_ADMIN_API},
# overwrite=False)
self.stub_out('nova.compute.api.API._allow_cross_cell_resize',
lambda *a, **kw: True)
self.policy.set_rules({
servers_policies.CROSS_CELL_RESIZE:
base_policies.RULE_ADMIN_API},
overwrite=False)
def assertFlavorMatchesAllocation(self, flavor, allocation,
volume_backed=False):

View File

@ -6987,6 +6987,49 @@ class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
mock_conductor_confirm.assert_called_once_with(
self.context, instance, migration)
@mock.patch('nova.objects.service.get_minimum_version_all_cells')
def test_allow_cross_cell_resize_default_false(self, mock_get_min_ver):
"""Based on the default policy this asserts nobody is allowed to
perform cross-cell resize.
"""
instance = objects.Instance(
project_id='fake-project', user_id='fake-user')
self.assertFalse(self.compute_api._allow_cross_cell_resize(
self.context, instance))
# We did not need to check the minimum nova-compute version since the
# policy check failed.
mock_get_min_ver.assert_not_called()
@mock.patch('nova.objects.service.get_minimum_version_all_cells',
return_value=compute_api.MIN_COMPUTE_CROSS_CELL_RESIZE - 1)
def test_allow_cross_cell_resize_false_old_version(self, mock_get_min_ver):
"""Policy allows cross-cell resize but minimum nova-compute service
version is not new enough.
"""
instance = objects.Instance(
project_id='fake-project', user_id='fake-user')
with mock.patch.object(self.context, 'can', return_value=True) as can:
self.assertFalse(self.compute_api._allow_cross_cell_resize(
self.context, instance))
can.assert_called_once()
mock_get_min_ver.assert_called_once_with(
self.context, ['nova-compute'])
@mock.patch('nova.objects.service.get_minimum_version_all_cells',
return_value=compute_api.MIN_COMPUTE_CROSS_CELL_RESIZE)
def test_allow_cross_cell_resize_true(self, mock_get_min_ver):
"""Policy allows cross-cell resize and minimum nova-compute service
version is new enough.
"""
instance = objects.Instance(
project_id='fake-project', user_id='fake-user')
with mock.patch.object(self.context, 'can', return_value=True) as can:
self.assertTrue(self.compute_api._allow_cross_cell_resize(
self.context, instance))
can.assert_called_once()
mock_get_min_ver.assert_called_once_with(
self.context, ['nova-compute'])
class DiffDictTestCase(test.NoDBTestCase):
"""Unit tests for _diff_dict()."""

View File

@ -24,6 +24,7 @@ import requests_mock
from nova import context
from nova import exception
from nova.policies import servers as servers_policy
from nova import policy
from nova import test
from nova.tests.unit import fake_policy
@ -460,6 +461,10 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
"os_compute_api:os-services:list",
)
self.allow_nobody_rules = (
servers_policy.CROSS_CELL_RESIZE,
)
def test_all_rules_in_sample_file(self):
special_rules = ["context_is_admin", "admin_or_owner", "default"]
for (name, rule) in self.fake_policy.items():
@ -485,6 +490,12 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
for rule in self.allow_all_rules:
policy.authorize(self.non_admin_context, rule, self.target)
def test_allow_nobody_rules(self):
"""No one can perform these operations, not even admin."""
for rule in self.allow_nobody_rules:
self.assertRaises(exception.PolicyNotAuthorized, policy.authorize,
self.admin_context, rule, self.target)
def test_rule_missing(self):
rules = policy.get_rules()
# eliqiao os_compute_api:os-quota-class-sets:show requires
@ -497,5 +508,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
'system_admin_or_owner', 'system_or_project_reader')
result = set(rules.keys()) - set(self.admin_only_rules +
self.admin_or_owner_rules +
self.allow_all_rules + self.system_reader_rules + special_rules)
self.allow_all_rules + self.system_reader_rules +
self.allow_nobody_rules + special_rules)
self.assertEqual(set([]), result)

View File

@ -0,0 +1,7 @@
---
features:
- |
Cross-cell resize is now supported but is disabled by default for all
users. Refer to the `administrator documentation`__ for details.
.. __: https://docs.openstack.org/nova/latest/admin/configuration/cross-cell-resize.html