Drop Nova floating IP dependency from dashboard

This commit drops Nova floating IP dependency from
the dashboard implementation.

* Use neutron policy for floating IP operations.
* SimpleAssociateIP is supported only by Nova API,
  so the action was removed in this patch.

Floating IP support in the nova API wrapper will be dropped
in a separate patch.

Partially implement blueprint drop-nova-network

Change-Id: I1610fb9aa2212b3f34814fdb9894b715a5211d9c
This commit is contained in:
Akihiro Motoki 2017-04-15 21:52:53 +00:00
parent f100793164
commit 8b6d3422e8
6 changed files with 35 additions and 132 deletions

View File

@ -20,6 +20,7 @@ import horizon
class FloatingIps(horizon.Panel):
name = _("Floating IPs")
slug = 'floating_ips'
permissions = ('openstack.services.network',)
@staticmethod
def can_register():

View File

@ -58,11 +58,7 @@ class AllocateIP(tables.LinkAction):
classes = [c for c in self.classes if c != "disabled"]
self.classes = classes
if api.base.is_service_enabled(request, "network"):
policy_rules = (("network", "create_floatingip"),)
else:
policy_rules = (("compute", "os_compute_api:os-floating-ips"),)
policy_rules = (("network", "create_floatingip"),)
return policy.check(policy_rules, request)
@ -90,11 +86,7 @@ class ReleaseIPs(tables.BatchAction):
)
def allowed(self, request, fip=None):
if api.base.is_service_enabled(request, "network"):
policy_rules = (("network", "delete_floatingip"),)
else:
policy_rules = (("compute", "os_compute_api:os-floating-ips"),)
policy_rules = (("network", "delete_floatingip"),)
return policy.check(policy_rules, request)
def action(self, request, obj_id):
@ -109,11 +101,7 @@ class AssociateIP(tables.LinkAction):
icon = "link"
def allowed(self, request, fip):
if api.base.is_service_enabled(request, "network"):
policy_rules = (("network", "update_floatingip"),)
else:
policy_rules = (("compute", "os_compute_api:os-floating-ips"),)
policy_rules = (("network", "update_floatingip"),)
return not fip.port_id and policy.check(policy_rules, request)
def get_link_url(self, datum):
@ -130,11 +118,7 @@ class DisassociateIP(tables.Action):
action_type = "danger"
def allowed(self, request, fip):
if api.base.is_service_enabled(request, "network"):
policy_rules = (("network", "update_floatingip"),)
else:
policy_rules = (("compute", "os_compute_api:os-floating-ips"),)
policy_rules = (("network", "update_floatingip"),)
return fip.port_id and policy.check(policy_rules, request)
def single(self, table, request, obj_id):
@ -203,8 +187,6 @@ class FloatingIPsTable(tables.DataTable):
super(FloatingIPsTable, self).__init__(
request, data=data, needs_form_wrapper=needs_form_wrapper,
**kwargs)
if not api.base.is_service_enabled(request, 'network'):
del self.columns['status']
def sanitize_id(self, obj_id):
return filters.get_int_or_uuid(obj_id)

View File

