diff --git a/nova/api/openstack/compute/extension_info.py b/nova/api/openstack/compute/extension_info.py index a040abb38b55..58e76dcf11c0 100644 --- a/nova/api/openstack/compute/extension_info.py +++ b/nova/api/openstack/compute/extension_info.py @@ -115,7 +115,10 @@ hardcoded_extensions = [ {'name': 'PreserveEphemeralOnRebuild', 'description': ('Allow preservation of the ' 'ephemeral partition on rebuild.'), - 'alias': 'os-preserve-ephemeral-rebuild'} + 'alias': 'os-preserve-ephemeral-rebuild'}, + {'name': 'Personality', + 'description': 'Personality support.', + 'alias': 'os-personality'}, ] # V2.1 does not support XML but we need to keep an entry in the diff --git a/nova/api/openstack/compute/helpers.py b/nova/api/openstack/compute/helpers.py index 0e08d0e0399e..24dc99301b70 100644 --- a/nova/api/openstack/compute/helpers.py +++ b/nova/api/openstack/compute/helpers.py @@ -22,6 +22,12 @@ API_DISK_CONFIG = "OS-DCF:diskConfig" API_ACCESS_V4 = "accessIPv4" API_ACCESS_V6 = "accessIPv6" +# possible ops +CREATE = 'create' +UPDATE = 'update' +REBUILD = 'rebuild' +RESIZE = 'resize' + def disk_config_from_api(value): if value == 'AUTO': @@ -33,7 +39,20 @@ def disk_config_from_api(value): raise exc.HTTPBadRequest(explanation=msg) -def translate_attributes(server_dict, operation_kwargs): +def get_injected_files(personality): + """Create a list of injected files from the personality attribute. + + At this time, injected_files must be formatted as a list of + (file_path, file_content) pairs for compatibility with the + underlying compute service. + """ + injected_files = [] + for item in personality: + injected_files.append((item['path'], item['contents'])) + return injected_files + + +def translate_attributes(op, server_dict, operation_kwargs): """Translate REST attributes on create to server object kwargs. Our REST API is relatively fixed, but internal representations @@ -45,6 +64,10 @@ def translate_attributes(server_dict, operation_kwargs): It's done in a common function as this is used for create / resize / rebuild / update + The ``op`` is the operation that we are transforming, because + there are times when we translate differently for different + operations. (Yes, it's a little nuts, but legacy... ) + The ``server_dict`` is a representation of the server in question. During ``create`` and ``update`` operations this will actually be the ``server`` element of the request body. @@ -57,6 +80,7 @@ def translate_attributes(server_dict, operation_kwargs): why it's important to only set operation_kwargs if there is something to set. Input validation will ensure that we are only operating on appropriate attributes for each operation. + """ # Disk config auto_disk_config_raw = server_dict.pop(API_DISK_CONFIG, None) @@ -72,7 +96,21 @@ def translate_attributes(server_dict, operation_kwargs): # This is only ever expected during rebuild operations, and only # does anything with Ironic driver. It also demonstrates the lack # of understanding of the word ephemeral. - if 'preserve_ephemeral' in server_dict: + if 'preserve_ephemeral' in server_dict and op == REBUILD: preserve = strutils.bool_from_string( server_dict.pop('preserve_ephemeral'), strict=True) operation_kwargs['preserve_ephemeral'] = preserve + + # yes, we use different kwargs, this goes all the way back to + # commit cebc98176926f57016a508d5c59b11f55dfcf2b3. + if 'personality' in server_dict: + if op == REBUILD: + operation_kwargs['files_to_inject'] = get_injected_files( + server_dict.pop('personality')) + # NOTE(sdague): the deprecated hooks infrastructure doesn't + # function if injected files is not defined as a list. Once hooks + # are removed, this should go back inside the personality + # conditional above. + if op == CREATE: + operation_kwargs['injected_files'] = get_injected_files( + server_dict.pop('personality', [])) diff --git a/nova/api/openstack/compute/personality.py b/nova/api/openstack/compute/personality.py deleted file mode 100644 index aede3fbd51d4..000000000000 --- a/nova/api/openstack/compute/personality.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2014 NEC Corporation. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from nova.api.openstack.compute.schemas import personality -from nova.api.openstack import extensions - -ALIAS = "os-personality" - - -class Personality(extensions.V21APIExtensionBase): - """Personality support.""" - - name = "Personality" - alias = ALIAS - version = 1 - - def get_controller_extensions(self): - return [] - - def get_resources(self): - return [] - - def _get_injected_files(self, personality): - """Create a list of injected files from the personality attribute. - - At this time, injected_files must be formatted as a list of - (file_path, file_content) pairs for compatibility with the - underlying compute service. - """ - injected_files = [] - for item in personality: - injected_files.append((item['path'], item['contents'])) - return injected_files - - # Extend server in both create & rebuild - # - # TODO(sdague): it looks weird that server_create is different - # than server_rebuild, right? Well you can totally blame hooks for - # that. By accident this means that server personalities are - # always creating the injected_files kwarg, even if there is - # nothing in it. Hooks totally needs injected_files to be a - # thing. Once hooks are removed from tree, this function can be - # made to look like the rebuild one. - def server_create(self, server_dict, create_kwargs, body_deprecated): - create_kwargs['injected_files'] = self._get_injected_files( - server_dict.get('personality', [])) - - def server_rebuild(self, server_dict, create_kwargs): - if 'personality' in server_dict: - create_kwargs['files_to_inject'] = self._get_injected_files( - server_dict['personality']) - - # Extend schema with the new allowed parameters - def get_server_create_schema(self, version): - return personality.server_create - - def get_server_rebuild_schema(self, version): - return personality.server_create diff --git a/nova/api/openstack/compute/schemas/personality.py b/nova/api/openstack/compute/schemas/personality.py deleted file mode 100644 index 0c7d5eaef2bd..000000000000 --- a/nova/api/openstack/compute/schemas/personality.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2014 NEC Corporation. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -server_create = { - 'personality': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'path': {'type': 'string'}, - 'contents': { - 'type': 'string', - 'format': 'base64' - } - }, - 'additionalProperties': False, - } - } -} diff --git a/nova/api/openstack/compute/schemas/servers.py b/nova/api/openstack/compute/schemas/servers.py index 1e7ca1f7f316..7e912f50267d 100644 --- a/nova/api/openstack/compute/schemas/servers.py +++ b/nova/api/openstack/compute/schemas/servers.py @@ -46,6 +46,7 @@ base_create = { 'OS-DCF:diskConfig': parameter_types.disk_config, 'accessIPv4': parameter_types.accessIPv4, 'accessIPv6': parameter_types.accessIPv6, + 'personality': parameter_types.personality, }, 'required': ['name', 'flavorRef'], 'additionalProperties': False, @@ -107,6 +108,7 @@ base_rebuild = { 'OS-DCF:diskConfig': parameter_types.disk_config, 'accessIPv4': parameter_types.accessIPv4, 'accessIPv6': parameter_types.accessIPv6, + 'personality': parameter_types.personality, }, 'required': ['imageRef'], 'additionalProperties': False, diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 3998209cab7e..65933aeb8278 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -60,8 +60,6 @@ class ServersController(wsgi.Controller): EXTENSION_CREATE_NAMESPACE = 'nova.api.v21.extensions.server.create' - EXTENSION_REBUILD_NAMESPACE = 'nova.api.v21.extensions.server.rebuild' - _view_builder_class = views_servers.ViewBuilderV21 schema_server_create = schema_servers.base_create @@ -159,17 +157,6 @@ class ServersController(wsgi.Controller): if not list(self.create_extension_manager): LOG.debug("Did not find any server create extensions") - # Look for implementation of extension point of server rebuild - self.rebuild_extension_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_REBUILD_NAMESPACE, - check_func=_check_load_extension('server_rebuild'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if not list(self.rebuild_extension_manager): - LOG.debug("Did not find any server rebuild extensions") - # Look for API schema of server create extension self.create_schema_manager = \ stevedore.enabled.EnabledExtensionManager( @@ -190,26 +177,6 @@ class ServersController(wsgi.Controller): else: LOG.debug("Did not find any server create schemas") - # Look for API schema of server rebuild extension - self.rebuild_schema_manager = \ - stevedore.enabled.EnabledExtensionManager( - namespace=self.EXTENSION_REBUILD_NAMESPACE, - check_func=_check_load_extension('get_server_rebuild_schema'), - invoke_on_load=True, - invoke_kwds={"extension_info": self.extension_info}, - propagate_map_exceptions=True) - if list(self.rebuild_schema_manager): - self.rebuild_schema_manager.map(self._rebuild_extension_schema, - self.schema_server_rebuild_v219, - '2.19') - self.rebuild_schema_manager.map(self._rebuild_extension_schema, - self.schema_server_rebuild, '2.1') - self.rebuild_schema_manager.map(self._rebuild_extension_schema, - self.schema_server_rebuild_v20, - '2.0') - else: - LOG.debug("Did not find any server rebuild schemas") - @extensions.expected_errors((400, 403)) def index(self, req): """Returns a list of server names and ids for a given user.""" @@ -524,7 +491,8 @@ class ServersController(wsgi.Controller): availability_zone = create_kwargs.pop("availability_zone", None) - helpers.translate_attributes(server_dict, create_kwargs) + helpers.translate_attributes(helpers.CREATE, + server_dict, create_kwargs) target = { 'project_id': context.project_id, @@ -748,15 +716,17 @@ class ServersController(wsgi.Controller): update_dict = {} ctxt.can(server_policies.SERVERS % 'update') - if 'name' in body['server']: + server = body['server'] + + if 'name' in server: update_dict['display_name'] = common.normalize_name( - body['server']['name']) + server['name']) - if 'description' in body['server']: + if 'description' in server: # This is allowed to be None (remove description) - update_dict['display_description'] = body['server']['description'] + update_dict['display_description'] = server['description'] - helpers.translate_attributes(body['server'], update_dict) + helpers.translate_attributes(helpers.UPDATE, server, update_dict) instance = self._get_server(ctxt, req, id, is_detail=True) try: @@ -920,10 +890,10 @@ class ServersController(wsgi.Controller): resize_dict = body['resize'] flavor_ref = str(resize_dict["flavorRef"]) - resize_kwargs = {} - helpers.translate_attributes(resize_dict, resize_kwargs) + kwargs = {} + helpers.translate_attributes(helpers.RESIZE, resize_dict, kwargs) - self._resize(req, id, flavor_ref, **resize_kwargs) + self._resize(req, id, flavor_ref, **kwargs) @wsgi.response(202) @extensions.expected_errors((400, 403, 404, 409)) @@ -950,21 +920,17 @@ class ServersController(wsgi.Controller): 'metadata': 'metadata', } - rebuild_kwargs = {} + kwargs = {} - if list(self.rebuild_extension_manager): - self.rebuild_extension_manager.map(self._rebuild_extension_point, - rebuild_dict, rebuild_kwargs) - - helpers.translate_attributes(rebuild_dict, rebuild_kwargs) + helpers.translate_attributes(helpers.REBUILD, rebuild_dict, kwargs) for request_attribute, instance_attribute in attr_map.items(): try: if request_attribute == 'name': - rebuild_kwargs[instance_attribute] = common.normalize_name( + kwargs[instance_attribute] = common.normalize_name( rebuild_dict[request_attribute]) else: - rebuild_kwargs[instance_attribute] = rebuild_dict[ + kwargs[instance_attribute] = rebuild_dict[ request_attribute] except (KeyError, TypeError): pass @@ -974,7 +940,7 @@ class ServersController(wsgi.Controller): instance, image_href, password, - **rebuild_kwargs) + **kwargs) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: diff --git a/nova/api/validation/parameter_types.py b/nova/api/validation/parameter_types.py index 7fada7667693..4110a5e0f017 100644 --- a/nova/api/validation/parameter_types.py +++ b/nova/api/validation/parameter_types.py @@ -377,3 +377,18 @@ flavor_param_positive = copy.deepcopy(volume_size) flavor_param_non_negative = copy.deepcopy(volume_size) flavor_param_non_negative['minimum'] = 0 + +personality = { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'path': {'type': 'string'}, + 'contents': { + 'type': 'string', + 'format': 'base64' + } + }, + 'additionalProperties': False, + } +} diff --git a/nova/policies/__init__.py b/nova/policies/__init__.py index 17f12be84171..3e25d54eac20 100644 --- a/nova/policies/__init__.py +++ b/nova/policies/__init__.py @@ -68,7 +68,6 @@ from nova.policies import networks from nova.policies import networks_associate from nova.policies import pause_server from nova.policies import pci -from nova.policies import personality from nova.policies import quota_class_sets from nova.policies import quota_sets from nova.policies import remote_consoles @@ -154,7 +153,6 @@ def list_rules(): networks_associate.list_rules(), pause_server.list_rules(), pci.list_rules(), - personality.list_rules(), quota_class_sets.list_rules(), quota_sets.list_rules(), remote_consoles.list_rules(), diff --git a/nova/policies/personality.py b/nova/policies/personality.py deleted file mode 100644 index 81f3c2ea944a..000000000000 --- a/nova/policies/personality.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2016 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_policy import policy - -from nova.policies import base - - -POLICY_ROOT = 'os_compute_api:os-personality:%s' - - -personality_policies = [ - policy.RuleDefault( - name=POLICY_ROOT % 'discoverable', - check_str=base.RULE_ANY), -] - - -def list_rules(): - return personality_policies diff --git a/nova/tests/unit/test_policy.py b/nova/tests/unit/test_policy.py index 6a271f7940dd..50801625ad00 100644 --- a/nova/tests/unit/test_policy.py +++ b/nova/tests/unit/test_policy.py @@ -466,7 +466,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase): "os_compute_api:os-networks-associate:discoverable", "os_compute_api:os-pause-server:discoverable", "os_compute_api:os-pci:discoverable", -"os_compute_api:os-personality:discoverable", "os_compute_api:os-quota-sets:discoverable", "os_compute_api:os-quota-class-sets:discoverable", "os_compute_api:os-rescue:discoverable", diff --git a/releasenotes/notes/extensions_remove-37e9d4092981abbe.yaml b/releasenotes/notes/extensions_remove-37e9d4092981abbe.yaml index 7a8ac5a5d33b..97c31e2c56a5 100644 --- a/releasenotes/notes/extensions_remove-37e9d4092981abbe.yaml +++ b/releasenotes/notes/extensions_remove-37e9d4092981abbe.yaml @@ -20,3 +20,6 @@ upgrade: * nova.api.v21.extensions.server.update - allowed accepting additional parameters on server update requests. + + * nova.api.v21.extensions.server.rebuild - allowed accepting + additional parameters on server rebuild requests. diff --git a/setup.cfg b/setup.cfg index 69a36b443289..426465047deb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -122,7 +122,6 @@ nova.api.v21.extensions = # NOTE(cyeoh): this is intentionally disabled until microversions is active. # See https://bugs.launchpad.net/nova/+bug/1426241 # pci = nova.api.openstack.compute.pci:Pci - personality = nova.api.openstack.compute.personality:Personality quota_classes = nova.api.openstack.compute.quota_classes:QuotaClasses quota_sets = nova.api.openstack.compute.quota_sets:QuotaSets remote_consoles = nova.api.openstack.compute.remote_consoles:RemoteConsoles @@ -157,14 +156,10 @@ nova.api.v21.extensions.server.create = 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 - personality = nova.api.openstack.compute.personality:Personality 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.extensions.server.rebuild = - personality = nova.api.openstack.compute.personality:Personality - nova.api.v21.test_extensions = basic = nova.tests.unit.api.openstack.compute.basic:Basic microversions = nova.tests.unit.api.openstack.compute.microversions:Microversions