Port floating_ips_bulk extention to v2.1

This patch ports floating_ips_bulk extention from v2 to v2.1,
and have v2 unit test cases shared between v2.1 and v2.
Partially implements blueprint v2-on-v3-api

Change-Id: I9444ff9c394d530cb76dd4ac21b51cd4de7ef6dc
This commit is contained in:
Eli Qiao
2014-09-05 14:11:11 +08:00
parent 977583cc9b
commit 4bb34182fe
19 changed files with 454 additions and 23 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -158,6 +158,8 @@
"compute_extension:v3:os-floating-ip-pools:discoverable": "",
"compute_extension:floating_ips": "",
"compute_extension:floating_ips_bulk": "rule:admin_api",
"compute_extension:v3:os-floating-ips-bulk": "rule_admin_api",
"compute_extension:v3:os-floating-ips-bulk:discoverable": "",
"compute_extension:fping": "",
"compute_extension:fping:all_tenants": "rule:admin_api",
"compute_extension:v3:os-fping": "",

View File

@@ -0,0 +1,167 @@
# 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 netaddr
from oslo.config import cfg
import six
import webob.exc
from nova.api.openstack.compute.schemas.v3 import floating_ips_bulk
from nova.api.openstack import extensions
from nova.api import validation
from nova import exception
from nova.i18n import _
from nova import objects
CONF = cfg.CONF
CONF.import_opt('default_floating_pool', 'nova.network.floating_ips')
CONF.import_opt('public_interface', 'nova.network.linux_net')
ALIAS = 'os-floating-ips-bulk'
authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS)
class FloatingIPBulkController(object):
@extensions.expected_errors(404)
def index(self, req):
"""Return a list of all floating ips."""
context = req.environ['nova.context']
authorize(context)
return self._get_floating_ip_info(context)
@extensions.expected_errors(404)
def show(self, req, id):
"""Return a list of all floating ips for a given host."""
context = req.environ['nova.context']
authorize(context)
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
if floating_ip.fixed_ip:
instance_uuid = floating_ip.fixed_ip.instance_uuid
result = {'address': str(floating_ip['address']),
'pool': floating_ip['pool'],
'interface': floating_ip['interface'],
'project_id': floating_ip['project_id'],
'instance_uuid': instance_uuid}
floating_ip_info['floating_ip_info'].append(result)
return floating_ip_info
@extensions.expected_errors(400)
@validation.schema(floating_ips_bulk.create)
def create(self, req, body):
"""Bulk create floating ips."""
context = req.environ['nova.context']
authorize(context)
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.HTTPBadRequest(explanation=exc.format_message())
return {"floating_ips_bulk_create": {"ip_range": ip_range,
"pool": pool,
"interface": interface}}
@extensions.expected_errors((400, 404))
@validation.schema(floating_ips_bulk.delete)
def update(self, req, id, body):
"""Bulk delete floating IPs."""
context = req.environ['nova.context']
authorize(context)
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))
class FloatingIpsBulk(extensions.V3APIExtensionBase):
"""Bulk handling of Floating IPs."""
name = "FloatingIpsBulk"
alias = ALIAS
version = 1
def get_resources(self):
resource = [extensions.ResourceExtension(ALIAS,
FloatingIPBulkController())]
return resource
def get_controller_extensions(self):
"""It's an abstract function V3APIExtensionBase and the extension
will not be loaded without it.
"""
return []

View File

@@ -0,0 +1,52 @@
# 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

