diff --git a/api-ref/source/index.rst b/api-ref/source/index.rst index 85ff0a954aef..80e5e369e7e4 100644 --- a/api-ref/source/index.rst +++ b/api-ref/source/index.rst @@ -73,7 +73,6 @@ limited to some maximum microversion. .. include:: os-security-group-default-rules.inc .. include:: os-security-group-rules.inc .. include:: os-hosts.inc -.. include:: os-cells.inc ============= Obsolete APIs @@ -89,3 +88,4 @@ Compute API in the past, but no longer exist. .. include:: os-fixed-ips.inc .. include:: os-floating-ips-bulk.inc .. include:: os-floating-ip-dns.inc +.. include:: os-cells.inc diff --git a/api-ref/source/os-cells.inc b/api-ref/source/os-cells.inc index de0d610f6a2f..a2f8d4f57d35 100644 --- a/api-ref/source/os-cells.inc +++ b/api-ref/source/os-cells.inc @@ -1,8 +1,4 @@ .. -*- rst -*- -.. needs:parameter_verification -.. needs:example_verification -.. needs:body_verification - ============================== Cells (os-cells, capacities) @@ -11,10 +7,13 @@ Adds neighbor cells, lists neighbor cells, and shows the capabilities of the local cell. By default, only administrators can manage cells. -.. warning:: These APIs refer to a Cells v1 deployment which was deprecated - in the 16.0.0 Pike release. These are not used with Cells v2 - which is required beginning with the 15.0.0 Ocata release where all Nova - deployments consist of at least one Cells v2 cell. +.. warning:: + + These APIs refer to a Cells v1 deployment which was deprecated in the 16.0.0 + Pike release. These are not used with Cells v2 which is required beginning + with the 15.0.0 Ocata release where all Nova deployments consist of at least + one Cells v2 cell. + They were removed in the 20.0.0 Train release. List Cells ========== @@ -26,7 +25,7 @@ Lists cells. Normal response codes: 200 Error response codes: badRequest(400), unauthorized(401), forbidden(403), -NotImplemented(501) +gone(410), notImplemented(501) Request ------- @@ -52,10 +51,10 @@ Create Cell Create a new cell. -Normal response code: 200 +Normal response codes: 200 Error response codes: badRequest(400), unauthorized(401), forbidden(403), -NotImplemented(501) +gone(410), notImplemented(501) Capacities ========== @@ -64,8 +63,10 @@ Capacities Retrieve capacities. +Normal response codes: 200 + Error response codes: badRequest(400), unauthorized(401), forbidden(403), -NotImplemented(501) +gone(410), notImplemented(501) List Cells With Details ======================= @@ -77,7 +78,7 @@ Lists cells with details of capabilities. Normal response codes: 200 Error response codes: badRequest(400), unauthorized(401), forbidden(403), -NotImplemented(501) +gone(410), notImplemented(501) Request ------- @@ -94,10 +95,10 @@ Info For This Cell Retrieve info about the current cell. -Normal response code: 200 +Normal response codes: 200 Error response codes: badRequest(400), unauthorized(401), forbidden(403), -NotImplemented(501) +gone(410), notImplemented(501) Show Cell Data ============== @@ -109,7 +110,7 @@ Shows data for a cell. Normal response codes: 200 Error response codes: badRequest(400), unauthorized(401), forbidden(403), -itemNotFound(404), NotImplemented(501) +itemNotFound(404), gone(410), notImplemented(501) Request ------- @@ -129,15 +130,15 @@ Response Update a Cell ============= -.. rest_method:: PUT /os-cells/{cell_od} +.. rest_method:: PUT /os-cells/{cell_id} Update an existing cell. -Normal response code: 200 +Normal response codes: 200 Error response codes: badRequest(400), unauthorized(401), forbidden(403), -itemNotFound(404), NotImplemented(501) +itemNotFound(404), gone(410), notImplemented(501) Delete a Cell ============= @@ -146,10 +147,10 @@ Delete a Cell Remove a cell. -Normal response code: 200 +Normal response codes: 200 Error response codes: badRequest(400), unauthorized(401), forbidden(403), -itemNotFound(404), NotImplemented(501) +itemNotFound(404), gone(410), notImplemented(501) Show Cell Capacities ==================== @@ -158,10 +159,10 @@ Show Cell Capacities Shows capacities for a cell. -Normal response codes: 200,501 +Normal response codes: 200 Error response codes: badRequest(400), unauthorized(401), forbidden(403), -itemNotFound(404), NotImplemented(501) +itemNotFound(404), gone(410), notImplemented(501) Request ------- diff --git a/doc/api_samples/os-cells/cells-list-empty-resp.json b/doc/api_samples/os-cells/cells-list-empty-resp.json deleted file mode 100644 index 5325a4e855e2..000000000000 --- a/doc/api_samples/os-cells/cells-list-empty-resp.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "cells": [] -} \ No newline at end of file diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index c54cf732665a..fd226130fbfb 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -14,7 +14,6 @@ # under the License. import collections -import functools import itertools import re @@ -495,15 +494,6 @@ def get_flavor(context, flavor_id): raise exc.HTTPNotFound(explanation=error.format_message()) -def check_cells_enabled(function): - @functools.wraps(function) - def inner(*args, **kwargs): - if not CONF.cells.enable: - raise_feature_not_supported() - return function(*args, **kwargs) - return inner - - def is_all_tenants(search_opts): """Checks to see if the all_tenants flag is in search_opts diff --git a/nova/api/openstack/compute/cells.py b/nova/api/openstack/compute/cells.py index 2dcc48ba50fa..87ec64b4b722 100644 --- a/nova/api/openstack/compute/cells.py +++ b/nova/api/openstack/compute/cells.py @@ -14,289 +14,49 @@ # License for the specific language governing permissions and limitations # under the License. -"""The cells extension.""" - -import oslo_messaging as messaging -from oslo_utils import strutils -import six from webob import exc -from nova.api.openstack import common -from nova.api.openstack.compute.schemas import cells from nova.api.openstack import wsgi -from nova.api import validation -from nova.cells import rpcapi as cells_rpcapi -import nova.conf -from nova import exception -from nova.i18n import _ -from nova.policies import cells as cells_policies -from nova import rpc - - -CONF = nova.conf.CONF - - -def _filter_keys(item, keys): - """Filters all model attributes except for keys - item is a dict - """ - return {k: v for k, v in item.items() if k in keys} - - -def _fixup_cell_info(cell_info, keys): - """If the transport_url is present in the cell, derive username, - rpc_host, and rpc_port from it. - """ - - if 'transport_url' not in cell_info: - return - - # Disassemble the transport URL - transport_url = cell_info.pop('transport_url') - try: - transport_url = rpc.get_transport_url(transport_url) - except messaging.InvalidTransportURL: - # Just go with None's - for key in keys: - cell_info.setdefault(key, None) - return - - if not transport_url.hosts: - return - - transport_host = transport_url.hosts[0] - - transport_field_map = {'rpc_host': 'hostname', 'rpc_port': 'port'} - for key in keys: - if key in cell_info: - continue - - transport_field = transport_field_map.get(key, key) - cell_info[key] = getattr(transport_host, transport_field) - - -def _scrub_cell(cell, detail=False): - keys = ['name', 'username', 'rpc_host', 'rpc_port'] - if detail: - keys.append('capabilities') - - cell_info = _filter_keys(cell, keys + ['transport_url']) - _fixup_cell_info(cell_info, keys) - cell_info['type'] = 'parent' if cell['is_parent'] else 'child' - return cell_info class CellsController(wsgi.Controller): - """Controller for Cell resources.""" + """(Removed) Controller for Cell resources. - def __init__(self): - self.cells_rpcapi = cells_rpcapi.CellsAPI() + This was removed during the Train release in favour of cells v2. + """ - def _get_cells(self, ctxt, req, detail=False): - """Return all cells.""" - # Ask the CellsManager for the most recent data - items = self.cells_rpcapi.get_cell_info_for_neighbors(ctxt) - items = common.limited(items, req) - items = [_scrub_cell(item, detail=detail) for item in items] - return dict(cells=items) - - @wsgi.expected_errors(501) - @common.check_cells_enabled + @wsgi.expected_errors(410) def index(self, req): - """Return all cells in brief.""" - ctxt = req.environ['nova.context'] - ctxt.can(cells_policies.BASE_POLICY_NAME) - return self._get_cells(ctxt, req) + raise exc.HTTPGone() - @wsgi.expected_errors(501) - @common.check_cells_enabled + @wsgi.expected_errors(410) def detail(self, req): - """Return all cells in detail.""" - ctxt = req.environ['nova.context'] - ctxt.can(cells_policies.BASE_POLICY_NAME) - return self._get_cells(ctxt, req, detail=True) + raise exc.HTTPGone() - @wsgi.expected_errors(501) - @common.check_cells_enabled + @wsgi.expected_errors(410) def info(self, req): - """Return name and capabilities for this cell.""" - context = req.environ['nova.context'] - context.can(cells_policies.BASE_POLICY_NAME) - cell_capabs = {} - my_caps = CONF.cells.capabilities - for cap in my_caps: - key, value = cap.split('=') - cell_capabs[key] = value - cell = {'name': CONF.cells.name, - 'type': 'self', - 'rpc_host': None, - 'rpc_port': 0, - 'username': None, - 'capabilities': cell_capabs} - return dict(cell=cell) + raise exc.HTTPGone() - @wsgi.expected_errors((404, 501)) - @common.check_cells_enabled + @wsgi.expected_errors(410) def capacities(self, req, id=None): - """Return capacities for a given cell or all cells.""" - # TODO(kaushikc): return capacities as a part of cell info and - # cells detail calls in v2.1, along with capabilities - context = req.environ['nova.context'] - context.can(cells_policies.BASE_POLICY_NAME) - try: - capacities = self.cells_rpcapi.get_capacities(context, - cell_name=id) - except exception.CellNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) + raise exc.HTTPGone() - return dict(cell={"capacities": capacities}) - - @wsgi.expected_errors((404, 501)) - @common.check_cells_enabled + @wsgi.expected_errors(410) def show(self, req, id): - """Return data about the given cell name. 'id' is a cell name.""" - context = req.environ['nova.context'] - context.can(cells_policies.BASE_POLICY_NAME) - try: - cell = self.cells_rpcapi.cell_get(context, id) - except exception.CellNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - return dict(cell=_scrub_cell(cell)) + raise exc.HTTPGone() - # NOTE(gmann): Returns 200 for backwards compatibility but should be 204 - # as this operation complete the deletion of aggregate resource and return - # no response body. - @wsgi.expected_errors((403, 404, 501)) - @common.check_cells_enabled + @wsgi.expected_errors(410) def delete(self, req, id): - """Delete a child or parent cell entry. 'id' is a cell name.""" - context = req.environ['nova.context'] + raise exc.HTTPGone() - context.can(cells_policies.POLICY_ROOT % "delete") - - try: - num_deleted = self.cells_rpcapi.cell_delete(context, id) - except exception.CellsUpdateUnsupported as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - if num_deleted == 0: - raise exc.HTTPNotFound( - explanation=_("Cell %s doesn't exist.") % id) - - def _normalize_cell(self, cell, existing=None): - """Normalize input cell data. Normalizations include: - - * Converting cell['type'] to is_parent boolean. - * Merging existing transport URL with transport information. - """ - - if 'name' in cell: - cell['name'] = common.normalize_name(cell['name']) - - # Start with the cell type conversion - if 'type' in cell: - cell['is_parent'] = cell.pop('type') == 'parent' - # Avoid cell type being overwritten to 'child' - elif existing: - cell['is_parent'] = existing['is_parent'] - else: - cell['is_parent'] = False - - # Now we disassemble the existing transport URL... - transport_url = existing.get('transport_url') if existing else None - transport_url = rpc.get_transport_url(transport_url) - - if 'rpc_virtual_host' in cell: - transport_url.virtual_host = cell.pop('rpc_virtual_host') - - if not transport_url.hosts: - transport_url.hosts.append(messaging.TransportHost()) - transport_host = transport_url.hosts[0] - if 'rpc_port' in cell: - cell['rpc_port'] = int(cell['rpc_port']) - # Copy over the input fields - transport_field_map = { - 'username': 'username', - 'password': 'password', - 'hostname': 'rpc_host', - 'port': 'rpc_port', - } - for key, input_field in transport_field_map.items(): - # Only override the value if we're given an override - if input_field in cell: - setattr(transport_host, key, cell.pop(input_field)) - - # Now set the transport URL - cell['transport_url'] = str(transport_url) - - # NOTE(gmann): Returns 200 for backwards compatibility but should be 201 - # as this operation complete the creation of aggregates resource when - # returning a response. - @wsgi.expected_errors((400, 403, 501)) - @common.check_cells_enabled - @validation.schema(cells.create_v20, '2.0', '2.0') - @validation.schema(cells.create, '2.1') + @wsgi.expected_errors(410) def create(self, req, body): - """Create a child cell entry.""" - context = req.environ['nova.context'] + raise exc.HTTPGone() - context.can(cells_policies.POLICY_ROOT % "create") - - cell = body['cell'] - self._normalize_cell(cell) - try: - cell = self.cells_rpcapi.cell_create(context, cell) - except exception.CellsUpdateUnsupported as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - return dict(cell=_scrub_cell(cell)) - - @wsgi.expected_errors((400, 403, 404, 501)) - @common.check_cells_enabled - @validation.schema(cells.update_v20, '2.0', '2.0') - @validation.schema(cells.update, '2.1') + @wsgi.expected_errors(410) def update(self, req, id, body): - """Update a child cell entry. 'id' is the cell name to update.""" - context = req.environ['nova.context'] + raise exc.HTTPGone() - context.can(cells_policies.POLICY_ROOT % "update") - - cell = body['cell'] - cell.pop('id', None) - - try: - # NOTE(Vek): There is a race condition here if multiple - # callers are trying to update the cell - # information simultaneously. Since this - # operation is administrative in nature, and - # will be going away in the future, I don't see - # it as much of a problem... - existing = self.cells_rpcapi.cell_get(context, id) - except exception.CellNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - self._normalize_cell(cell, existing) - try: - cell = self.cells_rpcapi.cell_update(context, id, cell) - except exception.CellNotFound as e: - raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.CellsUpdateUnsupported as e: - raise exc.HTTPForbidden(explanation=e.format_message()) - return dict(cell=_scrub_cell(cell)) - - # NOTE(gmann): Returns 200 for backwards compatibility but should be 204 - # as this operation complete the sync instance info and return - # no response body. - @wsgi.expected_errors((400, 501)) - @common.check_cells_enabled - @validation.schema(cells.sync_instances) + @wsgi.expected_errors(410) def sync_instances(self, req, body): - """Tell all cells to sync instance info.""" - context = req.environ['nova.context'] - - context.can(cells_policies.POLICY_ROOT % "sync_instances") - - project_id = body.pop('project_id', None) - deleted = body.pop('deleted', False) - updated_since = body.pop('updated_since', None) - if isinstance(deleted, six.string_types): - deleted = strutils.bool_from_string(deleted, strict=True) - self.cells_rpcapi.sync_instances(context, project_id=project_id, - updated_since=updated_since, deleted=deleted) + raise exc.HTTPGone() diff --git a/nova/api/openstack/compute/schemas/cells.py b/nova/api/openstack/compute/schemas/cells.py deleted file mode 100644 index f0d52f6792b6..000000000000 --- a/nova/api/openstack/compute/schemas/cells.py +++ /dev/null @@ -1,111 +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. - -import copy - -from nova.api.validation import parameter_types - - -create = { - 'type': 'object', - 'properties': { - 'cell': { - 'type': 'object', - 'properties': { - 'name': parameter_types.cell_name, - 'type': { - 'type': 'string', - 'enum': ['parent', 'child'], - }, - - # NOTE: In unparse_transport_url(), a url consists of the - # following parameters: - # "qpid://:@:/" - # or - # "rabbit://:@:/" - # Then the url is stored into transport_url of cells table - # which is defined with String(255). - 'username': { - 'type': 'string', 'maxLength': 255, - 'pattern': '^[a-zA-Z0-9-_]*$' - }, - 'password': { - # Allow to specify any string for strong password. - 'type': 'string', 'maxLength': 255, - }, - 'rpc_host': parameter_types.hostname_or_ip_address, - 'rpc_port': parameter_types.tcp_udp_port, - 'rpc_virtual_host': parameter_types.hostname_or_ip_address, - }, - 'required': ['name'], - 'additionalProperties': False, - }, - }, - 'required': ['cell'], - 'additionalProperties': False, -} - - -create_v20 = copy.deepcopy(create) -create_v20['properties']['cell']['properties']['name'] = (parameter_types. - cell_name_leading_trailing_spaces) - - -update = { - 'type': 'object', - 'properties': { - 'cell': { - 'type': 'object', - 'properties': { - 'name': parameter_types.cell_name, - 'type': { - 'type': 'string', - 'enum': ['parent', 'child'], - }, - 'username': { - 'type': 'string', 'maxLength': 255, - 'pattern': '^[a-zA-Z0-9-_]*$' - }, - 'password': { - 'type': 'string', 'maxLength': 255, - }, - 'rpc_host': parameter_types.hostname_or_ip_address, - 'rpc_port': parameter_types.tcp_udp_port, - 'rpc_virtual_host': parameter_types.hostname_or_ip_address, - }, - 'additionalProperties': False, - }, - }, - 'required': ['cell'], - 'additionalProperties': False, -} - - -update_v20 = copy.deepcopy(create) -update_v20['properties']['cell']['properties']['name'] = (parameter_types. - cell_name_leading_trailing_spaces) - - -sync_instances = { - 'type': 'object', - 'properties': { - 'project_id': parameter_types.project_id, - 'deleted': parameter_types.boolean, - 'updated_since': { - 'type': 'string', - 'format': 'date-time', - }, - }, - 'additionalProperties': False, -} diff --git a/nova/api/validation/parameter_types.py b/nova/api/validation/parameter_types.py index 2f63f4d8c2e0..7943142e3d97 100644 --- a/nova/api/validation/parameter_types.py +++ b/nova/api/validation/parameter_types.py @@ -200,24 +200,6 @@ valid_az_name_leading_trailing_spaces_regex = ValidationRegex( "with at least one non space character")) -valid_cell_name_regex = ValidationRegex( - valid_name_regex_base % ( - _build_regex_range(ws=False, invert=True), - _build_regex_range(exclude=['!', '.', '@']), - _build_regex_range(ws=False, invert=True)), - _("printable characters except !, ., @. " - "Can not start or end with whitespace.")) - - -# cell's name disallow '!', '.' and '@'. -valid_cell_name_leading_trailing_spaces_regex = ValidationRegex( - valid_name_leading_trailing_spaces_regex_base % { - 'ws': _build_regex_range(exclude=['!', '.', '@']), - 'no_ws': _build_regex_range(ws=False, exclude=['!', '.', '@'])}, - _("printable characters except !, ., @, " - "with at least one non space character")) - - valid_name_leading_trailing_spaces_regex = ValidationRegex( valid_name_leading_trailing_spaces_regex_base % { 'ws': _build_regex_range(), @@ -315,18 +297,6 @@ az_name_with_leading_trailing_spaces = { } -cell_name = { - 'type': 'string', 'minLength': 1, 'maxLength': 255, - 'format': 'cell_name' -} - - -cell_name_leading_trailing_spaces = { - 'type': 'string', 'minLength': 1, 'maxLength': 255, - 'format': 'cell_name_with_leading_trailing_spaces' -} - - name_with_leading_trailing_spaces = { 'type': 'string', 'minLength': 1, 'maxLength': 255, 'format': 'name_with_leading_trailing_spaces' @@ -339,6 +309,7 @@ description = { } +# TODO(stephenfin): This is no longer used and should be removed tcp_udp_port = { 'type': ['integer', 'string'], 'pattern': '^[0-9]*$', 'minimum': 0, 'maximum': 65535, diff --git a/nova/api/validation/validators.py b/nova/api/validation/validators.py index a4d3d1b4960d..f36393463c18 100644 --- a/nova/api/validation/validators.py +++ b/nova/api/validation/validators.py @@ -154,33 +154,6 @@ def _validate_az_name(instance): raise exception.InvalidName(reason=regex.reason) -@jsonschema.FormatChecker.cls_checks('cell_name_with_leading_trailing_spaces', - exception.InvalidName) -def _validate_cell_name_with_leading_trailing_spaces(instance): - regex = parameter_types.valid_cell_name_leading_trailing_spaces_regex - try: - if re.search(regex.regex, instance): - return True - except TypeError: - # The name must be string type. If instance isn't string type, the - # TypeError will be raised at here. - pass - raise exception.InvalidName(reason=regex.reason) - - -@jsonschema.FormatChecker.cls_checks('cell_name', exception.InvalidName) -def _validate_cell_name(instance): - regex = parameter_types.valid_cell_name_regex - try: - if re.search(regex.regex, instance): - return True - except TypeError: - # The name must be string type. If instance isn't string type, the - # TypeError will be raised at here. - pass - raise exception.InvalidName(reason=regex.reason) - - def _soft_validate_additional_properties(validator, additional_properties_value, instance, diff --git a/nova/policies/__init__.py b/nova/policies/__init__.py index c837df848c3f..8a27447c7fee 100644 --- a/nova/policies/__init__.py +++ b/nova/policies/__init__.py @@ -22,7 +22,6 @@ from nova.policies import attach_interfaces from nova.policies import availability_zone from nova.policies import baremetal_nodes from nova.policies import base -from nova.policies import cells from nova.policies import cells_scheduler from nova.policies import console_auth_tokens from nova.policies import console_output @@ -86,7 +85,6 @@ def list_rules(): attach_interfaces.list_rules(), availability_zone.list_rules(), baremetal_nodes.list_rules(), - cells.list_rules(), cells_scheduler.list_rules(), console_auth_tokens.list_rules(), console_output.list_rules(), diff --git a/nova/policies/cells.py b/nova/policies/cells.py deleted file mode 100644 index 14fa69721d27..000000000000 --- a/nova/policies/cells.py +++ /dev/null @@ -1,96 +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 - - -BASE_POLICY_NAME = 'os_compute_api:os-cells' -POLICY_ROOT = 'os_compute_api:os-cells:%s' - - -cells_policies = [ - policy.DocumentedRuleDefault( - POLICY_ROOT % 'update', - base.RULE_ADMIN_API, - 'Update an existing cell', - [ - { - 'method': 'PUT', - 'path': '/os-cells/{cell_id}' - } - ]), - policy.DocumentedRuleDefault( - POLICY_ROOT % 'create', - base.RULE_ADMIN_API, - 'Create a new cell', - [ - { - 'method': 'POST', - 'path': '/os-cells' - } - ]), - policy.DocumentedRuleDefault( - BASE_POLICY_NAME, - base.RULE_ADMIN_API, - 'List and show detailed info for a given cell or all cells', - [ - { - 'method': 'GET', - 'path': '/os-cells' - }, - { - 'method': 'GET', - 'path': '/os-cells/detail' - }, - { - 'method': 'GET', - 'path': '/os-cells/info' - }, - { - 'method': 'GET', - 'path': '/os-cells/capacities' - }, - { - 'method': 'GET', - 'path': '/os-cells/{cell_id}' - } - ]), - policy.DocumentedRuleDefault( - POLICY_ROOT % 'sync_instances', - base.RULE_ADMIN_API, - 'Sync instances info in all cells', - [ - { - 'method': 'POST', - 'path': '/os-cells/sync_instances' - } - ]), - policy.DocumentedRuleDefault( - POLICY_ROOT % 'delete', - base.RULE_ADMIN_API, - 'Remove a cell', - [ - { - 'method': 'DELETE', - 'path': '/os-cells/{cell_id}' - } - ]) -] - - -def list_rules(): - return cells_policies diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-capacities-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-capacities-resp.json.tpl deleted file mode 100644 index 5e067dd3aa36..000000000000 --- a/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-capacities-resp.json.tpl +++ /dev/null @@ -1,26 +0,0 @@ -{ - "cell": { - "capacities": { - "disk_free": { - "total_mb": 1052672, - "units_by_mb": { - "0": 0, - "163840": 5, - "20480": 46, - "40960": 23, - "81920": 11 - } - }, - "ram_free": { - "total_mb": 7680, - "units_by_mb": { - "16384": 0, - "2048": 3, - "4096": 1, - "512": 13, - "8192": 0 - } - } - } - } -} \ No newline at end of file diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-get-resp.json.tpl deleted file mode 100644 index 62eb8ec31d20..000000000000 --- a/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-get-resp.json.tpl +++ /dev/null @@ -1,9 +0,0 @@ -{ - "cell": { - "name": "cell3", - "rpc_host": null, - "rpc_port": null, - "type": "child", - "username": "username3" - } -} \ No newline at end of file diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-list-empty-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-list-empty-resp.json.tpl deleted file mode 100644 index 5325a4e855e2..000000000000 --- a/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-list-empty-resp.json.tpl +++ /dev/null @@ -1,3 +0,0 @@ -{ - "cells": [] -} \ No newline at end of file diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-list-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-list-resp.json.tpl deleted file mode 100644 index 97ea4c6dd32d..000000000000 --- a/nova/tests/functional/api_sample_tests/api_samples/os-cells/cells-list-resp.json.tpl +++ /dev/null @@ -1,39 +0,0 @@ -{ - "cells": [ - { - "name": "cell1", - "rpc_host": null, - "rpc_port": null, - "type": "child", - "username": "username1" - }, - { - "name": "cell3", - "rpc_host": null, - "rpc_port": null, - "type": "child", - "username": "username3" - }, - { - "name": "cell5", - "rpc_host": null, - "rpc_port": null, - "type": "child", - "username": "username5" - }, - { - "name": "cell2", - "rpc_host": null, - "rpc_port": null, - "type": "parent", - "username": "username2" - }, - { - "name": "cell4", - "rpc_host": null, - "rpc_port": null, - "type": "parent", - "username": "username4" - } - ] -} \ No newline at end of file diff --git a/nova/tests/functional/api_sample_tests/test_cells.py b/nova/tests/functional/api_sample_tests/test_cells.py index 4824b5b5e209..47a218386d37 100644 --- a/nova/tests/functional/api_sample_tests/test_cells.py +++ b/nova/tests/functional/api_sample_tests/test_cells.py @@ -1,5 +1,6 @@ # Copyright 2012 Nebula, Inc. # Copyright 2013 IBM Corp. +# Copyright 2019 Red Hat, Inc. # # 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 @@ -13,91 +14,47 @@ # License for the specific language governing permissions and limitations # under the License. -import mock -from six.moves import range - -from nova.cells import state -from nova.db.sqlalchemy import models -from nova import exception from nova.tests.functional.api_sample_tests import api_sample_base -class CellsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): - sample_dir = "os-cells" - - def setUp(self): - # db_check_interval < 0 makes cells manager always hit the DB - self.flags(enable=True, db_check_interval=-1, group='cells') - super(CellsSampleJsonTest, self).setUp() - self.cells = self.start_service('cells', - manager='nova.cells.manager.CellsManager') - self._stub_cells() - - def _stub_cells(self, num_cells=5): - self.cell_list = [] - self.cells_next_id = 1 - - def _fake_cell_get_all(context): - return self.cell_list - - def _fake_cell_get(inst, context, cell_name): - for cell in self.cell_list: - if cell['name'] == cell_name: - return cell - raise exception.CellNotFound(cell_name=cell_name) - - for x in range(num_cells): - cell = models.Cell() - our_id = self.cells_next_id - self.cells_next_id += 1 - cell.update({'id': our_id, - 'name': 'cell%s' % our_id, - 'transport_url': 'rabbit://username%s@/' % our_id, - 'is_parent': our_id % 2 == 0}) - self.cell_list.append(cell) - - self.stub_out('nova.db.api.cell_get_all', _fake_cell_get_all) - self.stub_out('nova.cells.rpcapi.CellsAPI.cell_get', _fake_cell_get) - - def test_cells_empty_list(self): - # Override this - self._stub_cells(num_cells=0) - response = self._do_get('os-cells') - self._verify_response('cells-list-empty-resp', {}, response, 200) +class CellsTest(api_sample_base.ApiSampleTestBaseV21): def test_cells_list(self): - response = self._do_get('os-cells') - self._verify_response('cells-list-resp', {}, response, 200) + self.api.api_get('os-cells', + check_response_status=[410]) - def test_cells_get(self): - response = self._do_get('os-cells/cell3') - self._verify_response('cells-get-resp', {}, response, 200) + def test_cells_capacity(self): + self.api.api_get('os-cells/capacities', + check_response_status=[410]) - def test_get_cell_capacity(self): - self._mock_cell_capacity() - state_manager = state.CellStateManager() - my_state = state_manager.get_my_state() - response = self._do_get('os-cells/%s/capacities' % - my_state.name) - return self._verify_response('cells-capacities-resp', - {}, response, 200) + def test_cells_detail(self): + self.api.api_get('os-cells/detail', + check_response_status=[410]) - def test_get_all_cells_capacity(self): - self._mock_cell_capacity() - response = self._do_get('os-cells/capacities') - return self._verify_response('cells-capacities-resp', - {}, response, 200) + def test_cells_info(self): + self.api.api_get('os-cells/info', + check_response_status=[410]) - def _mock_cell_capacity(self): - response = {"ram_free": - {"units_by_mb": {"8192": 0, "512": 13, - "4096": 1, "2048": 3, "16384": 0}, - "total_mb": 7680}, - "disk_free": - {"units_by_mb": {"81920": 11, "20480": 46, - "40960": 23, "163840": 5, "0": 0}, - "total_mb": 1052672} - } - goc_mock = mock.Mock() - goc_mock.return_value = response - self.cells.manager.state_manager.get_our_capacities = goc_mock + def test_cells_sync_instances(self): + self.api.api_post('os-cells/sync_instances', {}, + check_response_status=[410]) + + def test_cell_create(self): + self.api.api_post('os-cells', {}, + check_response_status=[410]) + + def test_cell_show(self): + self.api.api_get('os-cells/cell3', + check_response_status=[410]) + + def test_cell_update(self): + self.api.api_put('os-cells/cell3', {}, + check_response_status=[410]) + + def test_cell_delete(self): + self.api.api_delete('os-cells/cell3', + check_response_status=[410]) + + def test_cell_capacity(self): + self.api.api_get('os-cells/cell3/capacities', + check_response_status=[410]) diff --git a/nova/tests/unit/api/openstack/compute/test_cells.py b/nova/tests/unit/api/openstack/compute/test_cells.py deleted file mode 100644 index e0f0b19c474f..000000000000 --- a/nova/tests/unit/api/openstack/compute/test_cells.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright 2011-2012 OpenStack Foundation -# 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. - -import copy - -import mock -from oslo_utils import timeutils -from webob import exc - -from nova.api.openstack.compute import cells as cells_ext_v21 -from nova import context -from nova import exception -from nova import rpc -from nova import test -from nova.tests.unit.api.openstack import fakes - - -class BaseCellsTest(test.NoDBTestCase): - def setUp(self): - super(BaseCellsTest, self).setUp() - - self.fake_cells = [ - dict(id=1, name='cell1', is_parent=True, - weight_scale=1.0, weight_offset=0.0, - transport_url='rabbit://bob:xxxx@r1.example.org/'), - dict(id=2, name='cell2', is_parent=False, - weight_scale=1.0, weight_offset=0.0, - transport_url='rabbit://alice:qwerty@r2.example.org/')] - - self.fake_capabilities = [ - {'cap1': '0,1', 'cap2': '2,3'}, - {'cap3': '4,5', 'cap4': '5,6'}] - - def fake_cell_get(_self, context, cell_name): - for cell in self.fake_cells: - if cell_name == cell['name']: - return cell - else: - raise exception.CellNotFound(cell_name=cell_name) - - def fake_cell_create(_self, context, values): - cell = dict(id=1) - cell.update(values) - return cell - - def fake_cell_update(_self, context, cell_id, values): - cell = fake_cell_get(_self, context, cell_id) - cell.update(values) - return cell - - def fake_cells_api_get_all_cell_info(*args): - return self._get_all_cell_info(*args) - - self.stub_out('nova.cells.rpcapi.CellsAPI.cell_get', - fake_cell_get) - self.stub_out('nova.cells.rpcapi.CellsAPI.cell_update', - fake_cell_update) - self.stub_out('nova.cells.rpcapi.CellsAPI.cell_create', - fake_cell_create) - self.stub_out('nova.cells.rpcapi.CellsAPI.get_cell_info_for_neighbors', - fake_cells_api_get_all_cell_info) - - def _get_all_cell_info(self, *args): - def insecure_transport_url(url): - transport_url = rpc.get_transport_url(url) - transport_url.hosts[0].password = None - return str(transport_url) - - cells = copy.deepcopy(self.fake_cells) - cells[0]['transport_url'] = insecure_transport_url( - cells[0]['transport_url']) - cells[1]['transport_url'] = insecure_transport_url( - cells[1]['transport_url']) - for i, cell in enumerate(cells): - cell['capabilities'] = self.fake_capabilities[i] - return cells - - -class CellsTestV21(BaseCellsTest): - cell_extension = 'os_compute_api:os-cells' - bad_request = exception.ValidationError - - def _get_cell_controller(self): - return cells_ext_v21.CellsController() - - def _get_request(self, resource): - return fakes.HTTPRequest.blank('/v2/fake/' + resource) - - def setUp(self): - super(CellsTestV21, self).setUp() - self.controller = self._get_cell_controller() - self.context = context.get_admin_context() - self.flags(enable=True, group='cells') - - def test_index(self): - req = self._get_request("cells") - res_dict = self.controller.index(req) - - self.assertEqual(len(res_dict['cells']), 2) - for i, cell in enumerate(res_dict['cells']): - self.assertEqual(cell['name'], self.fake_cells[i]['name']) - self.assertNotIn('capabilities', cell) - self.assertNotIn('password', cell) - - def test_index_offset_and_limit(self): - req = self._get_request('cells?offset=1&limit=1') - res_dict = self.controller.index(req) - - self.assertEqual(len(res_dict['cells']), 1) - cell = res_dict['cells'][0] - self.assertEqual(cell['name'], self.fake_cells[1]['name']) - self.assertNotIn('capabilities', cell) - self.assertNotIn('password', cell) - - def test_detail(self): - req = self._get_request("cells/detail") - res_dict = self.controller.detail(req) - - self.assertEqual(len(res_dict['cells']), 2) - for i, cell in enumerate(res_dict['cells']): - self.assertEqual(cell['name'], self.fake_cells[i]['name']) - self.assertEqual(cell['capabilities'], self.fake_capabilities[i]) - self.assertNotIn('password', cell) - - def test_detail_offset_and_limit(self): - req = self._get_request("cells/detail?offset=1&limit=1") - res_dict = self.controller.detail(req) - - self.assertEqual(len(res_dict['cells']), 1) - cell = res_dict['cells'][0] - self.assertEqual(cell['name'], self.fake_cells[1]['name']) - self.assertEqual(cell['capabilities'], self.fake_capabilities[1]) - self.assertNotIn('password', cell) - - def test_show_bogus_cell_raises(self): - req = self._get_request("cells/bogus") - self.assertRaises(exc.HTTPNotFound, self.controller.show, req, 'bogus') - - def test_get_cell_by_name(self): - req = self._get_request("cells/cell1") - res_dict = self.controller.show(req, 'cell1') - cell = res_dict['cell'] - - self.assertEqual(cell['name'], 'cell1') - self.assertEqual(cell['rpc_host'], 'r1.example.org') - self.assertNotIn('password', cell) - - def _cell_delete(self): - call_info = {'delete_called': 0} - - def fake_cell_delete(inst, context, cell_name): - self.assertEqual(cell_name, 'cell999') - call_info['delete_called'] += 1 - - self.stub_out('nova.cells.rpcapi.CellsAPI.cell_delete', - fake_cell_delete) - - req = self._get_request("cells/cell999") - req.environ['nova.context'] = self.context - self.controller.delete(req, 'cell999') - self.assertEqual(call_info['delete_called'], 1) - - def test_cell_delete(self): - # Test cell delete with just cell policy - rules = {"default": "is_admin:true", - self.cell_extension: "is_admin:true"} - self.policy.set_rules(rules) - self._cell_delete() - - def test_cell_delete_with_delete_policy(self): - self._cell_delete() - - def test_delete_bogus_cell_raises(self): - def fake_cell_delete(inst, context, cell_name): - return 0 - - self.stub_out('nova.cells.rpcapi.CellsAPI.cell_delete', - fake_cell_delete) - - req = self._get_request("cells/cell999") - req.environ['nova.context'] = self.context - self.assertRaises(exc.HTTPNotFound, self.controller.delete, req, - 'cell999') - - def test_cell_delete_fails_for_invalid_policy(self): - def fake_cell_delete(inst, context, cell_name): - pass - - self.stub_out('nova.cells.rpcapi.CellsAPI.cell_delete', - fake_cell_delete) - - req = self._get_request("cells/cell999") - req.environ['nova.context'] = self.context - req.environ["nova.context"].is_admin = False - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.delete, req, 'cell999') - - def _cell_create_parent(self): - body = {'cell': {'name': 'meow', - 'username': 'fred', - 'password': 'fubar', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - res_dict = self.controller.create(req, body=body) - cell = res_dict['cell'] - - self.assertEqual(cell['name'], 'meow') - self.assertEqual(cell['username'], 'fred') - self.assertEqual(cell['rpc_host'], 'r3.example.org') - self.assertEqual(cell['type'], 'parent') - self.assertNotIn('password', cell) - self.assertNotIn('is_parent', cell) - - def test_cell_create_parent(self): - # Test create with just cells policy - rules = {"default": "is_admin:true", - self.cell_extension: "is_admin:true"} - self.policy.set_rules(rules) - self._cell_create_parent() - - def test_cell_create_parent_with_create_policy(self): - self._cell_create_parent() - - def _cell_create_child(self): - body = {'cell': {'name': 'meow', - 'username': 'fred', - 'password': 'fubar', - 'rpc_host': 'r3.example.org', - 'type': 'child'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - res_dict = self.controller.create(req, body=body) - cell = res_dict['cell'] - - self.assertEqual(cell['name'], 'meow') - self.assertEqual(cell['username'], 'fred') - self.assertEqual(cell['rpc_host'], 'r3.example.org') - self.assertEqual(cell['type'], 'child') - self.assertNotIn('password', cell) - self.assertNotIn('is_parent', cell) - - def test_cell_create_child(self): - # Test create with just cells policy - rules = {"default": "is_admin:true", - self.cell_extension: "is_admin:true"} - self.policy.set_rules(rules) - self._cell_create_child() - - def test_cell_create_child_with_create_policy(self): - self._cell_create_child() - - def test_cell_create_no_name_raises(self): - body = {'cell': {'username': 'moocow', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.create, req, body=body) - - def test_cell_create_name_empty_string_raises(self): - body = {'cell': {'name': '', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.create, req, body=body) - - def test_cell_create_name_with_invalid_character_raises(self): - body = {'cell': {'name': 'moo\x00cow', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.create, req, body=body) - - def test_cell_create_name_with_dot_raises(self): - body = {'cell': {'name': 'moo.cow', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, self.controller.create, - req, body=body) - - def test_cell_create_name_with_exclamation_point_raises(self): - body = {'cell': {'name': 'moo!cow', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, self.controller.create, - req, body=body) - - def test_cell_create_name_with_at_raises(self): - body = {'cell': {'name': 'moo@cow', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, self.controller.create, - req, body=body) - - def test_cell_create_name_with_leading_trailing_spaces(self): - body = {'cell': {'name': ' moocow ', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, self.controller.create, - req, body=body) - - def test_cell_create_name_with_leading_trailing_spaces_compat_mode(self): - body = {'cell': {'name': ' moocow ', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - req.set_legacy_v2() - resp = self.controller.create(req, body=body) - self.assertEqual('moocow', resp['cell']['name']) - - def test_cell_create_name_with_invalid_type_raises(self): - body = {'cell': {'name': 'moocow', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'type': 'invalid'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.create, req, body=body) - - def test_cell_create_fails_for_invalid_policy(self): - body = {'cell': {'name': 'fake'}} - req = self._get_request("cells") - req.environ['nova.context'] = self.context - req.environ['nova.context'].is_admin = False - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.create, req, body=body) - - def test_cell_create_rpc_port_with_string(self): - body = {'cell': {'name': 'fake', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'rpc_port': '123', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.controller.create(req, body=body) - - def test_cell_create_rpc_port_with_null(self): - body = {'cell': {'name': 'fake', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'rpc_port': None, - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.create, req, body=body) - - def test_cell_create_rpc_port_empty_string_raises(self): - body = {'cell': {'name': 'moocow', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'rpc_port': '', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.create, req, body=body) - - def _cell_update(self): - body = {'cell': {'username': 'zeb', - 'password': 'sneaky'}} - - req = self._get_request("cells/cell1") - req.environ['nova.context'] = self.context - res_dict = self.controller.update(req, 'cell1', body=body) - cell = res_dict['cell'] - - self.assertEqual(cell['name'], 'cell1') - self.assertEqual(cell['rpc_host'], 'r1.example.org') - self.assertEqual(cell['username'], 'zeb') - self.assertNotIn('password', cell) - - def test_cell_update(self): - # Test cell update with just cell policy - rules = {"default": "is_admin:true", - self.cell_extension: "is_admin:true"} - self.policy.set_rules(rules) - self._cell_update() - - def test_cell_update_with_update_policy(self): - self._cell_update() - - def test_cell_update_fails_for_invalid_policy(self): - body = {'cell': {'name': 'got_changed'}} - req = self._get_request("cells/cell1") - req.environ['nova.context'] = self.context - req.environ['nova.context'].is_admin = False - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.create, req, body=body) - - def test_cell_update_empty_name_raises(self): - body = {'cell': {'name': '', - 'username': 'zeb', - 'password': 'sneaky'}} - - req = self._get_request("cells/cell1") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.update, req, 'cell1', body=body) - - def test_cell_update_empty_rpc_port_raises(self): - body = {'cell': {'name': 'fake', - 'username': 'zeb', - 'password': 'sneaky', - 'rpc_port': ''}} - - req = self._get_request("cells/cell1") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.update, req, 'cell1', body=body) - - def test_cell_update_invalid_type_raises(self): - body = {'cell': {'username': 'zeb', - 'type': 'invalid', - 'password': 'sneaky'}} - - req = self._get_request("cells/cell1") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.update, req, 'cell1', body=body) - - def test_cell_update_without_type_specified(self): - body = {'cell': {'username': 'wingwj'}} - - req = self._get_request("cells/cell1") - req.environ['nova.context'] = self.context - res_dict = self.controller.update(req, 'cell1', body=body) - cell = res_dict['cell'] - - self.assertEqual(cell['name'], 'cell1') - self.assertEqual(cell['rpc_host'], 'r1.example.org') - self.assertEqual(cell['username'], 'wingwj') - self.assertEqual(cell['type'], 'parent') - - def test_cell_update_with_type_specified(self): - body1 = {'cell': {'username': 'wingwj', 'type': 'child'}} - body2 = {'cell': {'username': 'wingwj', 'type': 'parent'}} - - req1 = self._get_request("cells/cell1") - req1.environ['nova.context'] = self.context - res_dict1 = self.controller.update(req1, 'cell1', body=body1) - cell1 = res_dict1['cell'] - - req2 = self._get_request("cells/cell2") - req2.environ['nova.context'] = self.context - res_dict2 = self.controller.update(req2, 'cell2', body=body2) - cell2 = res_dict2['cell'] - - self.assertEqual(cell1['name'], 'cell1') - self.assertEqual(cell1['rpc_host'], 'r1.example.org') - self.assertEqual(cell1['username'], 'wingwj') - self.assertEqual(cell1['type'], 'child') - - self.assertEqual(cell2['name'], 'cell2') - self.assertEqual(cell2['rpc_host'], 'r2.example.org') - self.assertEqual(cell2['username'], 'wingwj') - self.assertEqual(cell2['type'], 'parent') - - def test_cell_update_rpc_port_with_string(self): - body = {'cell': {'name': 'fake', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'rpc_port': '123', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.controller.update(req, 'cell1', body=body) - - def test_cell_update_rpc_port_with_null(self): - body = {'cell': {'name': 'fake', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'rpc_port': None, - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.update, req, 'cell1', body=body) - - def test_cell_update_rpc_port_empty_string_raises(self): - body = {'cell': {'name': 'moocow', - 'username': 'fred', - 'password': 'secret', - 'rpc_host': 'r3.example.org', - 'rpc_port': '', - 'type': 'parent'}} - - req = self._get_request("cells") - req.environ['nova.context'] = self.context - self.assertRaises(self.bad_request, - self.controller.update, req, 'cell1', body=body) - - def test_cell_info(self): - caps = ['cap1=a;b', 'cap2=c;d'] - self.flags(name='darksecret', capabilities=caps, group='cells') - - req = self._get_request("cells/info") - res_dict = self.controller.info(req) - cell = res_dict['cell'] - cell_caps = cell['capabilities'] - - self.assertEqual(cell['name'], 'darksecret') - self.assertEqual(cell_caps['cap1'], 'a;b') - self.assertEqual(cell_caps['cap2'], 'c;d') - - def test_show_capacities(self): - response = {"ram_free": - {"units_by_mb": {"8192": 0, "512": 13, - "4096": 1, "2048": 3, "16384": 0}, - "total_mb": 7680}, - "disk_free": - {"units_by_mb": {"81920": 11, "20480": 46, - "40960": 23, "163840": 5, "0": 0}, - "total_mb": 1052672} - } - - with mock.patch.object(self.controller.cells_rpcapi, 'get_capacities', - return_value=response) as mock_get_capacities: - req = self._get_request("cells/capacities") - req.environ["nova.context"] = self.context - res_dict = self.controller.capacities(req) - self.assertEqual(response, res_dict['cell']['capacities']) - mock_get_capacities.assert_called_once_with( - self.context, cell_name=None) - - def test_show_capacity_fails_with_non_admin_context(self): - rules = {self.cell_extension: "is_admin:true"} - self.policy.set_rules(rules) - - req = self._get_request("cells/capacities") - req.environ["nova.context"] = self.context - req.environ["nova.context"].is_admin = False - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.capacities, req) - - def test_show_capacities_for_invalid_cell(self): - with mock.patch.object(self.controller.cells_rpcapi, 'get_capacities', - side_effect=exception.CellNotFound(cell_name="invalid_cell") - ) as mock_get_capacities: - req = self._get_request("cells/invalid_cell/capacities") - req.environ["nova.context"] = self.context - self.assertRaises(exc.HTTPNotFound, - self.controller.capacities, req, "invalid_cell") - - mock_get_capacities.assert_called_once_with( - self.context, cell_name="invalid_cell") - - def test_show_capacities_for_cell(self): - response = {"ram_free": - {"units_by_mb": {"8192": 0, "512": 13, - "4096": 1, "2048": 3, "16384": 0}, - "total_mb": 7680}, - "disk_free": - {"units_by_mb": {"81920": 11, "20480": 46, - "40960": 23, "163840": 5, "0": 0}, - "total_mb": 1052672} - } - - with mock.patch.object(self.controller.cells_rpcapi, 'get_capacities', - return_value=response) as mock_get_capacities: - req = self._get_request("cells/capacities") - req.environ["nova.context"] = self.context - res_dict = self.controller.capacities(req, 'cell_name') - self.assertEqual(response, res_dict['cell']['capacities']) - - mock_get_capacities.assert_called_once_with( - self.context, cell_name='cell_name') - - def test_sync_instances(self): - call_info = {} - - def sync_instances(self, context, **kwargs): - call_info['project_id'] = kwargs.get('project_id') - call_info['updated_since'] = kwargs.get('updated_since') - call_info['deleted'] = kwargs.get('deleted') - - self.stub_out('nova.cells.rpcapi.CellsAPI.sync_instances', - sync_instances) - - req = self._get_request("cells/sync_instances") - req.environ['nova.context'] = self.context - body = {} - self.controller.sync_instances(req, body=body) - self.assertIsNone(call_info['project_id']) - self.assertIsNone(call_info['updated_since']) - - body = {'project_id': 'test-project'} - self.controller.sync_instances(req, body=body) - self.assertEqual(call_info['project_id'], 'test-project') - self.assertIsNone(call_info['updated_since']) - - expected = timeutils.utcnow().isoformat() - if not expected.endswith("+00:00"): - expected += "+00:00" - - body = {'updated_since': expected} - self.controller.sync_instances(req, body=body) - self.assertIsNone(call_info['project_id']) - self.assertEqual(call_info['updated_since'], expected) - - body = {'updated_since': 'skjdfkjsdkf'} - self.assertRaises(self.bad_request, - self.controller.sync_instances, req, body=body) - - body = {'deleted': False} - self.controller.sync_instances(req, body=body) - self.assertIsNone(call_info['project_id']) - self.assertIsNone(call_info['updated_since']) - self.assertFalse(call_info['deleted']) - - body = {'deleted': 'False'} - self.controller.sync_instances(req, body=body) - self.assertIsNone(call_info['project_id']) - self.assertIsNone(call_info['updated_since']) - self.assertFalse(call_info['deleted']) - - body = {'deleted': 'True'} - self.controller.sync_instances(req, body=body) - self.assertIsNone(call_info['project_id']) - self.assertIsNone(call_info['updated_since']) - self.assertTrue(call_info['deleted']) - - body = {'deleted': 'foo'} - self.assertRaises(self.bad_request, - self.controller.sync_instances, req, body=body) - - body = {'foo': 'meow'} - self.assertRaises(self.bad_request, - self.controller.sync_instances, req, body=body) - - def test_sync_instances_fails_for_invalid_policy(self): - def sync_instances(self, context, **kwargs): - pass - - self.stub_out('nova.cells.rpcapi.CellsAPI.sync_instances', - sync_instances) - - req = self._get_request("cells/sync_instances") - req.environ['nova.context'] = self.context - req.environ['nova.context'].is_admin = False - - body = {} - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.sync_instances, req, body=body) - - def test_cells_disabled(self): - self.flags(enable=False, group='cells') - - req = self._get_request("cells") - self.assertRaises(exc.HTTPNotImplemented, - self.controller.index, req) - - req = self._get_request("cells/detail") - self.assertRaises(exc.HTTPNotImplemented, - self.controller.detail, req) - - req = self._get_request("cells/cell1") - self.assertRaises(exc.HTTPNotImplemented, - self.controller.show, req) - - self.assertRaises(exc.HTTPNotImplemented, - self.controller.delete, req, 'cell999') - - req = self._get_request("cells/cells") - self.assertRaises(exc.HTTPNotImplemented, - self.controller.create, req, {}) - - req = self._get_request("cells/capacities") - self.assertRaises(exc.HTTPNotImplemented, - self.controller.capacities, req) - - req = self._get_request("cells/sync_instances") - self.assertRaises(exc.HTTPNotImplemented, - self.controller.sync_instances, req, {}) diff --git a/nova/tests/unit/fake_policy.py b/nova/tests/unit/fake_policy.py index fb43e28114cb..688db4e736a3 100644 --- a/nova/tests/unit/fake_policy.py +++ b/nova/tests/unit/fake_policy.py @@ -27,7 +27,6 @@ policy_data = """ "os_compute_api:os-agents": "", "os_compute_api:os-attach-interfaces": "", "os_compute_api:os-baremetal-nodes": "", - "os_compute_api:os-cells": "", "os_compute_api:os-console-output": "", "os_compute_api:os-remote-consoles": "", "os_compute_api:os-consoles:create": "", diff --git a/nova/tests/unit/test_api_validation.py b/nova/tests/unit/test_api_validation.py index bb2e74ab2a4f..c71266c3ce61 100644 --- a/nova/tests/unit/test_api_validation.py +++ b/nova/tests/unit/test_api_validation.py @@ -75,15 +75,6 @@ class FakeRequest(object): class ValidationRegex(test.NoDBTestCase): - def test_cell_names(self): - cellre = re.compile(parameter_types.valid_cell_name_regex.regex) - self.assertTrue(cellre.search('foo')) - self.assertFalse(cellre.search('foo.bar')) - self.assertFalse(cellre.search('foo@bar')) - self.assertFalse(cellre.search('foo!bar')) - self.assertFalse(cellre.search(' foo!bar')) - self.assertFalse(cellre.search('\nfoo!bar')) - def test_build_regex_range(self): # this is much easier to think about if we only use the ascii # subset because it's a printable range we can think @@ -183,12 +174,6 @@ class FormatCheckerTestCase(test.NoDBTestCase): self._format_checker("name", " ", error_message) self._format_checker("name", None, error_message) - def test_format_checker_failed_with_non_string_cell_name(self): - error_message = ("An invalid 'name' value was provided. " - "The name must be: printable characters except " - "!, ., @. Can not start or end with whitespace.") - self._format_checker("cell_name", None, error_message) - def test_format_checker_failed_name_with_leading_trailing_spaces(self): error_message = ("An invalid 'name' value was provided. " "The name must be: printable characters with at " @@ -196,14 +181,6 @@ class FormatCheckerTestCase(test.NoDBTestCase): self._format_checker("name_with_leading_trailing_spaces", None, error_message) - def test_format_checker_failed_cell_name_with_leading_trailing_spaces( - self): - error_message = ("An invalid 'name' value was provided. " - "The name must be: printable characters except" - " !, ., @, with at least one non space character") - self._format_checker("cell_name_with_leading_trailing_spaces", - None, error_message) - class MicroversionsSchemaTestCase(APIValidationTestCase): @@ -757,106 +734,6 @@ class HostnameIPaddressTestCase(APIValidationTestCase): expected_detail=detail) -class CellNameTestCase(APIValidationTestCase): - - post_schema = { - 'type': 'object', - 'properties': { - 'foo': parameter_types.cell_name, - }, - } - - def test_validate_name(self): - self.assertEqual('Validation succeeded.', - self.post(body={'foo': 'abc'}, - req=FakeRequest())) - self.assertEqual('Validation succeeded.', - self.post(body={'foo': 'my server'}, - req=FakeRequest())) - self.assertEqual('Validation succeeded.', - self.post(body={'foo': u'\u0434'}, req=FakeRequest())) - self.assertEqual('Validation succeeded.', - self.post(body={'foo': u'\u0434\u2006\ufffd'}, - req=FakeRequest())) - - def test_validate_name_fails(self): - error = ("An invalid 'name' value was provided. The name must be: " - "printable characters except !, ., @. " - "Can not start or end with whitespace.") - - should_fail = (' ', - ' server', - 'server ', - u'a\xa0', # trailing unicode space - u'\uffff', # non-printable unicode - 'abc!def', - 'abc.def', - 'abc@def') - - for item in should_fail: - self.check_validation_error(self.post, body={'foo': item}, - expected_detail=error) - - # four-byte unicode, if supported by this python build - try: - self.check_validation_error(self.post, body={'foo': u'\U00010000'}, - expected_detail=error) - except ValueError: - pass - - -class CellNameLeadingTrailingSpacesTestCase(APIValidationTestCase): - - post_schema = { - 'type': 'object', - 'properties': { - 'foo': parameter_types.cell_name_leading_trailing_spaces, - }, - } - - def test_validate_name(self): - self.assertEqual('Validation succeeded.', - self.post(body={'foo': 'abc'}, - req=FakeRequest())) - self.assertEqual('Validation succeeded.', - self.post(body={'foo': 'my server'}, - req=FakeRequest())) - self.assertEqual('Validation succeeded.', - self.post(body={'foo': u'\u0434'}, req=FakeRequest())) - self.assertEqual('Validation succeeded.', - self.post(body={'foo': u'\u0434\u2006\ufffd'}, - req=FakeRequest())) - self.assertEqual('Validation succeeded.', - self.post(body={'foo': ' my server'}, - req=FakeRequest())) - self.assertEqual('Validation succeeded.', - self.post(body={'foo': 'my server '}, - req=FakeRequest())) - - def test_validate_name_fails(self): - error = ("An invalid 'name' value was provided. The name must be: " - "printable characters except !, ., @, " - "with at least one non space character") - - should_fail = ( - ' ', - u'\uffff', # non-printable unicode - 'abc!def', - 'abc.def', - 'abc@def') - - for item in should_fail: - self.check_validation_error(self.post, body={'foo': item}, - expected_detail=error) - - # four-byte unicode, if supported by this python build - try: - self.check_validation_error(self.post, body={'foo': u'\U00010000'}, - expected_detail=error) - except ValueError: - pass - - class NameTestCase(APIValidationTestCase): post_schema = { diff --git a/nova/tests/unit/test_policy.py b/nova/tests/unit/test_policy.py index 26be698cc9a6..085199493fd8 100644 --- a/nova/tests/unit/test_policy.py +++ b/nova/tests/unit/test_policy.py @@ -296,11 +296,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase): "os_compute_api:os-aggregates:set_metadata", "os_compute_api:os-agents", "os_compute_api:os-baremetal-nodes", -"os_compute_api:os-cells", -"os_compute_api:os-cells:create", -"os_compute_api:os-cells:delete", -"os_compute_api:os-cells:update", -"os_compute_api:os-cells:sync_instances", "os_compute_api:os-evacuate", "os_compute_api:os-extended-server-attributes", "os_compute_api:os-flavor-access:remove_tenant_access", diff --git a/releasenotes/notes/remove-cells-v1-055028c270d06680.yaml b/releasenotes/notes/remove-cells-v1-055028c270d06680.yaml index 165476f7917f..d19915b7227f 100644 --- a/releasenotes/notes/remove-cells-v1-055028c270d06680.yaml +++ b/releasenotes/notes/remove-cells-v1-055028c270d06680.yaml @@ -2,4 +2,18 @@ upgrade: - | The *cells v1* feature has been deprecated since the 16.0.0 Pike release - and has now been removed. The ``nova-cells`` service has been removed. + and has now been removed. The ``nova-cells`` service has been removed. The + *cells v1* specific REST APIs have been removed along with their related + policy rules. Calling these APIs will now result in a ``410 (Gone)`` error + response. + + * ``GET /os-cells`` + * ``POST /os-cells`` + * ``GET /os-cells/capacities`` + * ``GET /os-cells/detail`` + * ``GET /os-cells/info`` + * ``POST /os-cells/sync_instances`` + * ``GET /os-cells/{cell_id}`` + * ``PUT /os-cells/{cell_id}`` + * ``DELETE /os-cells/{cell_id}`` + * ``GET /os-cells/{cell_id}/capacities``