From 7ab5ace768797f56bf7e1c846fca0f1d40ed6086 Mon Sep 17 00:00:00 2001 From: Akihiro MOTOKI Date: Tue, 13 Nov 2012 21:45:03 +0900 Subject: [PATCH] Accepts UUID as an ID of Floating IP Fixes bug 1052561. In Quanutm, Floating IPs are identified by UUID instead of integer ID. After quantum-nova integration for FLoating IP has been implemented, Horizon also needs to accept UUID style of ID for Floating IP. Change-Id: I6ed919cbbc818c97cecef2fe3a91c8e5a7ac76e0 --- .../floating_ips/tables.py | 6 ++- .../access_and_security/floating_ips/tests.py | 39 +++++++++++++++++-- .../access_and_security/floating_ips/utils.py | 31 +++++++++++++++ .../floating_ips/workflows.py | 4 +- .../project/access_and_security/tests.py | 6 +++ .../test/test_data/nova_data.py | 15 +++++++ 6 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 openstack_dashboard/dashboards/project/access_and_security/floating_ips/utils.py diff --git a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py index 788c3730bb..0e8c33bb8b 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py +++ b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py @@ -28,6 +28,8 @@ from horizon import tables from openstack_dashboard import api +from .utils import get_int_or_uuid + LOG = logging.getLogger(__name__) @@ -83,7 +85,7 @@ class DisassociateIP(tables.Action): def single(self, table, request, obj_id): try: - fip = table.get_object_by_id(int(obj_id)) + fip = table.get_object_by_id(get_int_or_uuid(obj_id)) api.server_remove_floating_ip(request, fip.instance_id, fip.id) LOG.info('Disassociating Floating IP "%s".' % obj_id) messages.success(request, @@ -118,7 +120,7 @@ class FloatingIPsTable(tables.DataTable): empty_value="-") def sanitize_id(self, obj_id): - return int(obj_id) + return get_int_or_uuid(obj_id) def get_object_display(self, datum): return datum.ip diff --git a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py index 8daddbf793..ebb81582c8 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py +++ b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py @@ -19,6 +19,8 @@ # License for the specific language governing permissions and limitations # under the License. +import uuid + from django import http from django.core.urlresolvers import reverse @@ -27,6 +29,8 @@ from mox import IsA from openstack_dashboard import api from openstack_dashboard.test import helpers as test +from .utils import get_int_or_uuid + INDEX_URL = reverse('horizon:project:access_and_security:index') NAMESPACE = "horizon:project:access_and_security:floating_ips" @@ -49,10 +53,10 @@ class FloatingIpViewTests(test.TestCase): workflow = res.context['workflow'] choices = dict(workflow.steps[0].action.fields['ip_id'].choices) # Verify that our "associated" floating IP isn't in the choices list. - self.assertTrue(self.floating_ips.get(id=1) not in choices) + self.assertTrue(self.floating_ips.first() not in choices) def test_associate_post(self): - floating_ip = self.floating_ips.get(id=2) + floating_ip = self.floating_ips.list()[1] server = self.servers.first() self.mox.StubOutWithMock(api.nova, 'server_add_floating_ip') self.mox.StubOutWithMock(api.nova, 'tenant_floating_ip_list') @@ -74,7 +78,7 @@ class FloatingIpViewTests(test.TestCase): self.assertRedirectsNoFollow(res, INDEX_URL) def test_associate_post_with_redirect(self): - floating_ip = self.floating_ips.get(id=2) + floating_ip = self.floating_ips.list()[1] server = self.servers.first() self.mox.StubOutWithMock(api.nova, 'server_add_floating_ip') self.mox.StubOutWithMock(api.nova, 'tenant_floating_ip_list') @@ -97,7 +101,7 @@ class FloatingIpViewTests(test.TestCase): self.assertRedirectsNoFollow(res, next) def test_associate_post_with_exception(self): - floating_ip = self.floating_ips.get(id=2) + floating_ip = self.floating_ips.list()[1] server = self.servers.first() self.mox.StubOutWithMock(api.nova, 'server_add_floating_ip') self.mox.StubOutWithMock(api.nova, 'tenant_floating_ip_list') @@ -175,3 +179,30 @@ class FloatingIpViewTests(test.TestCase): action = "floating_ips__disassociate__%s" % floating_ip.id res = self.client.post(INDEX_URL, {"action": action}) self.assertRedirectsNoFollow(res, INDEX_URL) + + +class FloatingIpQuantumViewTests(FloatingIpViewTests): + def setUp(self): + super(FloatingIpViewTests, self).setUp() + self.floating_ips = self.floating_ips_uuid + + +class FloatingIpUtilsTests(test.TestCase): + def test_accept_valid_integer(self): + val = 100 + ret = get_int_or_uuid(val) + self.assertEqual(val, ret) + + def test_accept_valid_integer_string(self): + val = '100' + ret = get_int_or_uuid(val) + self.assertEqual(int(val), ret) + + def test_accept_valid_uuid(self): + val = str(uuid.uuid4()) + ret = get_int_or_uuid(val) + self.assertEqual(val, ret) + + def test_reject_random_string(self): + val = '55WbJTpJDf' + self.assertRaises(ValueError, get_int_or_uuid, val) diff --git a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/utils.py b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/utils.py new file mode 100644 index 0000000000..af2eb8e18d --- /dev/null +++ b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/utils.py @@ -0,0 +1,31 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 NEC 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. + +import uuid + + +def get_int_or_uuid(value): + """Check if a value is valid as UUID or an integer. + + This method is mainly used to convert floating IP id to the + appropriate type. For floating IP id, integer is used in Nova's + original implementation, but UUID is used in Quantum based one. + """ + try: + uuid.UUID(value) + return value + except (ValueError, AttributeError): + return int(value) diff --git a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/workflows.py b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/workflows.py index 1abf670ef5..48628308e8 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/workflows.py +++ b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/workflows.py @@ -24,13 +24,15 @@ from horizon import forms from openstack_dashboard import api +from .utils import get_int_or_uuid + ALLOCATE_URL = "horizon:project:access_and_security:floating_ips:allocate" class AssociateIPAction(workflows.Action): ip_id = forms.DynamicTypedChoiceField(label=_("IP Address"), - coerce=int, + coerce=get_int_or_uuid, empty_value=None, add_item_link=ALLOCATE_URL) instance_id = forms.ChoiceField(label=_("Instance")) diff --git a/openstack_dashboard/dashboards/project/access_and_security/tests.py b/openstack_dashboard/dashboards/project/access_and_security/tests.py index 180c9904cf..f990e9bf4e 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/tests.py +++ b/openstack_dashboard/dashboards/project/access_and_security/tests.py @@ -86,3 +86,9 @@ class AccessAndSecurityTests(test.TestCase): self.assertContains(res, '') self.assertContains(res, '') + + +class AccessAndSecurityQuantumTests(AccessAndSecurityTests): + def setUp(self): + super(AccessAndSecurityTests, self).setUp() + self.floating_ips = self.floating_ips_uuid diff --git a/openstack_dashboard/test/test_data/nova_data.py b/openstack_dashboard/test/test_data/nova_data.py index b3ccd96d75..26ec973e8d 100644 --- a/openstack_dashboard/test/test_data/nova_data.py +++ b/openstack_dashboard/test/test_data/nova_data.py @@ -13,6 +13,7 @@ # under the License. import json +import uuid from novaclient.v1_1 import (flavors, keypairs, servers, volumes, volume_types, quotas, @@ -143,6 +144,7 @@ def data(TEST): TEST.quotas = TestDataContainer() TEST.quota_usages = TestDataContainer() TEST.floating_ips = TestDataContainer() + TEST.floating_ips_uuid = TestDataContainer() TEST.usages = TestDataContainer() TEST.certs = TestDataContainer() TEST.volume_snapshots = TestDataContainer() @@ -335,6 +337,19 @@ def data(TEST): 'ip': '58.58.58.58'}) TEST.floating_ips.add(fip_1, fip_2) + # Floating IP with UUID id (for Floating IP with Quantum) + fip_3 = floating_ips.FloatingIP(floating_ips.FloatingIPManager(None), + {'id': str(uuid.uuid4()), + 'fixed_ip': '10.0.0.4', + 'instance_id': server_1.id, + 'ip': '58.58.58.58'}) + fip_4 = floating_ips.FloatingIP(floating_ips.FloatingIPManager(None), + {'id': str(uuid.uuid4()), + 'fixed_ip': None, + 'instance_id': None, + 'ip': '58.58.58.58'}) + TEST.floating_ips_uuid.add(fip_3, fip_4) + # Usage usage_vals = {"tenant_id": TEST.tenant.id, "instance_name": server_1.name,