Remove support for /os-floating-ips-bulk REST API

Drop support for the os-floating-ips-bulk API which has been deprecated
since Newton:

  Idca478c566f9a7b5b30a3172453ce7c66d9fd8f0

This API now returns a 410 response for all routes.

Unit tests are removed and the functional API sample tests are just
asserting the 410 response now.

The API sample docs are left intact since the API reference still builds
from those and can be considered more or less branchless, so people
looking at the API reference can apply it to older deployments of nova
before os-floating-ips-bulk was removed.

The release note added for previous nova-network API removals is
amended to note this additional change.

Part of blueprint remove-nova-network

Change-Id: I89d081108b398d8efba9636279088c61349b21e6
Depends-On: https://review.openstack.org/582945
This commit is contained in:
Stephen Finucane 2018-07-16 10:43:14 +01:00 committed by Matt Riedemann
parent 50f40854b0
commit 823c4e840d
17 changed files with 42 additions and 566 deletions

View File

@ -63,7 +63,6 @@ the `API guide <http://developer.openstack.org/api-guide/compute/index.html>`_.
.. include:: os-floating-ip-dns.inc
.. include:: os-floating-ip-pools.inc
.. include:: os-floating-ips.inc
.. include:: os-floating-ips-bulk.inc
.. include:: os-security-groups.inc
.. include:: os-security-group-default-rules.inc
.. include:: os-security-group-rules.inc
@ -81,3 +80,4 @@ Compute API in the past, but no longer exist.
.. include:: os-fping.inc
.. include:: os-virtual-interfaces.inc
.. include:: os-fixed-ips.inc
.. include:: os-floating-ips-bulk.inc

View File

@ -8,6 +8,7 @@
Since these APIs are only implemented for **nova-network**, they are
deprecated. These will fail with a 404 starting from microversion 2.36.
It was removed in the 18.0.0 Rocky release.
Bulk-creates, deletes, and lists floating IPs.
Default pool name is ``nova``.
@ -24,7 +25,8 @@ Lists all floating IPs.
Normal response codes: 200
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404)
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404),
gone(410)
Response
--------
@ -54,7 +56,7 @@ Bulk-creates floating IPs.
Normal response codes: 200
Error response codes: badRequest(400), unauthorized(401), forbidden(403),
conflict(409)
conflict(409), gone(410)
Request
-------
@ -96,7 +98,8 @@ Bulk-deletes floating IPs.
Normal response codes: 200
Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404)
Error response codes: badRequest(400), unauthorized(401), forbidden(403),
itemNotFound(404), gone(410)
Request
-------
@ -131,7 +134,8 @@ Lists all floating IPs for a host.
Normal response codes: 200
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404)
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404),
gone(410)
Request
-------

View File

