diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.json b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.json
new file mode 100644
index 000000000000..d174bad5abc0
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.json
@@ -0,0 +1,8 @@
+{
+ "floating_ips_bulk_create" :
+ {
+ "ip_range": "192.168.1.0/24",
+ "pool": "nova",
+ "interface": "eth0"
+ }
+}
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.xml b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.xml
new file mode 100644
index 000000000000..1fc7305090d2
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.xml
@@ -0,0 +1,6 @@
+
+
+192.168.1.0/24
+nova
+eth0
+
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.json b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.json
new file mode 100644
index 000000000000..ef1cbfb17f31
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.json
@@ -0,0 +1,7 @@
+{
+ "floating_ips_bulk_create": {
+ "interface": "eth0",
+ "ip_range": "192.168.1.0/24",
+ "pool": "nova"
+ }
+}
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.xml b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.xml
new file mode 100644
index 000000000000..db80bbfc10fd
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.xml
@@ -0,0 +1,6 @@
+
+
+ eth0
+ 192.168.1.0/24
+ nova
+
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.json b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.json
new file mode 100644
index 000000000000..df59c1a73599
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.json
@@ -0,0 +1,3 @@
+{
+ "ip_range": "192.168.1.0/24"
+}
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.xml b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.xml
new file mode 100644
index 000000000000..c40f28dc3489
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.xml
@@ -0,0 +1,2 @@
+
+192.168.1.0/24
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.json b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.json
new file mode 100644
index 000000000000..166984b24a4f
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.json
@@ -0,0 +1,3 @@
+{
+ "floating_ips_bulk_delete": "192.168.1.0/24"
+}
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.xml b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.xml
new file mode 100644
index 000000000000..3d77af334a56
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.xml
@@ -0,0 +1,2 @@
+
+192.168.1.0/24
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.json b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.json
new file mode 100644
index 000000000000..0eaaf75ae098
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.json
@@ -0,0 +1,11 @@
+{
+ "floating_ip_info": [
+ {
+ "address": "10.10.10.3",
+ "instance_uuid": null,
+ "interface": "eth0",
+ "pool": "nova",
+ "project_id": null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.xml b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.xml
new file mode 100644
index 000000000000..4c3c8cd9cae2
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.xml
@@ -0,0 +1,10 @@
+
+
+ -
+ eth0
+ None
+ None
+ nova
+ 10.10.10.3
+
+
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.json b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.json
new file mode 100644
index 000000000000..de1e622bb163
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.json
@@ -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
+ }
+ ]
+}
\ No newline at end of file
diff --git a/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.xml b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.xml
new file mode 100644
index 000000000000..6ef85bd87411
--- /dev/null
+++ b/doc/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.xml
@@ -0,0 +1,24 @@
+
+
+ -
+ eth0
+ None
+ None
+ nova
+ 10.10.10.1
+
+ -
+ eth0
+ None
+ None
+ nova
+ 10.10.10.2
+
+ -
+ eth0
+ None
+ None
+ nova
+ 10.10.10.3
+
+
\ No newline at end of file
diff --git a/etc/nova/policy.json b/etc/nova/policy.json
index cf4864650014..857613edc5e4 100644
--- a/etc/nova/policy.json
+++ b/etc/nova/policy.json
@@ -48,6 +48,7 @@
"compute_extension:floating_ip_dns": "",
"compute_extension:floating_ip_pools": "",
"compute_extension:floating_ips": "",
+ "compute_extension:floating_ips_bulk": "rule:admin_api",
"compute_extension:fping": "",
"compute_extension:fping:all_tenants": "rule:admin_api",
"compute_extension:hosts": "rule:admin_api",
diff --git a/nova/api/openstack/compute/contrib/floating_ips_bulk.py b/nova/api/openstack/compute/contrib/floating_ips_bulk.py
new file mode 100644
index 000000000000..59d9d24b6c29
--- /dev/null
+++ b/nova/api/openstack/compute/contrib/floating_ips_bulk.py
@@ -0,0 +1,176 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 IBM
+# 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 netaddr
+import urllib
+import webob.exc
+
+from nova.api.openstack import extensions
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
+from nova import db
+from nova import exception
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+
+CONF = cfg.CONF
+CONF.import_opt('default_floating_pool', 'nova.network.manager')
+CONF.import_opt('public_interface', 'nova.network.linux_net')
+
+
+LOG = logging.getLogger(__name__)
+authorize = extensions.extension_authorizer('compute', 'floating_ips_bulk')
+
+
+class FloatingIPBulkController(object):
+
+ 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)
+
+ 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": []}
+
+ try:
+ if host is None:
+ floating_ips = db.floating_ip_get_all(context)
+ else:
+ floating_ips = db.floating_ip_get_all_by_host(context, host)
+ except exception.NoFloatingIpsDefined:
+ return floating_ip_info
+
+ for floating_ip in floating_ips:
+ instance_uuid = None
+ if floating_ip['fixed_ip_id']:
+ fixed_ip = db.fixed_ip_get(context, floating_ip['fixed_ip_id'])
+ instance_uuid = fixed_ip['instance_uuid']
+
+ result = {'address': 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
+
+ def create(self, req, body):
+ """Bulk create floating ips"""
+ context = req.environ['nova.context']
+ authorize(context)
+
+ if not 'floating_ips_bulk_create' in body:
+ raise webob.exc.HTTPUnprocessableEntity()
+ params = body['floating_ips_bulk_create']
+
+ LOG.debug(params)
+
+ if not 'ip_range' in params:
+ raise webob.exc.HTTPUnprocessableEntity()
+ ip_range = params['ip_range']
+
+ pool = params.get('pool', CONF.default_floating_pool)
+ interface = params.get('interface', CONF.public_interface)
+
+ try:
+ ips = ({'address': str(address),
+ 'pool': pool,
+ 'interface': interface}
+ for address in self._address_to_hosts(ip_range))
+ except exception.InvalidInput as exc:
+ raise webob.exc.HTTPBadRequest(explanation=str(exc))
+
+ try:
+ db.floating_ip_bulk_create(context, ips)
+ except exception.FloatingIpExists as exc:
+ raise webob.exc.HTTPBadRequest(explanation=str(exc))
+
+ return {"floating_ips_bulk_create": {"ip_range": ip_range,
+ "pool": pool,
+ "interface": interface}}
+
+ def update(self, req, id, body):
+ """Bulk delete floating IPs"""
+ context = req.environ['nova.context']
+ authorize(context)
+
+ if id != "delete":
+ raise webob.exc.HTTPNotFound("Unknown action")
+
+ try:
+ ip_range = body['ip_range']
+ except (TypeError, KeyError):
+ raise webob.exc.HTTPUnprocessableEntity()
+
+ try:
+ ips = ({'address': str(address)}
+ for address in self._address_to_hosts(ip_range))
+ except exception.InvalidInput as exc:
+ raise webob.exc.HTTPBadRequest(explanation=str(exc))
+ db.floating_ip_bulk_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=str(exc))
+
+
+class Floating_ips_bulk(extensions.ExtensionDescriptor):
+ """Bulk handling of Floating IPs"""
+
+ name = "FloatingIpsBulk"
+ alias = "os-floating-ips-bulk"
+ namespace = ("http://docs.openstack.org/compute/ext/"
+ "floating_ips_bulk/api/v2")
+ updated = "2012-10-29T13:25:27-06:00"
+
+ def __init__(self, ext_mgr):
+ ext_mgr.register(self)
+
+ def get_resources(self):
+ resources = []
+ resource = extensions.ResourceExtension('os-floating-ips-bulk',
+ FloatingIPBulkController())
+ resources.append(resource)
+ return resources
diff --git a/nova/tests/api/openstack/compute/contrib/test_floating_ip_bulk.py b/nova/tests/api/openstack/compute/contrib/test_floating_ip_bulk.py
new file mode 100644
index 000000000000..2dd3cc2177b4
--- /dev/null
+++ b/nova/tests/api/openstack/compute/contrib/test_floating_ip_bulk.py
@@ -0,0 +1,127 @@
+# Copyright 2012 IBM
+# 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 netaddr
+import webob
+
+from nova.api.openstack.compute.contrib import floating_ips_bulk
+from nova import context
+from nova import db
+from nova import exception
+from nova.openstack.common import cfg
+from nova import test
+from nova.tests.api.openstack import fakes
+
+CONF = cfg.CONF
+
+
+class FloatingIPBulk(test.TestCase):
+
+ def setUp(self):
+ super(FloatingIPBulk, self).setUp()
+
+ self.context = context.get_admin_context()
+ self.controller = floating_ips_bulk.FloatingIPBulkController()
+
+ def tearDown(self):
+ super(FloatingIPBulk, self).tearDown()
+
+ 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)
+ 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/24'
+ self._setup_floating_ips(ip_range)
+
+ def test_create_ips_pool(self):
+ ip_range = '10.0.1.0/20'
+ pool = 'a new pool'
+ 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)
+ 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):
+ 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)
+ 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}
+ for ip_addr in netaddr.IPNetwork(ip_range).iter_hosts()]
+ response = {'floating_ip_info': ip_info}
+
+ self.assertEqual(res_dict, response)
+
+ def test_delete_ips(self):
+ ip_range = '192.168.1.0/20'
+ 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)
+
+ 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)
+ 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/20'
+ self._setup_floating_ips(ip_range)
+
+ 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')
+ self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
+ req, 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')
+ self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
+ req, 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)
diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py
index e71833916285..82278c4fc1ad 100644
--- a/nova/tests/api/openstack/compute/test_extensions.py
+++ b/nova/tests/api/openstack/compute/test_extensions.py
@@ -178,6 +178,7 @@ class ExtensionControllerTest(ExtensionTestCase):
"FloatingIps",
"FloatingIpDns",
"FloatingIpPools",
+ "FloatingIpsBulk",
"Fox In Socks",
"Hosts",
"Keypairs",
diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
index 531508951553..6ac33b933322 100644
--- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
+++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
@@ -208,6 +208,14 @@
"namespace": "http://docs.openstack.org/compute/ext/floating_ips/api/v1.1",
"updated": "%(timestamp)s"
},
+ {
+ "alias": "os-floating-ips-bulk",
+ "description": "%(text)s",
+ "links": [],
+ "name": "FloatingIpsBulk",
+ "namespace": "http://docs.openstack.org/compute/ext/floating_ips_bulk/api/v2",
+ "updated": "%(timestamp)s"
+ },
{
"alias": "os-hosts",
"description": "%(text)s",
diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
index 5f58cc7d7836..1fca0eb2c193 100644
--- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
+++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
@@ -78,6 +78,9 @@
%(text)s
+
+ %(text)s
+
%(text)s
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.json.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.json.tpl
new file mode 100644
index 000000000000..676859de9de9
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.json.tpl
@@ -0,0 +1,8 @@
+{
+ "floating_ips_bulk_create" :
+ {
+ "ip_range": "%(ip_range)s",
+ "pool": "%(pool)s",
+ "interface": "%(interface)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.xml.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.xml.tpl
new file mode 100644
index 000000000000..ebe0b9aa9aea
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-req.xml.tpl
@@ -0,0 +1,6 @@
+
+
+%(ip_range)s
+%(pool)s
+%(interface)s
+
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.json.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.json.tpl
new file mode 100644
index 000000000000..ef1cbfb17f31
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.json.tpl
@@ -0,0 +1,7 @@
+{
+ "floating_ips_bulk_create": {
+ "interface": "eth0",
+ "ip_range": "192.168.1.0/24",
+ "pool": "nova"
+ }
+}
\ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.xml.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.xml.tpl
new file mode 100644
index 000000000000..db80bbfc10fd
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-create-resp.xml.tpl
@@ -0,0 +1,6 @@
+
+
+ eth0
+ 192.168.1.0/24
+ nova
+
\ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.json.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.json.tpl
new file mode 100644
index 000000000000..d630d669cda3
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.json.tpl
@@ -0,0 +1,3 @@
+{
+ "ip_range": "%(ip_range)s"
+}
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.xml.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.xml.tpl
new file mode 100644
index 000000000000..27a6b0e95a9e
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-req.xml.tpl
@@ -0,0 +1,2 @@
+
+%(ip_range)s
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.json.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.json.tpl
new file mode 100644
index 000000000000..166984b24a4f
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.json.tpl
@@ -0,0 +1,3 @@
+{
+ "floating_ips_bulk_delete": "192.168.1.0/24"
+}
\ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.xml.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.xml.tpl
new file mode 100644
index 000000000000..3d77af334a56
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-delete-resp.xml.tpl
@@ -0,0 +1,2 @@
+
+192.168.1.0/24
\ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.json.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.json.tpl
new file mode 100644
index 000000000000..0eaaf75ae098
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.json.tpl
@@ -0,0 +1,11 @@
+{
+ "floating_ip_info": [
+ {
+ "address": "10.10.10.3",
+ "instance_uuid": null,
+ "interface": "eth0",
+ "pool": "nova",
+ "project_id": null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.xml.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.xml.tpl
new file mode 100644
index 000000000000..4c3c8cd9cae2
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-by-host-resp.xml.tpl
@@ -0,0 +1,10 @@
+
+
+ -
+ eth0
+ None
+ None
+ nova
+ 10.10.10.3
+
+
\ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.json.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.json.tpl
new file mode 100644
index 000000000000..de1e622bb163
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.json.tpl
@@ -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
+ }
+ ]
+}
\ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.xml.tpl b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.xml.tpl
new file mode 100644
index 000000000000..6ef85bd87411
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ips-bulk/floating-ips-bulk-list-resp.xml.tpl
@@ -0,0 +1,24 @@
+
+
+ -
+ eth0
+ None
+ None
+ nova
+ 10.10.10.1
+
+ -
+ eth0
+ None
+ None
+ nova
+ 10.10.10.2
+
+ -
+ eth0
+ None
+ None
+ nova
+ 10.10.10.3
+
+
\ No newline at end of file
diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py
index 3a8488cd2c5f..e86c334b1e9a 100644
--- a/nova/tests/integrated/test_api_samples.py
+++ b/nova/tests/integrated/test_api_samples.py
@@ -944,6 +944,80 @@ class FloatingIpsXmlTest(FloatingIpsJsonTest):
ctype = 'xml'
+class FloatingIpsBulkJsonTest(ApiSampleTestBase):
+ extension_name = "nova.api.openstack.compute.contrib." \
+ "floating_ips_bulk.Floating_ips_bulk"
+
+ def setUp(self):
+ super(FloatingIpsBulkJsonTest, 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)
+
+ def tearDown(self):
+ self.compute.db.floating_ip_bulk_destroy(
+ context.get_admin_context(), self.ip_pool)
+ super(FloatingIpsBulkJsonTest, self).tearDown()
+
+ def test_floating_ips_bulk_list(self):
+ response = self._do_get('os-floating-ips-bulk')
+ self.assertEqual(response.status, 200)
+ subs = self._get_regexes()
+ return self._verify_response('floating-ips-bulk-list-resp', subs,
+ response)
+
+ def test_floating_ips_bulk_list_by_host(self):
+ response = self._do_get('os-floating-ips-bulk/testHost')
+ self.assertEqual(response.status, 200)
+ subs = self._get_regexes()
+ return self._verify_response('floating-ips-bulk-list-by-host-resp',
+ subs, response)
+
+ 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.assertEqual(response.status, 200)
+ subs = self._get_regexes()
+ return self._verify_response('floating-ips-bulk-create-resp', subs,
+ response)
+
+ 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.assertEqual(response.status, 200)
+ subs = self._get_regexes()
+ return self._verify_response('floating-ips-bulk-delete-resp', subs,
+ response)
+
+
+class FloatingIpsBulkXmlTest(FloatingIpsBulkJsonTest):
+ ctype = 'xml'
+
+
class KeyPairsSampleJsonTest(ApiSampleTestBase):
extension_name = "nova.api.openstack.compute.contrib.keypairs.Keypairs"
diff --git a/nova/tests/policy.json b/nova/tests/policy.json
index 33a08c7c051c..20bfa3270613 100644
--- a/nova/tests/policy.json
+++ b/nova/tests/policy.json
@@ -106,6 +106,7 @@
"compute_extension:floating_ip_dns": "",
"compute_extension:floating_ip_pools": "",
"compute_extension:floating_ips": "",
+ "compute_extension:floating_ips_bulk": "",
"compute_extension:fping": "",
"compute_extension:fping:all_tenants": "is_admin:True",
"compute_extension:hosts": "",