Port fixed_ip extention to v2.1

This patch ports fixed_ip 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: Ifa7e0dd5a91cfe881077b92aeb7019d0435d780a
This commit is contained in:
Eli Qiao 2014-09-04 12:54:58 +08:00
parent 9fd059b938
commit f7107c8480
10 changed files with 306 additions and 34 deletions

View File

@ -0,0 +1,3 @@
{
"reserve": "None"
}

View File

@ -0,0 +1,8 @@
{
"fixed_ip": {
"address": "192.168.1.1",
"cidr": "192.168.1.0/24",
"host": "host",
"hostname": "openstack"
}
}

View File

@ -124,6 +124,8 @@
"compute_extension:v3:os-extended-volumes:attach": "",
"compute_extension:v3:os-extended-volumes:detach": "",
"compute_extension:fixed_ips": "rule:admin_api",
"compute_extension:v3:os-fixed-ips": "rule:admin_api",
"compute_extension:v3:os-fixed-ips:discoverable": "",
"compute_extension:flavor_access": "",
"compute_extension:flavor_access:addTenantAccess": "rule:admin_api",
"compute_extension:flavor_access:removeTenantAccess": "rule:admin_api",

View File

@ -0,0 +1,106 @@
# 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 webob
import webob.exc
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova import exception
from nova.i18n import _
from nova import objects
ALIAS = 'os-fixed-ips'
authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS)
class FixedIPController(wsgi.Controller):
@extensions.expected_errors((400, 404))
def show(self, req, id):
"""Return data about the given fixed ip."""
context = req.environ['nova.context']
authorize(context)
attrs = ['network', 'instance']
try:
fixed_ip = objects.FixedIP.get_by_address(context, id,
expected_attrs=attrs)
except exception.FixedIpNotFoundForAddress as ex:
raise webob.exc.HTTPNotFound(explanation=ex.format_message())
except exception.FixedIpInvalid as ex:
raise webob.exc.HTTPBadRequest(explanation=ex.format_message())
fixed_ip_info = {"fixed_ip": {}}
if fixed_ip is None:
msg = _("Fixed IP %s has been deleted") % id
raise webob.exc.HTTPNotFound(explanation=msg)
fixed_ip_info['fixed_ip']['cidr'] = str(fixed_ip.network.cidr)
fixed_ip_info['fixed_ip']['address'] = str(fixed_ip.address)
if fixed_ip.instance:
fixed_ip_info['fixed_ip']['hostname'] = fixed_ip.instance.hostname
fixed_ip_info['fixed_ip']['host'] = fixed_ip.instance.host
else:
fixed_ip_info['fixed_ip']['hostname'] = None
fixed_ip_info['fixed_ip']['host'] = None
return fixed_ip_info
@wsgi.response(202)
@extensions.expected_errors((400, 404))
@wsgi.action('reserve')
def reserve(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
return self._set_reserved(context, id, True)
@wsgi.response(202)
@extensions.expected_errors((400, 404))
@wsgi.action('unreserve')
def unreserve(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
return self._set_reserved(context, id, False)
def _set_reserved(self, context, address, reserved):
try:
fixed_ip = objects.FixedIP.get_by_address(context, address)
fixed_ip.reserved = reserved
fixed_ip.save()
except exception.FixedIpNotFoundForAddress:
msg = _("Fixed IP %s not found") % address
raise webob.exc.HTTPNotFound(explanation=msg)
except exception.FixedIpInvalid:
msg = _("Fixed IP %s not valid") % address
raise webob.exc.HTTPBadRequest(explanation=msg)
class FixedIps(extensions.V3APIExtensionBase):
"""Fixed IPs support."""
name = "FixedIPs"
alias = ALIAS
version = 1
def get_resources(self):
member_actions = {'action': 'POST'}
resources = extensions.ResourceExtension(ALIAS,
FixedIPController(),
member_actions=member_actions)
return [resources]
def get_controller_extensions(self):
return []

View File

@ -14,7 +14,8 @@
import webob
from nova.api.openstack.compute.contrib import fixed_ips
from nova.api.openstack.compute.contrib import fixed_ips as fixed_ips_v2
from nova.api.openstack.compute.plugins.v3 import fixed_ips as fixed_ips_v21
from nova import context
from nova import db
from nova import exception
@ -124,10 +125,13 @@ def fake_network_get_all(context):
return [FakeModel(network)]
class FixedIpTest(test.NoDBTestCase):
class FixedIpTestV21(test.NoDBTestCase):
fixed_ips = fixed_ips_v21
url = '/v2/fake/os-fixed-ips'
def setUp(self):
super(FixedIpTest, self).setUp()
super(FixedIpTestV21, self).setUp()
self.stubs.Set(db, "fixed_ip_get_by_address",
fake_fixed_ip_get_by_address)
@ -136,10 +140,19 @@ class FixedIpTest(test.NoDBTestCase):
self.stubs.Set(db, "fixed_ip_update", fake_fixed_ip_update)
self.context = context.get_admin_context()
self.controller = fixed_ips.FixedIPController()
self.controller = self.fixed_ips.FixedIPController()
def _assert_equal(self, ret, exp):
self.assertEqual(ret.wsgi_code, exp)
def _get_reserve_action(self):
return self.controller.reserve
def _get_unreserve_action(self):
return self.controller.unreserve
def test_fixed_ips_get(self):
req = fakes.HTTPRequest.blank('/v2/fake/os-fixed-ips/192.168.1.1')
req = fakes.HTTPRequest.blank('%s/192.168.1.1' % self.url)
res_dict = self.controller.show(req, '192.168.1.1')
response = {'fixed_ip': {'cidr': '192.168.1.0/24',
'hostname': None,
@ -148,78 +161,96 @@ class FixedIpTest(test.NoDBTestCase):
self.assertEqual(response, res_dict)
def test_fixed_ips_get_bad_ip_fail(self):
req = fakes.HTTPRequest.blank('/v2/fake/os-fixed-ips/10.0.0.1')
req = fakes.HTTPRequest.blank('%s/10.0.0.1' % self.url)
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, req,
'10.0.0.1')
def test_fixed_ips_get_invalid_ip_address(self):
req = fakes.HTTPRequest.blank('/v2/fake/os-fixed-ips/inv.ali.d.ip')
req = fakes.HTTPRequest.blank('%s/inv.ali.d.ip' % self.url)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.show, req,
'inv.ali.d.ip')
def test_fixed_ips_get_deleted_ip_fail(self):
req = fakes.HTTPRequest.blank('/v2/fake/os-fixed-ips/10.0.0.2')
req = fakes.HTTPRequest.blank('%s/10.0.0.2' % self.url)
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, req,
'10.0.0.2')
def test_fixed_ip_reserve(self):
fake_fixed_ips[0]['reserved'] = False
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)
req = fakes.HTTPRequest.blank('%s/192.168.1.1/action' % self.url)
action = self._get_reserve_action()
result = action(req, "192.168.1.1", body)
self.assertEqual('202 Accepted', result.status)
self._assert_equal(result or action, 202)
self.assertEqual(fake_fixed_ips[0]['reserved'], True)
def test_fixed_ip_reserve_bad_ip(self):
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,
req = fakes.HTTPRequest.blank('%s/10.0.0.1/action' % self.url)
action = self._get_reserve_action()
self.assertRaises(webob.exc.HTTPNotFound, action, req,
'10.0.0.1', body)
def test_fixed_ip_reserve_invalid_ip_address(self):
body = {'reserve': None}
req = fakes.HTTPRequest.blank(
'/v2/fake/os-fixed-ips/inv.ali.d.ip/action')
req = fakes.HTTPRequest.blank('%s/inv.ali.d.ip/action' % self.url)
action = self._get_reserve_action()
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.action, req, 'inv.ali.d.ip', body)
action, req, 'inv.ali.d.ip', body)
def test_fixed_ip_reserve_deleted_ip(self):
body = {'reserve': None}
req = fakes.HTTPRequest.blank(
'/v2/fake/os-fixed-ips/10.0.0.2/action')
self.assertRaises(webob.exc.HTTPNotFound, self.controller.action, req,
action = self._get_reserve_action()
req = fakes.HTTPRequest.blank('%s/10.0.0.2/action' % self.url)
self.assertRaises(webob.exc.HTTPNotFound, action, req,
'10.0.0.2', body)
def test_fixed_ip_unreserve(self):
fake_fixed_ips[0]['reserved'] = True
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)
req = fakes.HTTPRequest.blank('%s/192.168.1.1/action' % self.url)
action = self._get_unreserve_action()
result = action(req, "192.168.1.1", body)
self.assertEqual('202 Accepted', result.status)
self._assert_equal(result or action, 202)
self.assertEqual(fake_fixed_ips[0]['reserved'], False)
def test_fixed_ip_unreserve_bad_ip(self):
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,
req = fakes.HTTPRequest.blank('%s/10.0.0.1/action' % self.url)
action = self._get_unreserve_action()
self.assertRaises(webob.exc.HTTPNotFound, action, req,
'10.0.0.1', body)
def test_fixed_ip_unreserve_invalid_ip_address(self):
body = {'unreserve': None}
req = fakes.HTTPRequest.blank(
'/v2/fake/os-fixed-ips/inv.ali.d.ip/action')
req = fakes.HTTPRequest.blank('%s/inv.ali.d.ip/action' % self.url)
action = self._get_unreserve_action()
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.action, req, 'inv.ali.d.ip', body)
action, req, 'inv.ali.d.ip', body)
def test_fixed_ip_unreserve_deleted_ip(self):
body = {'unreserve': None}
req = fakes.HTTPRequest.blank(
'/v2/fake/os-fixed-ips/10.0.0.2/action')
self.assertRaises(webob.exc.HTTPNotFound, self.controller.action, req,
req = fakes.HTTPRequest.blank('%s/10.0.0.2/action' % self.url)
action = self._get_unreserve_action()
self.assertRaises(webob.exc.HTTPNotFound, action, req,
'10.0.0.2', body)
class FixedIpTestV2(FixedIpTestV21):
fixed_ips = fixed_ips_v2
def _assert_equal(self, ret, exp):
self.assertEqual(ret.status, '202 Accepted')
def _get_reserve_action(self):
return self.controller.action
def _get_unreserve_action(self):
return self.controller.action

View File

@ -184,6 +184,7 @@ policy_data = """
"compute_extension:v3:os-extended-volumes:detach": "",
"compute_extension:v3:extensions:discoverable": "",
"compute_extension:fixed_ips": "",
"compute_extension:v3:os-fixed-ips": "",
"compute_extension:flavor_access": "",
"compute_extension:flavor_access:addTenantAccess": "rule:admin_api",
"compute_extension:flavor_access:removeTenantAccess": "rule:admin_api",

View File

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

View File

@ -0,0 +1,8 @@
{
"fixed_ip": {
"cidr": "%(cidr)s",
"hostname": "%(hostname)s",
"host": "%(host)s",
"address": "%(address)s"
}
}

View File

@ -0,0 +1,109 @@
# 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 nova import db
from nova import exception
from nova.tests.integrated.v3 import test_servers
from nova.tests.objects import test_network
from nova.tests import utils as test_utils
class FixedIpTest(test_servers.ServersSampleBase):
extension_name = "os-fixed-ips"
def setUp(self):
super(FixedIpTest, self).setUp()
instance = dict(test_utils.get_test_instance(),
hostname='openstack', host='host')
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,
'created_at': None,
'deleted_at': None,
'updated_at': None,
'deleted': None,
'instance': instance,
'network': test_network.fake_network,
'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,
'created_at': None,
'deleted_at': None,
'updated_at': None,
'deleted': None,
'instance': instance,
'network': test_network.fake_network,
'host': None},
]
def fake_fixed_ip_get_by_address(context, address,
columns_to_join=None):
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"}
host = {'host': "host",
'hostname': 'openstack'}
for fixed_ip in fake_fixed_ips:
if fixed_ip['address'] == address:
return (fixed_ip, network, host)
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]
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)
def test_fixed_ip_reserve(self):
# Reserve a Fixed IP.
project = {'reserve': None}
response = self._do_post('os-fixed-ips/192.168.1.1/action',
'fixedip-post-req',
project)
self.assertEqual(response.status_code, 202)
self.assertEqual(response.content, "")
def test_get_fixed_ip(self):
# Return data about the given fixed ip.
response = self._do_get('os-fixed-ips/192.168.1.1')
project = {'cidr': '192.168.1.0/24',
'hostname': 'openstack',
'host': 'host',
'address': '192.168.1.1'}
self._verify_response('fixedips-get-resp', project, response, 200)

View File

@ -80,6 +80,7 @@ nova.api.v3.extensions =
extended_status = nova.api.openstack.compute.plugins.v3.extended_status:ExtendedStatus
extended_volumes = nova.api.openstack.compute.plugins.v3.extended_volumes:ExtendedVolumes
extension_info = nova.api.openstack.compute.plugins.v3.extension_info:ExtensionInfo
fixed_ips = nova.api.openstack.compute.plugins.v3.fixed_ips:FixedIps
flavors = nova.api.openstack.compute.plugins.v3.flavors:Flavors
flavors_extraspecs = nova.api.openstack.compute.plugins.v3.flavors_extraspecs:FlavorsExtraSpecs
flavor_access = nova.api.openstack.compute.plugins.v3.flavor_access:FlavorAccess