@ -12,141 +12,25 @@
# License for the specific language governing permissions and limitations
# under the License.
import netaddr
import six
import webob.exc
from webob import exc
from nova.api.openstack.api_version_request \
import MAX_PROXY_API_SUPPORT_VERSION
from nova.api.openstack.compute.schemas import floating_ips_bulk
from nova.api.openstack import wsgi
from nova.api import validation
import nova.conf
from nova import exception
from nova.i18n import _
from nova import objects
from nova.policies import floating_ips_bulk as fib_policies
CONF = nova.conf.CONF
class FloatingIPBulkController(wsgi.Controller):
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.expected_errors(404)
@wsgi.expected_errors(410)
def index(self, req):
"""Return a list of all floating IPs."""
context = req.environ['nova.context']
context.can(fib_policies.BASE_POLICY_NAME)
raise exc.HTTPGone()
return self._get_floating_ip_info(context)
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.expected_errors(404)
@wsgi.expected_errors(410)
def show(self, req, id):
"""Return a list of all floating IPs for a given host."""
context = req.environ['nova.context']
context.can(fib_policies.BASE_POLICY_NAME)
raise exc.HTTPGone()
return self._get_floating_ip_info(context, id)
def _get_floating_ip_info(self, context, host=None):
floating_ip_info = {"floating_ip_info": []}
if host is None:
try:
floating_ips = objects.FloatingIPList.get_all(context)
except exception.NoFloatingIpsDefined:
return floating_ip_info
else:
try:
floating_ips = objects.FloatingIPList.get_by_host(context,
host)
except exception.FloatingIpNotFoundForHost as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
for floating_ip in floating_ips:
instance_uuid = None
fixed_ip = None
if floating_ip.fixed_ip:
instance_uuid = floating_ip.fixed_ip.instance_uuid
fixed_ip = str(floating_ip.fixed_ip.address)
result = {'address': str(floating_ip.address),
'pool': floating_ip.pool,
'interface': floating_ip.interface,
'project_id': floating_ip.project_id,
'instance_uuid': instance_uuid,
'fixed_ip': fixed_ip}
floating_ip_info['floating_ip_info'].append(result)
return floating_ip_info
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.expected_errors((400, 409))
@validation.schema(floating_ips_bulk.create)
@wsgi.expected_errors(410)
def create(self, req, body):
"""Bulk create floating IPs."""
context = req.environ['nova.context']
context.can(fib_policies.BASE_POLICY_NAME)
raise exc.HTTPGone()
params = body['floating_ips_bulk_create']
ip_range = params['ip_range']
pool = params.get('pool', CONF.default_floating_pool)
interface = params.get('interface', CONF.public_interface)
try:
ips = [objects.FloatingIPList.make_ip_info(addr, pool, interface)
for addr in self._address_to_hosts(ip_range)]
except exception.InvalidInput as exc:
raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
try:
objects.FloatingIPList.create(context, ips)
except exception.FloatingIpExists as exc:
raise webob.exc.HTTPConflict(explanation=exc.format_message())
return {"floating_ips_bulk_create": {"ip_range": ip_range,
"pool": pool,
"interface": interface}}
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.expected_errors((400, 404))
@validation.schema(floating_ips_bulk.delete)
@wsgi.expected_errors(410)
def update(self, req, id, body):
"""Bulk delete floating IPs."""
context = req.environ['nova.context']
context.can(fib_policies.BASE_POLICY_NAME)
if id != "delete":
msg = _("Unknown action")
raise webob.exc.HTTPNotFound(explanation=msg)
ip_range = body['ip_range']
try:
ips = (objects.FloatingIPList.make_ip_info(address, None, None)
for address in self._address_to_hosts(ip_range))
except exception.InvalidInput as exc:
raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
objects.FloatingIPList.destroy(context, ips)
return {"floating_ips_bulk_delete": ip_range}
def _address_to_hosts(self, addresses):
"""Iterate over hosts within an address range.
If an explicit range specifier is missing, the parameter is
interpreted as a specific individual address.
"""
try:
return [netaddr.IPAddress(addresses)]
except ValueError:
net = netaddr.IPNetwork(addresses)
if net.size < 4:
reason = _("/%s should be specified as single address(es) "
"not in cidr format") % net.prefixlen
raise exception.InvalidInput(reason=reason)
else:
return net.iter_hosts()
except netaddr.AddrFormatError as exc:
raise exception.InvalidInput(reason=six.text_type(exc))
raise exc.HTTPGone()

View File

@ -1,52 +0,0 @@
# Copyright 2014 IBM 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.
ip_range = {
# TODO(eliqiao) need to find a better pattern
'type': 'string',
'pattern': '^[0-9./a-fA-F]*$',
}
create = {
'type': 'object',
'properties': {
'floating_ips_bulk_create': {
'type': 'object',
'properties': {
'ip_range': ip_range,
'pool': {
'type': 'string', 'minLength': 1, 'maxLength': 255,
},
'interface': {
'type': 'string', 'minLength': 1, 'maxLength': 255,
},
},
'required': ['ip_range'],
'additionalProperties': False,
},
},
'required': ['floating_ips_bulk_create'],
'additionalProperties': False,
}
delete = {
'type': 'object',
'properties': {
'ip_range': ip_range,
},
'required': ['ip_range'],
'additionalProperties': False,
}

View File

@ -44,7 +44,6 @@ from nova.policies import flavors
from nova.policies import floating_ip_dns
from nova.policies import floating_ip_pools
from nova.policies import floating_ips
from nova.policies import floating_ips_bulk
from nova.policies import hide_server_addresses
from nova.policies import hosts
from nova.policies import hypervisors
@ -119,7 +118,6 @@ def list_rules():
floating_ip_dns.list_rules(),
floating_ip_pools.list_rules(),
floating_ips.list_rules(),
floating_ips_bulk.list_rules(),
hide_server_addresses.list_rules(),
hosts.list_rules(),
hypervisors.list_rules(),

View File

