diff --git a/nova/api/openstack/compute/availability_zone.py b/nova/api/openstack/compute/availability_zone.py index b26f546dce..e31f40aa42 100644 --- a/nova/api/openstack/compute/availability_zone.py +++ b/nova/api/openstack/compute/availability_zone.py @@ -142,14 +142,15 @@ class AvailabilityZone(extensions.V21APIExtensionBase): """ return [] - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - # NOTE(alex_xu): For v2.1 compat mode, we strip the spaces when create - # availability_zone. But we don't strip at here for backward-compatible - # with some users already created availability_zone with - # leading/trailing spaces with legacy v2 API. - create_kwargs['availability_zone'] = server_dict.get(ATTRIBUTE_NAME) + +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + # NOTE(alex_xu): For v2.1 compat mode, we strip the spaces when create + # availability_zone. But we don't strip at here for backward-compatible + # with some users already created availability_zone with + # leading/trailing spaces with legacy v2 API. + create_kwargs['availability_zone'] = server_dict.get(ATTRIBUTE_NAME) def get_server_create_schema(version): diff --git a/nova/api/openstack/compute/block_device_mapping.py b/nova/api/openstack/compute/block_device_mapping.py index 6e872132c8..4487bd7bb9 100644 --- a/nova/api/openstack/compute/block_device_mapping.py +++ b/nova/api/openstack/compute/block_device_mapping.py @@ -43,36 +43,37 @@ class BlockDeviceMapping(extensions.V21APIExtensionBase): def get_controller_extensions(self): return [] - # use nova.api.extensions.server.extensions entry point to modify - # server create kwargs - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - # Have to check whether --image is given, see bug 1433609 - image_href = server_dict.get('imageRef') - image_uuid_specified = image_href is not None +# use nova.api.extensions.server.extensions entry point to modify +# server create kwargs +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): - bdm = server_dict.get(ATTRIBUTE_NAME, []) - legacy_bdm = server_dict.get(LEGACY_ATTRIBUTE_NAME, []) + # Have to check whether --image is given, see bug 1433609 + image_href = server_dict.get('imageRef') + image_uuid_specified = image_href is not None - if bdm and legacy_bdm: - expl = _('Using different block_device_mapping syntaxes ' - 'is not allowed in the same request.') - raise exc.HTTPBadRequest(explanation=expl) + bdm = server_dict.get(ATTRIBUTE_NAME, []) + legacy_bdm = server_dict.get(LEGACY_ATTRIBUTE_NAME, []) - try: - block_device_mapping = [ - block_device.BlockDeviceDict.from_api(bdm_dict, - image_uuid_specified) - for bdm_dict in bdm] - except exception.InvalidBDMFormat as e: - raise exc.HTTPBadRequest(explanation=e.format_message()) + if bdm and legacy_bdm: + expl = _('Using different block_device_mapping syntaxes ' + 'is not allowed in the same request.') + raise exc.HTTPBadRequest(explanation=expl) - if block_device_mapping: - create_kwargs['block_device_mapping'] = block_device_mapping - # Unset the legacy_bdm flag if we got a block device mapping. - create_kwargs['legacy_bdm'] = False + try: + block_device_mapping = [ + block_device.BlockDeviceDict.from_api(bdm_dict, + image_uuid_specified) + for bdm_dict in bdm] + except exception.InvalidBDMFormat as e: + raise exc.HTTPBadRequest(explanation=e.format_message()) + + if block_device_mapping: + create_kwargs['block_device_mapping'] = block_device_mapping + # Unset the legacy_bdm flag if we got a block device mapping. + create_kwargs['legacy_bdm'] = False def get_server_create_schema(version): diff --git a/nova/api/openstack/compute/block_device_mapping_v1.py b/nova/api/openstack/compute/block_device_mapping_v1.py index 2598e302dc..fd4c51525a 100644 --- a/nova/api/openstack/compute/block_device_mapping_v1.py +++ b/nova/api/openstack/compute/block_device_mapping_v1.py @@ -41,28 +41,29 @@ class BlockDeviceMappingV1(extensions.V21APIExtensionBase): def get_controller_extensions(self): return [] - # use nova.api.extensions.server.extensions entry point to modify - # server create kwargs - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - block_device_mapping = server_dict.get(ATTRIBUTE_NAME, []) - block_device_mapping_v2 = server_dict.get(ATTRIBUTE_NAME_V2, []) - if block_device_mapping and block_device_mapping_v2: - expl = _('Using different block_device_mapping syntaxes ' - 'is not allowed in the same request.') - raise exc.HTTPBadRequest(explanation=expl) +# use nova.api.extensions.server.extensions entry point to modify +# server create kwargs +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + block_device_mapping = server_dict.get(ATTRIBUTE_NAME, []) + block_device_mapping_v2 = server_dict.get(ATTRIBUTE_NAME_V2, []) - for bdm in block_device_mapping: - if 'delete_on_termination' in bdm: - bdm['delete_on_termination'] = strutils.bool_from_string( - bdm['delete_on_termination']) + if block_device_mapping and block_device_mapping_v2: + expl = _('Using different block_device_mapping syntaxes ' + 'is not allowed in the same request.') + raise exc.HTTPBadRequest(explanation=expl) - if block_device_mapping: - create_kwargs['block_device_mapping'] = block_device_mapping - # Sets the legacy_bdm flag if we got a legacy block device mapping. - create_kwargs['legacy_bdm'] = True + for bdm in block_device_mapping: + if 'delete_on_termination' in bdm: + bdm['delete_on_termination'] = strutils.bool_from_string( + bdm['delete_on_termination']) + + if block_device_mapping: + create_kwargs['block_device_mapping'] = block_device_mapping + # Sets the legacy_bdm flag if we got a legacy block device mapping. + create_kwargs['legacy_bdm'] = True def get_server_create_schema(version): diff --git a/nova/api/openstack/compute/config_drive.py b/nova/api/openstack/compute/config_drive.py index 751e7d1d5e..d23942305f 100644 --- a/nova/api/openstack/compute/config_drive.py +++ b/nova/api/openstack/compute/config_drive.py @@ -69,10 +69,11 @@ class ConfigDrive(extensions.V21APIExtensionBase): def get_resources(self): return [] - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - create_kwargs['config_drive'] = server_dict.get(ATTRIBUTE_NAME) + +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + create_kwargs['config_drive'] = server_dict.get(ATTRIBUTE_NAME) def get_server_create_schema(version): diff --git a/nova/api/openstack/compute/keypairs.py b/nova/api/openstack/compute/keypairs.py index 11980df000..c9f6f733c2 100644 --- a/nova/api/openstack/compute/keypairs.py +++ b/nova/api/openstack/compute/keypairs.py @@ -341,16 +341,17 @@ class Keypairs(extensions.V21APIExtensionBase): extension = extensions.ControllerExtension(self, 'servers', controller) return [extension] - # use nova.api.extensions.server.extensions entry point to modify - # server create kwargs - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - # NOTE(alex_xu): The v2.1 API compat mode, we strip the spaces for - # keypair create. But we didn't strip spaces at here for - # backward-compatible some users already created keypair and name with - # leading/trailing spaces by legacy v2 API. - create_kwargs['key_name'] = server_dict.get('key_name') + +# use nova.api.extensions.server.extensions entry point to modify +# server create kwargs +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + # NOTE(alex_xu): The v2.1 API compat mode, we strip the spaces for + # keypair create. But we didn't strip spaces at here for + # backward-compatible some users already created keypair and name with + # leading/trailing spaces by legacy v2 API. + create_kwargs['key_name'] = server_dict.get('key_name') def get_server_create_schema(version): diff --git a/nova/api/openstack/compute/multiple_create.py b/nova/api/openstack/compute/multiple_create.py index 33990e3ed1..f949b8208b 100644 --- a/nova/api/openstack/compute/multiple_create.py +++ b/nova/api/openstack/compute/multiple_create.py @@ -39,26 +39,27 @@ class MultipleCreate(extensions.V21APIExtensionBase): def get_controller_extensions(self): return [] - # use nova.api.extensions.server.extensions entry point to modify - # server create kwargs - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - # min_count and max_count are optional. If they exist, they may come - # in as strings. Verify that they are valid integers and > 0. - # Also, we want to default 'min_count' to 1, and default - # 'max_count' to be 'min_count'. - min_count = int(server_dict.get(MIN_ATTRIBUTE_NAME, 1)) - max_count = int(server_dict.get(MAX_ATTRIBUTE_NAME, min_count)) - return_id = server_dict.get(RRID_ATTRIBUTE_NAME, False) - if min_count > max_count: - msg = _('min_count must be <= max_count') - raise exc.HTTPBadRequest(explanation=msg) +# use nova.api.extensions.server.extensions entry point to modify +# server create kwargs +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + # min_count and max_count are optional. If they exist, they may come + # in as strings. Verify that they are valid integers and > 0. + # Also, we want to default 'min_count' to 1, and default + # 'max_count' to be 'min_count'. + min_count = int(server_dict.get(MIN_ATTRIBUTE_NAME, 1)) + max_count = int(server_dict.get(MAX_ATTRIBUTE_NAME, min_count)) + return_id = server_dict.get(RRID_ATTRIBUTE_NAME, False) - create_kwargs['min_count'] = min_count - create_kwargs['max_count'] = max_count - create_kwargs['return_reservation_id'] = return_id + if min_count > max_count: + msg = _('min_count must be <= max_count') + raise exc.HTTPBadRequest(explanation=msg) + + create_kwargs['min_count'] = min_count + create_kwargs['max_count'] = max_count + create_kwargs['return_reservation_id'] = return_id def get_server_create_schema(version): diff --git a/nova/api/openstack/compute/scheduler_hints.py b/nova/api/openstack/compute/scheduler_hints.py index 18ffe78507..5492f0e28b 100644 --- a/nova/api/openstack/compute/scheduler_hints.py +++ b/nova/api/openstack/compute/scheduler_hints.py @@ -31,18 +31,19 @@ class SchedulerHints(extensions.V21APIExtensionBase): def get_resources(self): return [] - # NOTE(gmann): Accepting request body in this function to fetch "scheduler - # hint". This is a workaround to allow OS_SCH-HNT at the top level - # of the body request, but that it will be changed in the future to be a - # subset of the servers dict. - def server_create(self, server_dict, create_kwargs, req_body): - scheduler_hints = {} - if 'os:scheduler_hints' in req_body: - scheduler_hints = req_body['os:scheduler_hints'] - elif 'OS-SCH-HNT:scheduler_hints' in req_body: - scheduler_hints = req_body['OS-SCH-HNT:scheduler_hints'] - create_kwargs['scheduler_hints'] = scheduler_hints +# NOTE(gmann): Accepting request body in this function to fetch "scheduler +# hint". This is a workaround to allow OS_SCH-HNT at the top level +# of the body request, but that it will be changed in the future to be a +# subset of the servers dict. +def server_create(server_dict, create_kwargs, req_body): + scheduler_hints = {} + if 'os:scheduler_hints' in req_body: + scheduler_hints = req_body['os:scheduler_hints'] + elif 'OS-SCH-HNT:scheduler_hints' in req_body: + scheduler_hints = req_body['OS-SCH-HNT:scheduler_hints'] + + create_kwargs['scheduler_hints'] = scheduler_hints def get_server_create_schema(version): diff --git a/nova/api/openstack/compute/security_groups.py b/nova/api/openstack/compute/security_groups.py index 5272d350b6..3ed786139f 100644 --- a/nova/api/openstack/compute/security_groups.py +++ b/nova/api/openstack/compute/security_groups.py @@ -521,15 +521,16 @@ class SecurityGroups(extensions.V21APIExtensionBase): controller=SecurityGroupRulesController()) return [secgrp_ext, server_secgrp_ext, secgrp_rules_ext] - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - security_groups = server_dict.get(ATTRIBUTE_NAME) - if security_groups is not None: - create_kwargs['security_groups'] = [ - sg['name'] for sg in security_groups if sg.get('name')] - create_kwargs['security_groups'] = list( - set(create_kwargs['security_groups'])) + +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + security_groups = server_dict.get(ATTRIBUTE_NAME) + if security_groups is not None: + create_kwargs['security_groups'] = [ + sg['name'] for sg in security_groups if sg.get('name')] + create_kwargs['security_groups'] = list( + set(create_kwargs['security_groups'])) def get_server_create_schema(version): diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index e5902dbf85..b5c1deccf5 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -22,7 +22,6 @@ from oslo_utils import strutils from oslo_utils import timeutils from oslo_utils import uuidutils import six -import stevedore import webob from webob import exc @@ -67,8 +66,6 @@ LOG = logging.getLogger(__name__) class ServersController(wsgi.Controller): """The Server API base controller class for the OpenStack API.""" - EXTENSION_CREATE_NAMESPACE = 'nova.api.v21.extensions.server.create' - _view_builder_class = views_servers.ViewBuilder schema_server_create = schema_servers.base_create @@ -102,6 +99,20 @@ class ServersController(wsgi.Controller): user_data.get_server_create_schema, ] + # NOTE(alex_xu): Please do not add more items into this list. This list + # should be removed in the future. + server_create_func_list = [ + availability_zone.server_create, + block_device_mapping.server_create, + block_device_mapping_v1.server_create, + config_drive.server_create, + keypairs.server_create, + multiple_create.server_create, + scheduler_hints.server_create, + security_groups.server_create, + user_data.server_create, + ] + @staticmethod def _add_location(robj): # Just in case... @@ -116,47 +127,13 @@ class ServersController(wsgi.Controller): return robj def __init__(self, **kwargs): - def _check_load_extension(required_function): + # TODO(alex_xu): Remove this line when 'extension_info' won't be passed + # in when creating controller. + kwargs.pop('extension_info', None) - def check_load_extension(ext): - if isinstance(ext.obj, extensions.V21APIExtensionBase): - # Filter out for the existence of the required - # function here rather than on every request. We - # don't have a new abstract base class to reduce - # duplication in the extensions as they may want - # to implement multiple server (and other) entry - # points if hasattr(ext.obj, 'server_create'): - if hasattr(ext.obj, required_function): - LOG.debug('extension %(ext_alias)s detected by ' - 'servers extension for function %(func)s', - {'ext_alias': ext.obj.alias, - 'func': required_function}) - return True - else: - LOG.debug( - 'extension %(ext_alias)s is missing %(func)s', - {'ext_alias': ext.obj.alias, - 'func': required_function}) - return False - else: - return False - return check_load_extension - - self.extension_info = kwargs.pop('extension_info') super(ServersController, self).__init__(**kwargs) self.compute_api = compute.API() - # Look for implementation of extension point of server creation - self.create_extension_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_CREATE_NAMESPACE, - check_func=_check_load_extension('server_create'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if not list(self.create_extension_manager): - LOG.debug("Did not find any server create extensions") - # TODO(alex_xu): The final goal is that merging all of # extended json-schema into server main json-schema. self._create_schema(self.schema_server_create_v242, '2.42') @@ -525,16 +502,10 @@ class ServersController(wsgi.Controller): # Arguments to be passed to instance create function create_kwargs = {} - # Query extensions which want to manipulate the keyword - # arguments. - # NOTE(cyeoh): This is the hook that extensions use - # to replace the extension specific code below. - # When the extensions are ported this will also result - # in some convenience function from this class being - # moved to the extension - if list(self.create_extension_manager): - self.create_extension_manager.map(self._create_extension_point, - server_dict, create_kwargs, body) + # TODO(alex_xu): This is for back-compatible with stevedore + # extension interface. But the final goal is that merging + # all of extended code into ServersController. + self._create_by_func_list(server_dict, create_kwargs, body) availability_zone = create_kwargs.pop("availability_zone", None) @@ -709,12 +680,10 @@ class ServersController(wsgi.Controller): # NOTE(gmann): Parameter 'req_body' is placed to handle scheduler_hint # extension for V2.1. No other extension supposed to use this as # it will be removed soon. - def _create_extension_point(self, ext, server_dict, - create_kwargs, req_body): - handler = ext.obj - LOG.debug("Running _create_extension_point for %s", ext.obj) - - handler.server_create(server_dict, create_kwargs, req_body) + def _create_by_func_list(self, server_dict, + create_kwargs, req_body): + for func in self.server_create_func_list: + func(server_dict, create_kwargs, req_body) def _rebuild_extension_point(self, ext, rebuild_dict, rebuild_kwargs): handler = ext.obj diff --git a/nova/api/openstack/compute/user_data.py b/nova/api/openstack/compute/user_data.py index a4847f2a86..f957c27c2e 100644 --- a/nova/api/openstack/compute/user_data.py +++ b/nova/api/openstack/compute/user_data.py @@ -33,10 +33,11 @@ class UserData(extensions.V21APIExtensionBase): def get_resources(self): return [] - # NOTE(gmann): This function is not supposed to use 'body_deprecated_param' - # parameter as this is placed to handle scheduler_hint extension for V2.1. - def server_create(self, server_dict, create_kwargs, body_deprecated_param): - create_kwargs['user_data'] = server_dict.get(ATTRIBUTE_NAME) + +# NOTE(gmann): This function is not supposed to use 'body_deprecated_param' +# parameter as this is placed to handle scheduler_hint extension for V2.1. +def server_create(server_dict, create_kwargs, body_deprecated_param): + create_kwargs['user_data'] = server_dict.get(ATTRIBUTE_NAME) def get_server_create_schema(version): diff --git a/nova/tests/unit/api/openstack/compute/test_serversV21.py b/nova/tests/unit/api/openstack/compute/test_serversV21.py index bfb5a72a38..58ab397c03 100644 --- a/nova/tests/unit/api/openstack/compute/test_serversV21.py +++ b/nova/tests/unit/api/openstack/compute/test_serversV21.py @@ -38,7 +38,6 @@ from nova.api.openstack import common from nova.api.openstack import compute from nova.api.openstack.compute import extension_info from nova.api.openstack.compute import ips -from nova.api.openstack.compute import keypairs from nova.api.openstack.compute import servers from nova.api.openstack.compute import views from nova.api.openstack import extensions @@ -2967,12 +2966,12 @@ class ServersControllerCreateTest(test.TestCase): self.assertEqual(FAKE_UUID, server['id']) def test_create_instance_extension_create_exception(self): - def fake_keypair_server_create(self, server_dict, - create_kwargs): + def fake_keypair_server_create(server_dict, + create_kwargs, body_deprecated_param): raise KeyError - self.stubs.Set(keypairs.Keypairs, 'server_create', - fake_keypair_server_create) + self.controller.server_create_func_list.append( + fake_keypair_server_create) image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/123/flavors/3' body = { @@ -2993,6 +2992,8 @@ class ServersControllerCreateTest(test.TestCase): req.headers["content-type"] = "application/json" self.assertRaises(webob.exc.HTTPInternalServerError, self.controller.create, req, body=body) + self.controller.server_create_func_list.remove( + fake_keypair_server_create) def test_create_instance_pass_disabled(self): self.flags(enable_instance_password=False, group='api') diff --git a/setup.cfg b/setup.cfg index 280606f897..a6b20a4b4f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -155,17 +155,6 @@ nova.api.v21.extensions = virtual_interfaces = nova.api.openstack.compute.virtual_interfaces:VirtualInterfaces volumes = nova.api.openstack.compute.volumes:Volumes -nova.api.v21.extensions.server.create = - availability_zone = nova.api.openstack.compute.availability_zone:AvailabilityZone - block_device_mapping = nova.api.openstack.compute.block_device_mapping:BlockDeviceMapping - block_device_mapping_v1 = nova.api.openstack.compute.block_device_mapping_v1:BlockDeviceMappingV1 - config_drive = nova.api.openstack.compute.config_drive:ConfigDrive - keypairs_create = nova.api.openstack.compute.keypairs:Keypairs - multiple_create = nova.api.openstack.compute.multiple_create:MultipleCreate - scheduler_hints = nova.api.openstack.compute.scheduler_hints:SchedulerHints - security_groups = nova.api.openstack.compute.security_groups:SecurityGroups - user_data = nova.api.openstack.compute.user_data:UserData - nova.api.v21.test_extensions = basic = nova.tests.unit.api.openstack.compute.basic:Basic microversions = nova.tests.unit.api.openstack.compute.microversions:Microversions