diff options
| author | Chris Yeoh <cyeoh@au1.ibm.com> | 2012-10-24 16:42:03 +1030 |
|---|---|---|
| committer | Chris Yeoh <cyeoh@au1.ibm.com> | 2012-11-13 23:56:40 +1030 |
| commit | f1e19b46cb6efdcd4b446095ef5efcb2a7c8359c (patch) | |
| tree | c06deebf9c7acd1efc9a39537e698d579293a1a1 /nova | |
| parent | 3c9e48d1119b77428346680c2a009c44ff9bf2ce (diff) | |
| download | nova-f1e19b46cb6efdcd4b446095ef5efcb2a7c8359c.tar.gz nova-f1e19b46cb6efdcd4b446095ef5efcb2a7c8359c.tar.xz nova-f1e19b46cb6efdcd4b446095ef5efcb2a7c8359c.zip | |
Adds REST API support for Fixed IPs
This adds an extension that provides a REST API for getting information
about a fixed ip, reserving a fixed ip and unreserving a fixed ip. The
interface is accessed via
/v2/{tenant_id}/os-fixed-ips/<ip_address> # GET ip info
/v2/{tenant_id}/os-fixed-ips/<ip_address>/action # POST reserve/unreserve ip
This forms part of the work to provide APIs for functionality currently
implemented by nova-manage that needs direct db access so nova-manage
can eventually be removed
Adds db function fixed_ip_get_by_address_detailed in order to optimise being able
to get the instance and network information for a fixed ip at the same
time as the rest of the fixed ip information
Change-Id: I92edf4e6b74b14bb9c49d5bc0c79e40d3a496bc5
Implements: blueprint apis-for-nova-manage
DocImpact
Diffstat (limited to 'nova')
| -rw-r--r-- | nova/api/openstack/compute/contrib/fixed_ips.py | 100 | ||||
| -rw-r--r-- | nova/db/api.py | 5 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 23 | ||||
| -rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_fixed_ips.py | 164 | ||||
| -rw-r--r-- | nova/tests/api/openstack/compute/test_extensions.py | 1 | ||||
| -rw-r--r-- | nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl | 8 | ||||
| -rw-r--r-- | nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl | 3 | ||||
| -rw-r--r-- | nova/tests/policy.json | 1 |
8 files changed, 305 insertions, 0 deletions
diff --git a/nova/api/openstack/compute/contrib/fixed_ips.py b/nova/api/openstack/compute/contrib/fixed_ips.py new file mode 100644 index 000000000..178847d64 --- /dev/null +++ b/nova/api/openstack/compute/contrib/fixed_ips.py @@ -0,0 +1,100 @@ +# 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 urllib +import webob.exc + +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova import db +from nova import exception +from nova.openstack.common import log as logging + +LOG = logging.getLogger(__name__) +authorize = extensions.extension_authorizer('compute', 'fixed_ips') + + +class FixedIPController(object): + def show(self, req, id): + """Return data about the given fixed ip.""" + context = req.environ['nova.context'] + authorize(context) + + try: + fixed_ip = db.fixed_ip_get_by_address_detailed(context, id) + except exception.FixedIpNotFoundForAddress as ex: + raise webob.exc.HTTPNotFound(explanation=str(ex)) + + fixed_ip_info = {"fixed_ip": {}} + if fixed_ip[1] is None: + msg = _("Fixed IP %s has been deleted") % id + raise webob.exc.HTTPNotFound(explanation=msg) + + fixed_ip_info['fixed_ip']['cidr'] = fixed_ip[1]['cidr'] + fixed_ip_info['fixed_ip']['address'] = fixed_ip[0]['address'] + + if fixed_ip[2]: + fixed_ip_info['fixed_ip']['hostname'] = fixed_ip[2]['hostname'] + fixed_ip_info['fixed_ip']['host'] = fixed_ip[2]['host'] + else: + fixed_ip_info['fixed_ip']['hostname'] = None + fixed_ip_info['fixed_ip']['host'] = None + + return fixed_ip_info + + def action(self, req, id, body): + context = req.environ['nova.context'] + authorize(context) + if 'reserve' in body: + return self._set_reserved(context, id, True) + elif 'unreserve' in body: + return self._set_reserved(context, id, False) + else: + raise webob.exc.HTTPBadRequest( + explanation="No valid action specified") + + def _set_reserved(self, context, address, reserved): + try: + fixed_ip = db.fixed_ip_get_by_address(context, address) + db.fixed_ip_update(context, fixed_ip['address'], + {'reserved': reserved}) + except exception.FixedIpNotFoundForAddress as ex: + msg = _("Fixed IP %s not found") % address + raise webob.exc.HTTPNotFound(explanation=msg) + + return webob.exc.HTTPAccepted() + + +class Fixed_ips(extensions.ExtensionDescriptor): + """Fixed IPs support""" + + name = "FixedIPs" + alias = "os-fixed-ips" + namespace = "http://docs.openstack.org/compute/ext/fixed_ips/api/v2" + updated = "2012-10-18T13:25:27-06:00" + + def __init__(self, ext_mgr): + ext_mgr.register(self) + + def get_resources(self): + member_actions = {'action': 'POST'} + resources = [] + resource = extensions.ResourceExtension('os-fixed-ips', + FixedIPController(), + member_actions=member_actions) + resources.append(resource) + return resources diff --git a/nova/db/api.py b/nova/db/api.py index 04786a1fb..45686d9ef 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -466,6 +466,11 @@ def fixed_ip_get_by_address(context, address): return IMPL.fixed_ip_get_by_address(context, address) +def fixed_ip_get_by_address_detailed(context, address): + """Get detailed fixed ip info by address or raise if it does not exist.""" + return IMPL.fixed_ip_get_by_address_detailed(context, address) + + def fixed_ip_get_by_instance(context, instance_uuid): """Get fixed ips by instance or raise if none exist.""" return IMPL.fixed_ip_get_by_instance(context, instance_uuid) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 72caad74d..bc92aaa62 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1151,6 +1151,29 @@ def fixed_ip_get_by_address(context, address, session=None): return result +@require_admin_context +def fixed_ip_get_by_address_detailed(context, address, session=None): + # This method returns a tuple of (models.FixedIp, models.Network, + # models.Instance). + if not session: + session = get_session() + + result = session.query(models.FixedIp, models.Network, models.Instance).\ + filter_by(address=address).\ + outerjoin((models.Network, + models.Network.id == + models.FixedIp.network_id)).\ + outerjoin((models.Instance, + models.Instance.uuid == + models.FixedIp.instance_uuid)).\ + first() + + if not result: + raise exception.FixedIpNotFoundForAddress(address=address) + + return result + + @require_context def fixed_ip_get_by_instance(context, instance_uuid): if not uuidutils.is_uuid_like(instance_uuid): diff --git a/nova/tests/api/openstack/compute/contrib/test_fixed_ips.py b/nova/tests/api/openstack/compute/contrib/test_fixed_ips.py new file mode 100644 index 000000000..4d5ca5e5c --- /dev/null +++ b/nova/tests/api/openstack/compute/contrib/test_fixed_ips.py @@ -0,0 +1,164 @@ +# 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 webob + +from nova.api.openstack.compute.contrib import fixed_ips +from nova import context +from nova import db +from nova import exception +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes + + +fake_fixed_ips = [{'id': 1, + 'address': '192.168.1.1', + 'network_id': 1, + 'virtual_interface_id': 1, + 'instance_uuid': '1', + 'allocated': False, + 'leased': False, + 'reserved': False, + 'host': None}, + {'id': 2, + 'address': '192.168.1.2', + 'network_id': 1, + 'virtual_interface_id': 2, + 'instance_uuid': '2', + 'allocated': False, + 'leased': False, + 'reserved': False, + 'host': None}, + ] + + +def fake_fixed_ip_get_by_address(context, address): + for fixed_ip in fake_fixed_ips: + if fixed_ip['address'] == address: + return fixed_ip + raise exception.FixedIpNotFoundForAddress(address=address) + + +def fake_fixed_ip_get_by_address_detailed(context, address): + network = {'id': 1, + 'cidr': "192.168.1.0/24"} + for fixed_ip in fake_fixed_ips: + if fixed_ip['address'] == address: + return (fixed_ip, FakeModel(network), None) + raise exception.FixedIpNotFoundForAddress(address=address) + + +def fake_fixed_ip_update(context, address, values): + fixed_ip = fake_fixed_ip_get_by_address(context, address) + if fixed_ip is None: + raise exception.FixedIpNotFoundForAddress(address=address) + else: + for key in values: + fixed_ip[key] = values[key] + + +class FakeModel(object): + """Stubs out for model.""" + def __init__(self, values): + self.values = values + + def __getattr__(self, name): + return self.values[name] + + def __getitem__(self, key): + if key in self.values: + return self.values[key] + else: + raise NotImplementedError() + + def __repr__(self): + return '<FakeModel: %s>' % self.values + + +def fake_network_get_all(context): + network = {'id': 1, + 'cidr': "192.168.1.0/24"} + return [FakeModel(network)] + + +class FixedIpTest(test.TestCase): + + def setUp(self): + super(FixedIpTest, self).setUp() + + self.stubs.Set(db, "fixed_ip_get_by_address", + fake_fixed_ip_get_by_address) + self.stubs.Set(db, "fixed_ip_get_by_address_detailed", + fake_fixed_ip_get_by_address_detailed) + self.stubs.Set(db, "fixed_ip_update", fake_fixed_ip_update) + + self.context = context.get_admin_context() + self.controller = fixed_ips.FixedIPController() + + def tearDown(self): + super(FixedIpTest, self).tearDown() + + def test_fixed_ips_get(self): + req = fakes.HTTPRequest.blank('/v2/fake/os-fixed-ips/192.168.1.1') + res_dict = self.controller.show(req, '192.168.1.1') + response = {'fixed_ip': {'cidr': '192.168.1.0/24', + 'hostname': None, + 'host': None, + 'address': '192.168.1.1'}} + self.assertEqual(response, res_dict) + + def test_fixed_ips_get_fail(self): + req = fakes.HTTPRequest.blank('/v2/fake/os-fixed-ips/10.0.0.1') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, req, + '10.0.0.1') + + def test_fixed_ip_reserve(self): + fake_fixed_ips[0]['reserved'] = False + ip_addr = '192.168.1.1' + body = {'reserve': None} + req = fakes.HTTPRequest.blank( + '/v2/fake/os-fixed-ips/192.168.1.1/action') + result = self.controller.action(req, "192.168.1.1", body) + + self.assertEqual('202 Accepted', result.status) + self.assertEqual(fake_fixed_ips[0]['reserved'], True) + + def test_fixed_ip_reserve_bad_ip(self): + ip_addr = '10.0.0.1' + body = {'reserve': None} + req = fakes.HTTPRequest.blank( + '/v2/fake/os-fixed-ips/10.0.0.1/action') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.action, req, + '10.0.0.1', body) + + def test_fixed_ip_unreserve(self): + fake_fixed_ips[0]['reserved'] = True + ip_addr = '192.168.1.1' + body = {'unreserve': None} + req = fakes.HTTPRequest.blank( + '/v2/fake/os-fixed-ips/192.168.1.1/action') + result = self.controller.action(req, "192.168.1.1", body) + + self.assertEqual('202 Accepted', result.status) + self.assertEqual(fake_fixed_ips[0]['reserved'], False) + + def test_fixed_ip_unreserve_bad_ip(self): + ip_addr = '10.0.0.1' + body = {'unreserve': None} + req = fakes.HTTPRequest.blank( + '/v2/fake/os-fixed-ips/10.0.0.1/action') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.action, req, + '10.0.0.1', body) diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py index f3e65a150..8282c4c19 100644 --- a/nova/tests/api/openstack/compute/test_extensions.py +++ b/nova/tests/api/openstack/compute/test_extensions.py @@ -167,6 +167,7 @@ class ExtensionControllerTest(ExtensionTestCase): "DiskConfig", "ExtendedStatus", "ExtendedServerAttributes", + "FixedIPs", "FlavorAccess", "FlavorDisabled", "FlavorExtraSpecs", 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 34f305967..2ed3486eb 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 @@ -137,6 +137,14 @@ "updated": "%(timestamp)s" }, { + "alias": "os-fixed-ips", + "description": "Fixed IPs support", + "links": [], + "name": "FixedIPs", + "namespace": "http://docs.openstack.org/compute/ext/fixed_ips/api/v2", + "updated": "2012-10-18T13:25:27-06:00" + }, + { "alias": "os-flavor-access", "description": "%(text)s", "links": [], 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 44aa5ee81..3962ebe9b 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 @@ -51,6 +51,9 @@ <extension alias="os-deferred-delete" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/deferred-delete/api/v1.1" name="DeferredDelete"> <description>%(text)s</description> </extension> + <extension alias="os-fixed-ips" name="FixedIPs" namespace="http://docs.openstack.org/compute/ext/fixed_ips/api/v2" updated="2012-10-18T13:25:27-06:00"> + <description>Fixed IPs support</description> + </extension> <extension alias="os-flavor-access" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/flavor_access/api/v2" name="FlavorAccess"> <description>%(text)s</description> </extension> diff --git a/nova/tests/policy.json b/nova/tests/policy.json index bf94d4e49..6381a9031 100644 --- a/nova/tests/policy.json +++ b/nova/tests/policy.json @@ -94,6 +94,7 @@ "compute_extension:disk_config": "", "compute_extension:extended_server_attributes": "", "compute_extension:extended_status": "", + "compute_extension:fixed_ips": "", "compute_extension:flavor_access": "", "compute_extension:flavor_disabled": "", "compute_extension:flavor_rxtx": "", |