@ -1,51 +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-floating-ips-bulk'
floating_ips_bulk_policies = [
policy.DocumentedRuleDefault(
BASE_POLICY_NAME,
base.RULE_ADMIN_API,
"Bulk-create, delete, and list floating IPs. API is deprecated.",
[
{
'method': 'GET',
'path': '/os-floating-ips-bulk'
},
{
'method': 'POST',
'path': '/os-floating-ips-bulk'
},
{
'method': 'PUT',
'path': '/os-floating-ips-bulk/delete'
},
{
'method': 'GET',
'path': '/os-floating-ips-bulk/{host_name}'
},
]),
]
def list_rules():
return floating_ips_bulk_policies

View File

@ -1,7 +0,0 @@
{
"floating_ips_bulk_create": {
"ip_range": "%(ip_range)s",
"pool": "%(pool)s",
"interface": "%(interface)s"
}
}

View File

@ -1,7 +0,0 @@
{
"floating_ips_bulk_create": {
"interface": "eth0",
"ip_range": "192.168.1.0/24",
"pool": "nova"
}
}

View File

@ -1,3 +0,0 @@
{
"floating_ips_bulk_delete": "192.168.1.0/24"
}

View File

@ -1,12 +0,0 @@
{
"floating_ip_info": [
{
"address": "10.10.10.3",
"instance_uuid": null,
"interface": "eth0",
"pool": "nova",
"fixed_ip": null,
"project_id": null
}
]
}

View File

@ -1,28 +0,0 @@
{
"floating_ip_info": [
{
"address": "10.10.10.1",
"instance_uuid": null,
"interface": "eth0",
"pool": "nova",
"fixed_ip": null,
"project_id": null
},
{
"address": "10.10.10.2",
"instance_uuid": null,
"interface": "eth0",
"pool": "nova",
"fixed_ip": null,
"project_id": null
},
{
"address": "10.10.10.3",
"instance_uuid": null,
"interface": "eth0",
"pool": "nova",
"fixed_ip": null,
"project_id": null
}
]
}

View File

@ -14,7 +14,7 @@
# under the License.
import nova.conf
from nova import context
from nova.tests.functional.api import client as api_client
from nova.tests.functional.api_sample_tests import api_sample_base
CONF = nova.conf.CONF
@ -22,61 +22,30 @@ CONF = nova.conf.CONF
class FloatingIpsBulkTest(api_sample_base.ApiSampleTestBaseV21):
ADMIN_API = True
sample_dir = "os-floating-ips-bulk"
def setUp(self):
super(FloatingIpsBulkTest, self).setUp()
pool = CONF.default_floating_pool
interface = CONF.public_interface
self.ip_pool = [
{
'address': "10.10.10.1",
'pool': pool,
'interface': interface,
'host': None
},
{
'address': "10.10.10.2",
'pool': pool,
'interface': interface,
'host': None
},
{
'address': "10.10.10.3",
'pool': pool,
'interface': interface,
'host': "testHost"
},
]
self.compute.db.floating_ip_bulk_create(
context.get_admin_context(), self.ip_pool)
self.addCleanup(self.compute.db.floating_ip_bulk_destroy,
context.get_admin_context(), self.ip_pool)
def test_floating_ips_bulk_list(self):
response = self._do_get('os-floating-ips-bulk')
self._verify_response('floating-ips-bulk-list-resp',
{}, response, 200)
ex = self.assertRaises(api_client.OpenStackApiException,
self.api.api_get, 'os-floating-ips-bulk')
self.assertEqual(410, ex.response.status_code)
def test_floating_ips_bulk_list_by_host(self):
response = self._do_get('os-floating-ips-bulk/testHost')
self._verify_response('floating-ips-bulk-list-by-host-resp',
{}, response, 200)
ex = self.assertRaises(api_client.OpenStackApiException,
self.api.api_get,
'os-floating-ips-bulk/testHost')
self.assertEqual(410, ex.response.status_code)
def test_floating_ips_bulk_create(self):
response = self._do_post('os-floating-ips-bulk',
'floating-ips-bulk-create-req',
{"ip_range": "192.168.1.0/24",
"pool": CONF.default_floating_pool,
"interface": CONF.public_interface})
self._verify_response('floating-ips-bulk-create-resp', {},
response, 200)
ex = self.assertRaises(api_client.OpenStackApiException,
self.api.api_post,
'/os-floating-ips-bulk',
{"ip_range": "192.168.1.0/24",
"pool": CONF.default_floating_pool,
"interface": CONF.public_interface})
self.assertEqual(410, ex.response.status_code)
def test_floating_ips_bulk_delete(self):
response = self._do_put('os-floating-ips-bulk/delete',
'floating-ips-bulk-delete-req',
{"ip_range": "192.168.1.0/24"})
self._verify_response('floating-ips-bulk-delete-resp', {},
response, 200)
ex = self.assertRaises(api_client.OpenStackApiException,
self.api.api_put,
'os-floating-ips-bulk/delete',
{"ip_range": "192.168.1.0/24"})
self.assertEqual(410, ex.response.status_code)

