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:
parent
b558d616c3
commit
e6b7a9b461
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue