Parse availability_zone in API

This moves the parsing of availability_zone, and as a consequence
force_host/force_node, into the API to prepare for building filter_properties
earlier.  This will be necessary in order to build and persist the request_spec
object before the instance has been saved to the db.

Change-Id: I55399a77fc8d474380f7c422dcef1e6d145a48c8
This commit is contained in:
Andrew Laski 2016-01-12 13:41:01 -05:00 committed by Dan Smith
parent b558d616c3
commit e6b7a9b461
5 changed files with 48 additions and 52 deletions

View File

@ -608,6 +608,8 @@ class Controller(wsgi.Controller):
scheduler_hints = {}
if self.ext_mgr.is_loaded('OS-SCH-HNT'):
scheduler_hints = server_dict.get('scheduler_hints', {})
parse_az = self.compute_api.parse_availability_zone
availability_zone, host, node = parse_az(context, availability_zone)
check_server_group_quota = self.ext_mgr.is_loaded(
'os-server-group-quotas')
@ -633,6 +635,7 @@ class Controller(wsgi.Controller):
security_group=sg_names,
user_data=user_data,
availability_zone=availability_zone,
forced_host=host, forced_node=node,
config_drive=config_drive,
block_device_mapping=block_device_mapping,
auto_disk_config=auto_disk_config,

View File