View File

@ -1,218 +0,0 @@
# Copyright 2012 IBM Corp.
#
# 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 mock
import netaddr
from oslo_config import cfg
import webob
from nova.api.openstack.compute import floating_ips_bulk \
as fipbulk_v21
from nova import context
from nova import exception
from nova import objects
from nova import test
from nova.tests.unit.api.openstack import fakes
from nova.tests import uuidsentinel as uuids
CONF = cfg.CONF
class FloatingIPBulkV21(test.TestCase):
floating_ips_bulk = fipbulk_v21
bad_request = exception.ValidationError
def setUp(self):
super(FloatingIPBulkV21, self).setUp()
self.context = context.get_admin_context()
self.controller = self.floating_ips_bulk.FloatingIPBulkController()
self.req = fakes.HTTPRequest.blank('')
def _setup_floating_ips(self, ip_range):
body = {'floating_ips_bulk_create': {'ip_range': ip_range}}
res_dict = self.controller.create(self.req, body=body)
response = {"floating_ips_bulk_create": {
'ip_range': ip_range,
'pool': CONF.default_floating_pool,
'interface': CONF.public_interface}}
self.assertEqual(res_dict, response)
def test_create_ips(self):
ip_range = '192.168.1.0/28'
self._setup_floating_ips(ip_range)
def test_create_ips_pool(self):
ip_range = '10.0.1.0/29'
pool = 'a new pool'
body = {'floating_ips_bulk_create':
{'ip_range': ip_range,
'pool': pool}}
res_dict = self.controller.create(self.req, body=body)
response = {"floating_ips_bulk_create": {
'ip_range': ip_range,
'pool': pool,
'interface': CONF.public_interface}}
self.assertEqual(res_dict, response)
def test_list_ips(self):
self._test_list_ips(self.req)
def _test_list_ips(self, req):
ip_range = '192.168.1.1/28'
self._setup_floating_ips(ip_range)
res_dict = self.controller.index(req)
ip_info = [{'address': str(ip_addr),
'pool': CONF.default_floating_pool,
'interface': CONF.public_interface,
'project_id': None,
'instance_uuid': None,
'fixed_ip': None}
for ip_addr in netaddr.IPNetwork(ip_range).iter_hosts()]
response = {'floating_ip_info': ip_info}
self.assertEqual(res_dict, response)
def test_list_ips_associated(self):
self._test_list_ips_associated(self.req)
@mock.patch('nova.objects.FloatingIPList.get_all')
def _test_list_ips_associated(self, req, mock_get):
instance_uuid = uuids.instance
fixed_address = "10.0.0.1"
floating_address = "192.168.0.1"
fixed_ip = objects.FixedIP(instance_uuid=instance_uuid,
address=fixed_address)
floating_ip = objects.FloatingIP(address=floating_address,
fixed_ip=fixed_ip,
pool=CONF.default_floating_pool,
interface=CONF.public_interface,
project_id=None)
floating_list = objects.FloatingIPList(objects=[floating_ip])
mock_get.return_value = floating_list
res_dict = self.controller.index(req)
ip_info = [{'address': floating_address,
'pool': CONF.default_floating_pool,
'interface': CONF.public_interface,
'project_id': None,
'instance_uuid': instance_uuid,
'fixed_ip': fixed_address}]
response = {'floating_ip_info': ip_info}
self.assertEqual(res_dict, response)
def test_list_ip_by_host(self):
self._test_list_ip_by_host(self.req)
def _test_list_ip_by_host(self, req):
ip_range = '192.168.1.1/28'
self._setup_floating_ips(ip_range)
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.show, req, 'host')
def test_delete_ips(self):
self._test_delete_ips(self.req)
def _test_delete_ips(self, req):
ip_range = '192.168.1.0/29'
self._setup_floating_ips(ip_range)
body = {'ip_range': ip_range}
res_dict = self.controller.update(req, "delete", body=body)
response = {"floating_ips_bulk_delete": ip_range}
self.assertEqual(res_dict, response)
# Check that the IPs are actually deleted
res_dict = self.controller.index(req)
response = {'floating_ip_info': []}
self.assertEqual(res_dict, response)
def test_create_duplicate_fail(self):
ip_range = '192.168.1.0/30'
self._setup_floating_ips(ip_range)
ip_range = '192.168.1.0/29'
body = {'floating_ips_bulk_create': {'ip_range': ip_range}}
self.assertRaises(webob.exc.HTTPConflict, self.controller.create,
self.req, body=body)
def test_create_bad_cidr_fail(self):
# netaddr can't handle /32 or 31 cidrs
ip_range = '192.168.1.1/32'
body = {'floating_ips_bulk_create': {'ip_range': ip_range}}
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
self.req, body=body)
def test_create_invalid_cidr_fail(self):
ip_range = 'not a cidr'
body = {'floating_ips_bulk_create': {'ip_range': ip_range}}
self.assertRaises(self.bad_request, self.controller.create,
self.req, body=body)
class FloatingIPBulkPolicyEnforcementV21(test.NoDBTestCase):
def setUp(self):
super(FloatingIPBulkPolicyEnforcementV21, self).setUp()
self.controller = fipbulk_v21.FloatingIPBulkController()
self.req = fakes.HTTPRequest.blank('')
def _common_policy_check(self, func, *arg, **kwarg):
rule_name = "os_compute_api:os-floating-ips-bulk"
rule = {rule_name: "project:non_fake"}
self.policy.set_rules(rule)
exc = self.assertRaises(
exception.PolicyNotAuthorized, func, *arg, **kwarg)
self.assertEqual(
"Policy doesn't allow %s to be performed." % rule_name,
exc.format_message())
def test_index_policy_failed(self):
self._common_policy_check(self.controller.index, self.req)
def test_show_ip_policy_failed(self):
self._common_policy_check(self.controller.show, self.req, "host")
def test_create_policy_failed(self):
ip_range = '192.168.1.0/28'
body = {'floating_ips_bulk_create': {'ip_range': ip_range}}
self._common_policy_check(self.controller.create, self.req, body=body)
def test_update_policy_failed(self):
ip_range = '192.168.1.0/29'
body = {'ip_range': ip_range}
self._common_policy_check(self.controller.update, self.req,
"delete", body=body)
class FloatingIPBulkDeprecationTest(test.NoDBTestCase):
def setUp(self):
super(FloatingIPBulkDeprecationTest, self).setUp()
self.controller = fipbulk_v21.FloatingIPBulkController()
self.req = fakes.HTTPRequest.blank('', version='2.36')
def test_all_apis_return_not_found(self):
self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.index, self.req)
self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.show, self.req, fakes.FAKE_UUID)
self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.create, self.req, {})
self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.update, self.req, fakes.FAKE_UUID, {})

View File

@ -51,7 +51,6 @@ policy_data = """
"os_compute_api:os-floating-ip-dns:domain:delete": "",
"os_compute_api:os-floating-ip-pools": "",
"os_compute_api:os-floating-ips": "",
"os_compute_api:os-floating-ips-bulk": "",
"os_compute_api:os-instance-actions": "",
"os_compute_api:os-instance-usage-audit-log": "",

View File

@ -311,7 +311,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
"os_compute_api:os-flavor-manage:create",
"os_compute_api:os-flavor-manage:update",
"os_compute_api:os-flavor-manage:delete",
"os_compute_api:os-floating-ips-bulk",
"os_compute_api:os-floating-ip-dns:domain:delete",
"os_compute_api:os-floating-ip-dns:domain:update",
"os_compute_api:os-hosts",

View File

@ -12,6 +12,10 @@ upgrade:
* ``GET /os-fixed-ips/{fixed_ip}``
* ``POST /os-fixed-ips/{fixed_ip}/action (reserve)``
* ``POST /os-fixed-ips/{fixed_ip}/action (unreserve)``
* ``GET /os-floating-ips-bulk``
* ``GET /os-floating-ips-bulk/{host_name}``
* ``POST /os-floating-ips-bulk``
* ``PUT /os-floating-ips-bulk/delete``
In addition, the following configuration options have been removed.