@ -36,6 +36,21 @@ NAMESPACE = "horizon:project:floating_ips"
class FloatingIpViewTests(test.TestCase):
# TODO(amotoki): [drop-nova-network] self.floating_ips (with integer
# ID) is no longer needed. self.floating_ips_uuid should be renamed
# self.floating_ips.
# floating IP test data in nova_data.py needs to be dropped as well.
def setUp(self):
super(FloatingIpViewTests, self).setUp()
self._floating_ips_orig = self.floating_ips
self.floating_ips = self.floating_ips_uuid
def tearDown(self):
self.floating_ips = self._floating_ips_orig
super(FloatingIpViewTests, self).tearDown()
@test.create_stubs({api.network: ('floating_ip_target_list',
'tenant_floating_ip_list',)})
def test_associate(self):
@ -237,10 +252,6 @@ class FloatingIpViewTests(test.TestCase):
quotas.tenant_quota_usages(
IsA(http.HttpRequest)).MultipleTimes() \
.AndReturn(quota_data)
api.base.is_service_enabled(
IsA(http.HttpRequest),
'network').MultipleTimes() \
.AndReturn(True)
self.mox.ReplayAll()
@ -279,10 +290,6 @@ class FloatingIpViewTests(test.TestCase):
quotas.tenant_quota_usages(
IsA(http.HttpRequest)).MultipleTimes() \
.AndReturn(quota_data)
api.base.is_service_enabled(
IsA(http.HttpRequest),
'network').MultipleTimes() \
.AndReturn(True)
self.mox.ReplayAll()
@ -295,17 +302,6 @@ class FloatingIpViewTests(test.TestCase):
self.assertEqual('Allocate IP To Project (Quota exceeded)',
six.text_type(allocate_action.verbose_name))
class FloatingIpNeutronViewTests(FloatingIpViewTests):
def setUp(self):
super(FloatingIpViewTests, self).setUp()
self._floating_ips_orig = self.floating_ips
self.floating_ips = self.floating_ips_uuid
def tearDown(self):
self.floating_ips = self._floating_ips_orig
super(FloatingIpViewTests, self).tearDown()
@test.create_stubs({api.nova: ('tenant_quota_get', 'flavor_list',
'server_list'),
api.network: ('floating_ip_pools_list',

View File

@ -35,7 +35,7 @@ class AssociateIPAction(workflows.Action):
coerce=filters.get_int_or_uuid,
empty_value=None,
add_item_link=ALLOCATE_URL)
instance_id = forms.ThemableChoiceField(label=_("Instance"))
instance_id = forms.ThemableChoiceField(label=_("Port to be associated"))
class Meta(object):
name = _("IP Address")
@ -44,11 +44,6 @@ class AssociateIPAction(workflows.Action):
def __init__(self, *args, **kwargs):
super(AssociateIPAction, self).__init__(*args, **kwargs)
if api.base.is_service_enabled(self.request, 'network'):
label = _("Port to be associated")
else:
label = _("Instance to be associated")
self.fields['instance_id'].label = label
# If AssociateIP is invoked from instance menu, instance_id parameter
# is passed in URL. In Neutron based Floating IP implementation
@ -101,6 +96,7 @@ class AssociateIPAction(workflows.Action):
redirect=redirect)
return targets
# TODO(amotoki): [drop-nova-network] Rename instance_id to port_id
def populate_instance_id_choices(self, request, context):
targets = self._get_target_list()
@ -111,19 +107,10 @@ class AssociateIPAction(workflows.Action):
# Sort instances for easy browsing
instances = sorted(instances, key=lambda x: x[1])
neutron_enabled = api.base.is_service_enabled(request, 'network')
if instances:
if neutron_enabled:
label = _("Select a port")
else:
label = _("Select an instance")
instances.insert(0, ("", label))
instances.insert(0, ("", _("Select a port")))
else:
if neutron_enabled:
label = _("No ports available")
else:
label = _("No instances available")
instances = (("", label),)
instances = (("", _("No ports available")),)
return instances

View File

@ -626,11 +626,11 @@ class AssociateIP(policy.PolicyTargetMixin, tables.LinkAction):
url = "horizon:project:floating_ips:associate"
classes = ("ajax-modal",)
icon = "link"
# Nova doesn't support floating ip actions policy, update this
# when bug #1610520 resloved
policy_rules = (("compute", "os_compute_api:os-floating-ips"),)
policy_rules = (("network", "update_floatingip"),)
def allowed(self, request, instance):
if not api.base.is_service_enabled(request, 'network'):
return False
if not api.network.floating_ip_supported(request):
return False
if api.network.floating_ip_simple_associate_supported(request):
@ -653,49 +653,20 @@ class AssociateIP(policy.PolicyTargetMixin, tables.LinkAction):
return "?".join([base_url, params])
class SimpleAssociateIP(policy.PolicyTargetMixin, tables.Action):
name = "associate-simple"
verbose_name = _("Associate Floating IP")
icon = "link"
# Nova doesn't support floating ip actions policy, update this
# when bug #1610520 resloved
policy_rules = (("compute", "os_compute_api:os-floating-ips"),)
def allowed(self, request, instance):
if not api.network.floating_ip_simple_associate_supported(request):
return False
if instance.status == "ERROR":
return False
return not is_deleting(instance)
def single(self, table, request, instance_id):
try:
# target_id is port_id for Neutron and instance_id for Nova Network
# (Neutron API wrapper returns a 'portid_fixedip' string)
target_id = api.network.floating_ip_target_get_by_instance(
request, instance_id).split('_')[0]
fip = api.network.tenant_floating_ip_allocate(request)
api.network.floating_ip_associate(request, fip.id, target_id)
messages.success(request,
_("Successfully associated floating IP: %s")
% fip.ip)
except Exception:
exceptions.handle(request,
_("Unable to associate floating IP."))
return shortcuts.redirect(request.get_full_path())
# TODO(amotoki): [drop-nova-network] The current SimpleDisassociateIP
# just disassociates the first found FIP. It looks better to have a form
# which allows to choose which FIP should be disassociated.
# HORIZON_CONFIG['simple_ip_management'] can be dropped then.
class SimpleDisassociateIP(policy.PolicyTargetMixin, tables.Action):
name = "disassociate"
verbose_name = _("Disassociate Floating IP")
classes = ("btn-disassociate",)
# Nova doesn't support floating ip actions policy, update this
# when bug #1610520 resloved
policy_rules = (("compute", "os_compute_api:os-floating-ips"),)
policy_rules = (("network", "update_floatingip"),)
action_type = "danger"
def allowed(self, request, instance):
if not api.base.is_service_enabled(request, 'network'):
return False
if not api.network.floating_ip_supported(request):
return False
if not conf.HORIZON_CONFIG["simple_ip_management"]:
@ -1278,7 +1249,7 @@ class InstancesTable(tables.DataTable):
table_actions = launch_actions + (DeleteInstance,
InstancesFilterAction)
row_actions = (StartInstance, ConfirmResize, RevertResize,
CreateSnapshot, SimpleAssociateIP, AssociateIP,
CreateSnapshot, AssociateIP,
SimpleDisassociateIP, AttachInterface,
DetachInterface, EditInstance, AttachVolume,
DetachVolume, UpdateMetadata, DecryptInstancePassword,

View File

@ -3537,40 +3537,6 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
def test_select_default_keypair_if_only_one_glance_v1(self):
self.test_select_default_keypair_if_only_one()
@helpers.create_stubs({api.network: ('floating_ip_target_get_by_instance',
'tenant_floating_ip_allocate',
'floating_ip_associate',
'servers_update_addresses',),
api.glance: ('image_list_detailed',),
api.nova: ('server_list',
'flavor_list')})
def test_associate_floating_ip(self):
servers = self.servers.list()
server = servers[0]
fip = self.q_floating_ips.first()
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
.AndReturn([servers, False])
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
api.glance.image_list_detailed(IgnoreArg()) \
.AndReturn((self.images.list(), False, False))
api.network.floating_ip_target_get_by_instance(
IsA(http.HttpRequest),
server.id).AndReturn(server.id)
api.network.tenant_floating_ip_allocate(
IsA(http.HttpRequest)).AndReturn(fip)
api.network.floating_ip_associate(
IsA(http.HttpRequest), fip.id, server.id)
self.mox.ReplayAll()
formData = {'action': 'instances__associate-simple__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
@helpers.create_stubs({api.network: ('floating_ip_target_list_by_instance',
'tenant_floating_ip_list',
'floating_ip_disassociate',