@ -536,7 +536,7 @@ class ServersController(wsgi.Controller):
self.create_extension_manager.map(self._create_extension_point,
server_dict, create_kwargs, body)
availability_zone = create_kwargs.get("availability_zone")
availability_zone = create_kwargs.pop("availability_zone", None)
target = {
'project_id': context.project_id,
@ -546,11 +546,10 @@ class ServersController(wsgi.Controller):
# TODO(Shao He, Feng) move this policy check to os-availabilty-zone
# extension after refactor it.
if availability_zone:
_dummy, host, node = self.compute_api._handle_availability_zone(
context, availability_zone)
if host or node:
authorize(context, {}, 'create:forced_host')
parse_az = self.compute_api.parse_availability_zone
availability_zone, host, node = parse_az(context, availability_zone)
if host or node:
authorize(context, {}, 'create:forced_host')
block_device_mapping = create_kwargs.get("block_device_mapping")
# TODO(Shao He, Feng) move this policy check to os-block-device-mapping
@ -597,6 +596,8 @@ class ServersController(wsgi.Controller):
image_uuid,
display_name=name,
display_description=name,
availability_zone=availability_zone,
forced_host=host, forced_node=node,
metadata=server_dict.get('metadata', {}),
admin_password=password,
requested_networks=requested_networks,

View File

@ -493,7 +493,7 @@ class API(base.Base):
return kernel_id, ramdisk_id
@staticmethod
def _handle_availability_zone(context, availability_zone):
def parse_availability_zone(context, availability_zone):
# NOTE(vish): We have a legacy hack to allow admins to specify hosts
# via az using az:host:node. It might be nice to expose an
# api to specify specific hosts to force onto, but for
@ -797,23 +797,14 @@ class API(base.Base):
kernel_id, ramdisk_id, display_name,
display_description, key_name,
key_data, security_groups,
availability_zone, forced_host,
user_data, metadata,
access_ip_v4, access_ip_v6,
availability_zone, user_data,
metadata, access_ip_v4, access_ip_v6,
requested_networks, config_drive,
auto_disk_config, reservation_id,
max_count):
"""Verify all the input parameters regardless of the provisioning
strategy being performed.
"""
if availability_zone:
available_zones = availability_zones.\
get_availability_zones(context.elevated(), True)
if forced_host is None and availability_zone not in \
available_zones:
msg = _('The requested availability zone is not available')
raise exception.InvalidRequest(msg)
if instance_type['disabled']:
raise exception.FlavorNotFound(flavor_id=instance_type['id'])
@ -1047,8 +1038,8 @@ class API(base.Base):
min_count, max_count,
display_name, display_description,
key_name, key_data, security_groups,
availability_zone, user_data, metadata,
injected_files, admin_password,
availability_zone, forced_host, forced_node, user_data,
metadata, injected_files, admin_password,
access_ip_v4, access_ip_v6,
requested_networks, config_drive,
block_device_mapping, auto_disk_config,
@ -1080,21 +1071,13 @@ class API(base.Base):
self._check_auto_disk_config(image=boot_meta,
auto_disk_config=auto_disk_config)
handle_az = self._handle_availability_zone
availability_zone, forced_host, forced_node = handle_az(context,
availability_zone)
if not self.skip_policy_check and (forced_host or forced_node):
check_policy(context, 'create:forced_host', {})
base_options, max_net_count = self._validate_and_build_base_options(
context,
instance_type, boot_meta, image_href, image_id, kernel_id,
ramdisk_id, display_name, display_description,
context, instance_type, boot_meta, image_href, image_id,
kernel_id, ramdisk_id, display_name, display_description,
key_name, key_data, security_groups, availability_zone,
forced_host, user_data, metadata, access_ip_v4,
access_ip_v6, requested_networks, config_drive,
auto_disk_config, reservation_id, max_count)
user_data, metadata, access_ip_v4, access_ip_v6,
requested_networks, config_drive, auto_disk_config,
reservation_id, max_count)
# max_net_count is the maximum number of instances requested by the
# user adjusted for any network quota constraints, including
@ -1427,7 +1410,8 @@ class API(base.Base):
return instance
def _check_create_policies(self, context, availability_zone,
requested_networks, block_device_mapping):
requested_networks, block_device_mapping, forced_host,
forced_node):
"""Check policies for create()."""
target = {'project_id': context.project_id,
'user_id': context.user_id,
@ -1441,6 +1425,9 @@ class API(base.Base):
if block_device_mapping:
check_policy(context, 'create:attach_volume', target)
if forced_host or forced_node:
check_policy(context, 'create:forced_host', {})
def _check_multiple_instances_neutron_ports(self, requested_networks):
"""Check whether multiple instances are created from port id(s)."""
for requested_net in requested_networks:
@ -1465,21 +1452,23 @@ class API(base.Base):
min_count=None, max_count=None,
display_name=None, display_description=None,
key_name=None, key_data=None, security_group=None,
availability_zone=None, user_data=None, metadata=None,
injected_files=None, admin_password=None,
block_device_mapping=None, access_ip_v4=None,
access_ip_v6=None, requested_networks=None, config_drive=None,
auto_disk_config=None, scheduler_hints=None, legacy_bdm=True,
shutdown_terminate=False, check_server_group_quota=False):
availability_zone=None, forced_host=None, forced_node=None,
user_data=None, metadata=None, injected_files=None,
admin_password=None, block_device_mapping=None,
access_ip_v4=None, access_ip_v6=None, requested_networks=None,
config_drive=None, auto_disk_config=None, scheduler_hints=None,
legacy_bdm=True, shutdown_terminate=False,
check_server_group_quota=False):
"""Provision instances, sending instance information to the
scheduler. The scheduler will determine where the instance(s)
go and will handle creating the DB entries.
Returns a tuple of (instances, reservation_id)
"""
# Check policies up front to fail before performing more expensive work
self._check_create_policies(context, availability_zone,
requested_networks, block_device_mapping)
requested_networks, block_device_mapping, forced_host,
forced_node)
if requested_networks and max_count > 1:
self._check_multiple_instances_and_specified_ip(requested_networks)
@ -1487,13 +1476,22 @@ class API(base.Base):
self._check_multiple_instances_neutron_ports(
requested_networks)
if availability_zone:
available_zones = availability_zones.\
get_availability_zones(context.elevated(), True)
if forced_host is None and availability_zone not in \
available_zones:
msg = _('The requested availability zone is not available')
raise exception.InvalidRequest(msg)
return self._create_instance(
context, instance_type,
image_href, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
key_name, key_data, security_group,
availability_zone, user_data, metadata,
availability_zone, forced_host, forced_node,
user_data, metadata,
injected_files, admin_password,
access_ip_v4, access_ip_v6,
requested_networks, config_drive,

View File

@ -245,9 +245,6 @@ class ServersControllerCreateTestV21(test.TestCase):
self.no_availability_zone_controller = servers_v21.ServersController(
extension_info=ext_info)
def _verify_no_availability_zone(self, **kwargs):
self.assertNotIn('availability_zone', kwargs)
def _test_create_extra(self, params, controller):
image_uuid = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
server = dict(name='server_test', imageRef=image_uuid, flavorRef=2)
@ -260,7 +257,7 @@ class ServersControllerCreateTestV21(test.TestCase):
old_create = compute_api.API.create
def create(*args, **kwargs):
self._verify_no_availability_zone(**kwargs)
self.assertIsNone(kwargs['availability_zone'])
return old_create(*args, **kwargs)
self.stubs.Set(compute_api.API, 'create', create)
@ -354,9 +351,6 @@ class ServersControllerCreateTestV2(ServersControllerCreateTestV21):
self.no_availability_zone_controller = servers_v2.Controller(
ext_mgr_no_az)
def _verify_no_availability_zone(self, **kwargs):
self.assertIsNone(kwargs['availability_zone'])
def test_create_instance_with_invalid_availability_zone_too_long(self):
# NOTE: v2.0 API does not check this bad request case.
# So we skip this test for v2.0 API.

View File

@ -10966,7 +10966,7 @@ class ComputePolicyTestCase(BaseTestCase):
self.assertRaises(exception.PolicyNotAuthorized,
self.compute_api.create, self.context, None, '1',
availability_zone='1:1')
availability_zone='1', forced_host='1')
def test_force_host_pass(self):
rules = {"compute:create": [],
@ -10976,7 +10976,7 @@ class ComputePolicyTestCase(BaseTestCase):
self.compute_api.create(self.context, None,
image_href=uuids.host_instance,
availability_zone='1:1')
availability_zone='1', forced_host='1')
class DisabledInstanceTypesTestCase(BaseTestCase):