@@ -16,26 +16,34 @@ import netaddr
from oslo.config import cfg
import webob
from nova.api.openstack.compute.contrib import floating_ips_bulk
from nova.api.openstack.compute.contrib import floating_ips_bulk as fipbulk_v2
from nova.api.openstack.compute.plugins.v3 import floating_ips_bulk as\
fipbulk_v21
from nova import context
from nova import exception
from nova import test
from nova.tests.api.openstack import fakes
CONF = cfg.CONF
class FloatingIpsBulk(test.TestCase):
class FloatingIPBulkV21(test.TestCase):
floating_ips_bulk = fipbulk_v21
url = '/v2/fake/os-floating-ips-bulk'
delete_url = '/v2/fake/os-fixed-ips/delete'
bad_request = exception.ValidationError
def setUp(self):
super(FloatingIpsBulk, self).setUp()
super(FloatingIPBulkV21, self).setUp()
self.context = context.get_admin_context()
self.controller = floating_ips_bulk.FloatingIPBulkController()
self.controller = self.floating_ips_bulk.FloatingIPBulkController()
def _setup_floating_ips(self, ip_range):
body = {'floating_ips_bulk_create': {'ip_range': ip_range}}
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips-bulk')
res_dict = self.controller.create(req, body)
req = fakes.HTTPRequest.blank(self.url)
res_dict = self.controller.create(req, body=body)
response = {"floating_ips_bulk_create": {
'ip_range': ip_range,
'pool': CONF.default_floating_pool,
@@ -52,8 +60,8 @@ class FloatingIpsBulk(test.TestCase):
body = {'floating_ips_bulk_create':
{'ip_range': ip_range,
'pool': pool}}
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips-bulk')
res_dict = self.controller.create(req, body)
req = fakes.HTTPRequest.blank(self.url)
res_dict = self.controller.create(req, body=body)
response = {"floating_ips_bulk_create": {
'ip_range': ip_range,
'pool': pool,
@@ -63,8 +71,7 @@ class FloatingIpsBulk(test.TestCase):
def test_list_ips(self):
ip_range = '192.168.1.1/28'
self._setup_floating_ips(ip_range)
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips-bulk',
use_admin_context=True)
req = fakes.HTTPRequest.blank(self.url, use_admin_context=True)
res_dict = self.controller.index(req)
ip_info = [{'address': str(ip_addr),
@@ -80,8 +87,7 @@ class FloatingIpsBulk(test.TestCase):
def test_list_ip_by_host(self):
ip_range = '192.168.1.1/28'
self._setup_floating_ips(ip_range)
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips-bulk',
use_admin_context=True)
req = fakes.HTTPRequest.blank(self.url, use_admin_context=True)
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.show, req, 'host')
@@ -90,15 +96,14 @@ class FloatingIpsBulk(test.TestCase):
self._setup_floating_ips(ip_range)
body = {'ip_range': ip_range}
req = fakes.HTTPRequest.blank('/v2/fake/os-fixed-ips/delete')
res_dict = self.controller.update(req, "delete", body)
req = fakes.HTTPRequest.blank(self.delete_url)
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
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips-bulk',
use_admin_context=True)
req = fakes.HTTPRequest.blank(self.url, use_admin_context=True)
res_dict = self.controller.index(req)
response = {'floating_ip_info': []}
self.assertEqual(res_dict, response)
@@ -109,21 +114,26 @@ class FloatingIpsBulk(test.TestCase):
ip_range = '192.168.1.0/28'
body = {'floating_ips_bulk_create': {'ip_range': ip_range}}
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips-bulk')
req = fakes.HTTPRequest.blank(self.url)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, body)
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}}
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips-bulk')
req = fakes.HTTPRequest.blank(self.url)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, body)
req, body=body)
def test_create_invalid_cidr_fail(self):
ip_range = 'not a cidr'
body = {'floating_ips_bulk_create': {'ip_range': ip_range}}
req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips-bulk')
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, body)
req = fakes.HTTPRequest.blank(self.url)
self.assertRaises(self.bad_request, self.controller.create,
req, body=body)
class FloatingIPBulkV2(FloatingIPBulkV21):
floating_ips_bulk = fipbulk_v2
bad_request = webob.exc.HTTPBadRequest

View File

@@ -216,6 +216,7 @@ policy_data = """
"compute_extension:v3:os-floating-ip-pools": "",
"compute_extension:floating_ips": "",
"compute_extension:floating_ips_bulk": "",
"compute_extension:v3:os-floating-ips-bulk": "",
"compute_extension:fping": "",
"compute_extension:fping:all_tenants": "is_admin:True",
"compute_extension:v3:os-fping": "",

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
{
"ip_range": "%(ip_range)s"
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,86 @@
# Copyright 2014 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.
from oslo.config import cfg
from nova import context
from nova.tests.integrated.v3 import api_sample_base
CONF = cfg.CONF
CONF.import_opt('default_floating_pool', 'nova.network.floating_ips')
CONF.import_opt('public_interface', 'nova.network.linux_net')
class FloatingIpsBulkTest(api_sample_base.ApiSampleTestBaseV3):
extension_name = "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
},
{
'address': "10.10.10.2",
'pool': pool,
'interface': interface
},
{
'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')
subs = self._get_regexes()
self._verify_response('floating-ips-bulk-list-resp',
subs, response, 200)
def test_floating_ips_bulk_list_by_host(self):
response = self._do_get('os-floating-ips-bulk/testHost')
subs = self._get_regexes()
self._verify_response('floating-ips-bulk-list-by-host-resp',
subs, response, 200)
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})
subs = self._get_regexes()
self._verify_response('floating-ips-bulk-create-resp', subs,
response, 200)
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"})
subs = self._get_regexes()
self._verify_response('floating-ips-bulk-delete-resp', subs,
response, 200)

View File

@@ -86,6 +86,7 @@ nova.api.v3.extensions =
flavor_rxtx = nova.api.openstack.compute.plugins.v3.flavor_rxtx:FlavorRxtx
flavor_manage = nova.api.openstack.compute.plugins.v3.flavor_manage:FlavorManage
floating_ip_pools = nova.api.openstack.compute.plugins.v3.floating_ip_pools:FloatingIpPools
floating_ips_bulk = nova.api.openstack.compute.plugins.v3.floating_ips_bulk:FloatingIpsBulk
fping = nova.api.openstack.compute.plugins.v3.fping:Fping
hide_server_addresses = nova.api.openstack.compute.plugins.v3.hide_server_addresses:HideServerAddresses
hosts = nova.api.openstack.compute.plugins.v3.hosts:Hosts