Move Floating IPs from Access & Security to panel

This patch makes the Floating IPs tab in Access & Security its own panel
under Project > Network

Change-Id: Ibb83ae5a0448d2824c10f867e620cec8219b7b72
Implements: blueprint reorganise-access-and-security
This commit is contained in:
Rob Cresswell 2017-01-26 11:17:59 +00:00
parent 4f654e30c3
commit 99849ad88f
18 changed files with 125 additions and 231 deletions

View File

@ -23,9 +23,9 @@ from horizon import messages
from horizon import tables from horizon import tables
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.dashboards.project.floating_ips \
import tables as project_tables
from openstack_dashboard import policy from openstack_dashboard import policy
from openstack_dashboard.dashboards.project.access_and_security.\
floating_ips import tables as project_tables
from openstack_dashboard.utils import filters from openstack_dashboard.utils import filters

View File

@ -32,8 +32,8 @@ from openstack_dashboard.dashboards.admin.floating_ips \
import forms as fip_forms import forms as fip_forms
from openstack_dashboard.dashboards.admin.floating_ips \ from openstack_dashboard.dashboards.admin.floating_ips \
import tables as fip_tables import tables as fip_tables
from openstack_dashboard.dashboards.project.access_and_security.\ from openstack_dashboard.dashboards.project.floating_ips \
floating_ips import tables as project_tables import tables as project_tables
def get_floatingip_pools(request): def get_floatingip_pools(request):

View File

@ -25,10 +25,6 @@ from horizon import tabs
from neutronclient.common import exceptions as neutron_exc from neutronclient.common import exceptions as neutron_exc
from openstack_dashboard.api import network from openstack_dashboard.api import network
from openstack_dashboard.api import nova
from openstack_dashboard.dashboards.project.access_and_security.\
floating_ips.tables import FloatingIPsTable
from openstack_dashboard.dashboards.project.access_and_security.\ from openstack_dashboard.dashboards.project.access_and_security.\
security_groups.tables import SecurityGroupsTable security_groups.tables import SecurityGroupsTable
@ -53,60 +49,7 @@ class SecurityGroupsTab(tabs.TableTab):
return sorted(security_groups, key=lambda group: group.name) return sorted(security_groups, key=lambda group: group.name)
class FloatingIPsTab(tabs.TableTab):
table_classes = (FloatingIPsTable,)
name = _("Floating IPs")
slug = "floating_ips_tab"
template_name = "horizon/common/_detail_table.html"
permissions = ('openstack.services.compute',)
def get_floating_ips_data(self):
try:
floating_ips = network.tenant_floating_ip_list(self.request)
except neutron_exc.ConnectionFailed:
floating_ips = []
exceptions.handle(self.request)
except Exception:
floating_ips = []
exceptions.handle(self.request,
_('Unable to retrieve floating IP addresses.'))
try:
floating_ip_pools = network.floating_ip_pools_list(self.request)
except neutron_exc.ConnectionFailed:
floating_ip_pools = []
exceptions.handle(self.request)
except Exception:
floating_ip_pools = []
exceptions.handle(self.request,
_('Unable to retrieve floating IP pools.'))
pool_dict = dict([(obj.id, obj.name) for obj in floating_ip_pools])
attached_instance_ids = [ip.instance_id for ip in floating_ips
if ip.instance_id is not None]
if attached_instance_ids:
instances = []
try:
# TODO(tsufiev): we should pass attached_instance_ids to
# nova.server_list as soon as Nova API allows for this
instances, has_more = nova.server_list(self.request)
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve instance list.'))
instances_dict = dict([(obj.id, obj.name) for obj in instances])
for ip in floating_ips:
ip.instance_name = instances_dict.get(ip.instance_id)
ip.pool_name = pool_dict.get(ip.pool, ip.pool)
return floating_ips
def allowed(self, request):
return network.floating_ip_supported(request)
class AccessAndSecurityTabs(tabs.TabGroup): class AccessAndSecurityTabs(tabs.TabGroup):
slug = "access_security_tabs" slug = "access_security_tabs"
tabs = (SecurityGroupsTab, FloatingIPsTab) tabs = (SecurityGroupsTab,)
sticky = True sticky = True

View File

@ -1,7 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Allocate Floating IP" %}{% endblock %}
{% block main %}
{% include 'project/access_and_security/floating_ips/_allocate.html' %}
{% endblock %}

View File

@ -23,7 +23,6 @@ from django import http
from mox3.mox import IsA # noqa from mox3.mox import IsA # noqa
import six import six
from horizon.workflows import views
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas from openstack_dashboard.usage import quotas
@ -35,32 +34,14 @@ class AccessAndSecurityTests(test.TestCase):
def setUp(self): def setUp(self):
super(AccessAndSecurityTests, self).setUp() super(AccessAndSecurityTests, self).setUp()
@test.create_stubs({api.network: ('floating_ip_supported', @test.create_stubs({api.network: ('security_group_list',),
'tenant_floating_ip_list',
'floating_ip_pools_list',
'security_group_list',),
api.nova: ('server_list',),
api.base: ('is_service_enabled',), api.base: ('is_service_enabled',),
quotas: ('tenant_quota_usages',)}) quotas: ('tenant_quota_usages',)})
def _test_index(self, instanceless_ips=False): def _test_index(self):
sec_groups = self.security_groups.list() sec_groups = self.security_groups.list()
floating_ips = self.floating_ips.list()
floating_pools = self.pools.list()
if instanceless_ips:
for fip in floating_ips:
fip.instance_id = None
quota_data = self.quota_usages.first() quota_data = self.quota_usages.first()
quota_data['security_groups']['available'] = 10 quota_data['security_groups']['available'] = 10
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
if not instanceless_ips:
api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(floating_ips)
api.network.floating_ip_pools_list(IsA(http.HttpRequest)) \
.AndReturn(floating_pools)
api.network.security_group_list(IsA(http.HttpRequest)) \ api.network.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(sec_groups) .AndReturn(sec_groups)
quotas.tenant_quota_usages(IsA(http.HttpRequest)).MultipleTimes() \ quotas.tenant_quota_usages(IsA(http.HttpRequest)).MultipleTimes() \
@ -74,8 +55,6 @@ class AccessAndSecurityTests(test.TestCase):
res = self.client.get(INDEX_URL) res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'project/access_and_security/index.html') self.assertTemplateUsed(res, 'project/access_and_security/index.html')
self.assertItemsEqual(res.context['floating_ips_table'].data,
floating_ips)
# Security groups # Security groups
sec_groups_from_ctx = res.context['security_groups_table'].data sec_groups_from_ctx = res.context['security_groups_table'].data
@ -93,81 +72,22 @@ class AccessAndSecurityTests(test.TestCase):
def test_index(self): def test_index(self):
self._test_index() self._test_index()
def test_index_with_instanceless_fips(self):
self._test_index(instanceless_ips=True)
@test.create_stubs({api.network: ('floating_ip_target_list',
'tenant_floating_ip_list',)})
def test_association(self):
servers = [api.nova.Server(s, self.request)
for s in self.servers.list()]
# Add duplicate instance name to test instance name with [ID]
# Change id and private IP
server3 = api.nova.Server(self.servers.first(), self.request)
server3.id = 101
server3.addresses = deepcopy(server3.addresses)
server3.addresses['private'][0]['addr'] = "10.0.0.5"
servers.append(server3)
targets = [api.nova.FloatingIpTarget(s) for s in servers]
api.network.tenant_floating_ip_list(
IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.network.floating_ip_target_list(
IsA(http.HttpRequest)) \
.AndReturn(targets)
self.mox.ReplayAll()
res = self.client.get(reverse("horizon:project:access_and_security:"
"floating_ips:associate"))
self.assertTemplateUsed(res, views.WorkflowView.template_name)
self.assertContains(res, '<option value="1">server_1 (1)</option>')
self.assertContains(res, '<option value="101">server_1 (101)</option>')
self.assertContains(res, '<option value="2">server_2 (2)</option>')
class AccessAndSecurityNeutronProxyTests(AccessAndSecurityTests):
def setUp(self):
super(AccessAndSecurityNeutronProxyTests, self).setUp()
self.floating_ips = self.floating_ips_uuid
class SecurityGroupTabTests(test.TestCase): class SecurityGroupTabTests(test.TestCase):
def setUp(self): def setUp(self):
super(SecurityGroupTabTests, self).setUp() super(SecurityGroupTabTests, self).setUp()
@test.create_stubs({api.network: ('floating_ip_supported', @test.create_stubs({api.network: ('security_group_list',),
'tenant_floating_ip_list',
'security_group_list',
'floating_ip_pools_list',),
api.nova: ('server_list',),
quotas: ('tenant_quota_usages',), quotas: ('tenant_quota_usages',),
api.base: ('is_service_enabled',)}) api.base: ('is_service_enabled',)})
def test_create_button_attributes(self): def test_create_button_attributes(self):
floating_ips = self.floating_ips.list()
floating_pools = self.pools.list()
sec_groups = self.security_groups.list() sec_groups = self.security_groups.list()
quota_data = self.quota_usages.first() quota_data = self.quota_usages.first()
quota_data['security_groups']['available'] = 10 quota_data['security_groups']['available'] = 10
api.network.floating_ip_supported(
IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(
IsA(http.HttpRequest)) \
.AndReturn(floating_ips)
api.network.floating_ip_pools_list(
IsA(http.HttpRequest)) \
.AndReturn(floating_pools)
api.network.security_group_list( api.network.security_group_list(
IsA(http.HttpRequest)) \ IsA(http.HttpRequest)) \
.AndReturn(sec_groups) .AndReturn(sec_groups)
api.nova.server_list(
IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
quotas.tenant_quota_usages( quotas.tenant_quota_usages(
IsA(http.HttpRequest)).MultipleTimes() \ IsA(http.HttpRequest)).MultipleTimes() \
.AndReturn(quota_data) .AndReturn(quota_data)
@ -195,36 +115,18 @@ class SecurityGroupTabTests(test.TestCase):
url = 'horizon:project:access_and_security:security_groups:create' url = 'horizon:project:access_and_security:security_groups:create'
self.assertEqual(url, create_action.url) self.assertEqual(url, create_action.url)
@test.create_stubs({api.network: ('floating_ip_supported', @test.create_stubs({api.network: ('security_group_list',),
'tenant_floating_ip_list',
'security_group_list',
'floating_ip_pools_list',),
api.nova: ('server_list',),
quotas: ('tenant_quota_usages',), quotas: ('tenant_quota_usages',),
api.base: ('is_service_enabled',)}) api.base: ('is_service_enabled',)})
def _test_create_button_disabled_when_quota_exceeded(self, def _test_create_button_disabled_when_quota_exceeded(self,
network_enabled): network_enabled):
floating_ips = self.floating_ips.list()
floating_pools = self.pools.list()
sec_groups = self.security_groups.list() sec_groups = self.security_groups.list()
quota_data = self.quota_usages.first() quota_data = self.quota_usages.first()
quota_data['security_groups']['available'] = 0 quota_data['security_groups']['available'] = 0
api.network.floating_ip_supported(
IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(
IsA(http.HttpRequest)) \
.AndReturn(floating_ips)
api.network.floating_ip_pools_list(
IsA(http.HttpRequest)) \
.AndReturn(floating_pools)
api.network.security_group_list( api.network.security_group_list(
IsA(http.HttpRequest)) \ IsA(http.HttpRequest)) \
.AndReturn(sec_groups) .AndReturn(sec_groups)
api.nova.server_list(
IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
quotas.tenant_quota_usages( quotas.tenant_quota_usages(
IsA(http.HttpRequest)).MultipleTimes() \ IsA(http.HttpRequest)).MultipleTimes() \
.AndReturn(quota_data) .AndReturn(quota_data)

View File

@ -19,8 +19,6 @@
from django.conf.urls import include from django.conf.urls import include
from django.conf.urls import url from django.conf.urls import url
from openstack_dashboard.dashboards.project.access_and_security.\
floating_ips import urls as fip_urls
from openstack_dashboard.dashboards.project.access_and_security.\ from openstack_dashboard.dashboards.project.access_and_security.\
security_groups import urls as sec_group_urls security_groups import urls as sec_group_urls
from openstack_dashboard.dashboards.project.access_and_security import views from openstack_dashboard.dashboards.project.access_and_security import views
@ -28,7 +26,6 @@ from openstack_dashboard.dashboards.project.access_and_security import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'), url(r'^$', views.IndexView.as_view(), name='index'),
url(r'floating_ips/', include(fip_urls, namespace='floating_ips')),
url(r'security_groups/', url(r'security_groups/',
include(sec_group_urls, namespace='security_groups')), include(sec_group_urls, namespace='security_groups')),
] ]

View File

@ -0,0 +1,27 @@
# Copyright 2017 Cisco Systems, Inc.
#
# 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 django.conf import settings
from django.utils.translation import ugettext_lazy as _
import horizon
class FloatingIps(horizon.Panel):
name = _("Floating IPs")
slug = 'floating_ips'
@staticmethod
def can_register():
network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
return network_config.get('enable_router', True)

View File

@ -41,10 +41,10 @@ class AllocateIP(tables.LinkAction):
verbose_name = _("Allocate IP To Project") verbose_name = _("Allocate IP To Project")
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "link" icon = "link"
url = "horizon:project:access_and_security:floating_ips:allocate" url = "horizon:project:floating_ips:allocate"
def single(self, data_table, request, *args): def single(self, data_table, request, *args):
return shortcuts.redirect('horizon:project:access_and_security:index') return shortcuts.redirect('horizon:project:floating_ips:index')
def allowed(self, request, fip=None): def allowed(self, request, fip=None):
usages = quotas.tenant_quota_usages(request) usages = quotas.tenant_quota_usages(request)
@ -106,7 +106,7 @@ class ReleaseIPs(tables.BatchAction):
class AssociateIP(tables.LinkAction): class AssociateIP(tables.LinkAction):
name = "associate" name = "associate"
verbose_name = _("Associate") verbose_name = _("Associate")
url = "horizon:project:access_and_security:floating_ips:associate" url = "horizon:project:floating_ips:associate"
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "link" icon = "link"
@ -152,7 +152,7 @@ class DisassociateIP(tables.Action):
except Exception: except Exception:
exceptions.handle(request, exceptions.handle(request,
_('Unable to disassociate floating IP.')) _('Unable to disassociate floating IP.'))
return shortcuts.redirect('horizon:project:access_and_security:index') return shortcuts.redirect('horizon:project:floating_ips:index')
def get_instance_info(fip): def get_instance_info(fip):

View File

@ -0,0 +1,6 @@
{% extends 'base.html' %}
{% load i18n %}
{% block main %}
{% include 'project/floating_ips/_allocate.html' %}
{% endblock %}

View File

@ -31,8 +31,8 @@ from openstack_dashboard.usage import quotas
from horizon.workflows import views from horizon.workflows import views
INDEX_URL = reverse('horizon:project:access_and_security:index') INDEX_URL = reverse('horizon:project:floating_ips:index')
NAMESPACE = "horizon:project:access_and_security:floating_ips" NAMESPACE = "horizon:project:floating_ips"
class FloatingIpViewTests(test.TestCase): class FloatingIpViewTests(test.TestCase):
@ -167,7 +167,6 @@ class FloatingIpViewTests(test.TestCase):
@test.create_stubs({api.nova: ('server_list',), @test.create_stubs({api.nova: ('server_list',),
api.network: ('floating_ip_disassociate', api.network: ('floating_ip_disassociate',
'floating_ip_supported',
'tenant_floating_ip_get', 'tenant_floating_ip_get',
'tenant_floating_ip_list',), 'tenant_floating_ip_list',),
api.neutron: ('is_extension_supported',)}) api.neutron: ('is_extension_supported',)})
@ -176,8 +175,6 @@ class FloatingIpViewTests(test.TestCase):
api.nova.server_list(IsA(http.HttpRequest)) \ api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False]) .AndReturn([self.servers.list(), False])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list()) .AndReturn(self.floating_ips.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest), api.neutron.is_extension_supported(IsA(http.HttpRequest),
@ -194,7 +191,6 @@ class FloatingIpViewTests(test.TestCase):
@test.create_stubs({api.nova: ('server_list',), @test.create_stubs({api.nova: ('server_list',),
api.network: ('floating_ip_disassociate', api.network: ('floating_ip_disassociate',
'floating_ip_supported',
'tenant_floating_ip_get', 'tenant_floating_ip_get',
'tenant_floating_ip_list',), 'tenant_floating_ip_list',),
api.neutron: ('is_extension_supported',)}) api.neutron: ('is_extension_supported',)})
@ -203,8 +199,6 @@ class FloatingIpViewTests(test.TestCase):
api.nova.server_list(IsA(http.HttpRequest)) \ api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False]) .AndReturn([self.servers.list(), False])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list()) .AndReturn(self.floating_ips.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest), api.neutron.is_extension_supported(IsA(http.HttpRequest),
@ -220,9 +214,7 @@ class FloatingIpViewTests(test.TestCase):
res = self.client.post(INDEX_URL, {"action": action}) res = self.client.post(INDEX_URL, {"action": action})
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.network: ('floating_ip_supported', @test.create_stubs({api.network: ('tenant_floating_ip_list',
'tenant_floating_ip_list',
'security_group_list',
'floating_ip_pools_list',), 'floating_ip_pools_list',),
api.nova: ('server_list',), api.nova: ('server_list',),
quotas: ('tenant_quota_usages',), quotas: ('tenant_quota_usages',),
@ -232,17 +224,10 @@ class FloatingIpViewTests(test.TestCase):
floating_pools = self.pools.list() floating_pools = self.pools.list()
quota_data = self.quota_usages.first() quota_data = self.quota_usages.first()
quota_data['floating_ips']['available'] = 10 quota_data['floating_ips']['available'] = 10
sec_groups = self.security_groups.list()
api.network.floating_ip_supported(
IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list( api.network.tenant_floating_ip_list(
IsA(http.HttpRequest)) \ IsA(http.HttpRequest)) \
.AndReturn(floating_ips) .AndReturn(floating_ips)
api.network.security_group_list(
IsA(http.HttpRequest)).MultipleTimes()\
.AndReturn(sec_groups)
api.network.floating_ip_pools_list( api.network.floating_ip_pools_list(
IsA(http.HttpRequest)) \ IsA(http.HttpRequest)) \
.AndReturn(floating_pools) .AndReturn(floating_pools)
@ -252,7 +237,6 @@ class FloatingIpViewTests(test.TestCase):
quotas.tenant_quota_usages( quotas.tenant_quota_usages(
IsA(http.HttpRequest)).MultipleTimes() \ IsA(http.HttpRequest)).MultipleTimes() \
.AndReturn(quota_data) .AndReturn(quota_data)
api.base.is_service_enabled( api.base.is_service_enabled(
IsA(http.HttpRequest), IsA(http.HttpRequest),
'network').MultipleTimes() \ 'network').MultipleTimes() \
@ -260,8 +244,7 @@ class FloatingIpViewTests(test.TestCase):
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(INDEX_URL + res = self.client.get(INDEX_URL)
"?tab=access_security_tabs__floating_ips_tab")
allocate_action = self.getAndAssertTableAction(res, 'floating_ips', allocate_action = self.getAndAssertTableAction(res, 'floating_ips',
'allocate') 'allocate')
@ -270,12 +253,10 @@ class FloatingIpViewTests(test.TestCase):
six.text_type(allocate_action.verbose_name)) six.text_type(allocate_action.verbose_name))
self.assertIsNone(allocate_action.policy_rules) self.assertIsNone(allocate_action.policy_rules)
url = 'horizon:project:access_and_security:floating_ips:allocate' url = 'horizon:project:floating_ips:allocate'
self.assertEqual(url, allocate_action.url) self.assertEqual(url, allocate_action.url)
@test.create_stubs({api.network: ('floating_ip_supported', @test.create_stubs({api.network: ('tenant_floating_ip_list',
'tenant_floating_ip_list',
'security_group_list',
'floating_ip_pools_list',), 'floating_ip_pools_list',),
api.nova: ('server_list',), api.nova: ('server_list',),
quotas: ('tenant_quota_usages',), quotas: ('tenant_quota_usages',),
@ -285,17 +266,10 @@ class FloatingIpViewTests(test.TestCase):
floating_pools = self.pools.list() floating_pools = self.pools.list()
quota_data = self.quota_usages.first() quota_data = self.quota_usages.first()
quota_data['floating_ips']['available'] = 0 quota_data['floating_ips']['available'] = 0
sec_groups = self.security_groups.list()
api.network.floating_ip_supported(
IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list( api.network.tenant_floating_ip_list(
IsA(http.HttpRequest)) \ IsA(http.HttpRequest)) \
.AndReturn(floating_ips) .AndReturn(floating_ips)
api.network.security_group_list(
IsA(http.HttpRequest)).MultipleTimes()\
.AndReturn(sec_groups)
api.network.floating_ip_pools_list( api.network.floating_ip_pools_list(
IsA(http.HttpRequest)) \ IsA(http.HttpRequest)) \
.AndReturn(floating_pools) .AndReturn(floating_pools)
@ -305,7 +279,6 @@ class FloatingIpViewTests(test.TestCase):
quotas.tenant_quota_usages( quotas.tenant_quota_usages(
IsA(http.HttpRequest)).MultipleTimes() \ IsA(http.HttpRequest)).MultipleTimes() \
.AndReturn(quota_data) .AndReturn(quota_data)
api.base.is_service_enabled( api.base.is_service_enabled(
IsA(http.HttpRequest), IsA(http.HttpRequest),
'network').MultipleTimes() \ 'network').MultipleTimes() \
@ -313,8 +286,7 @@ class FloatingIpViewTests(test.TestCase):
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(INDEX_URL + res = self.client.get(INDEX_URL)
"?tab=access_security_tabs__floating_ips_tab")
allocate_action = self.getAndAssertTableAction(res, 'floating_ips', allocate_action = self.getAndAssertTableAction(res, 'floating_ips',
'allocate') 'allocate')

View File

@ -18,11 +18,10 @@
from django.conf.urls import url from django.conf.urls import url
from openstack_dashboard.dashboards.project.access_and_security.\ from openstack_dashboard.dashboards.project.floating_ips import views
floating_ips import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^associate/$', views.AssociateView.as_view(), name='associate'), url(r'^associate/$', views.AssociateView.as_view(), name='associate'),
url(r'^allocate/$', views.AllocateView.as_view(), name='allocate'), url(r'^allocate/$', views.AllocateView.as_view(), name='allocate'),
] ]

View File

@ -28,15 +28,18 @@ from neutronclient.common import exceptions as neutron_exc
from horizon import exceptions from horizon import exceptions
from horizon import forms from horizon import forms
from horizon import tables
from horizon import workflows from horizon import workflows
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.usage import quotas from openstack_dashboard.usage import quotas
from openstack_dashboard.dashboards.project.access_and_security.\ from openstack_dashboard.dashboards.project.floating_ips \
floating_ips import forms as project_forms import forms as project_forms
from openstack_dashboard.dashboards.project.access_and_security.\ from openstack_dashboard.dashboards.project.floating_ips \
floating_ips import workflows as project_workflows import tables as project_tables
from openstack_dashboard.dashboards.project.floating_ips \
import workflows as project_workflows
class AssociateView(workflows.WorkflowView): class AssociateView(workflows.WorkflowView):
@ -47,11 +50,10 @@ class AllocateView(forms.ModalFormView):
form_class = project_forms.FloatingIpAllocate form_class = project_forms.FloatingIpAllocate
form_id = "associate_floating_ip_form" form_id = "associate_floating_ip_form"
page_title = _("Allocate Floating IP") page_title = _("Allocate Floating IP")
template_name = 'project/access_and_security/floating_ips/allocate.html' template_name = 'project/floating_ips/allocate.html'
submit_label = _("Allocate IP") submit_label = _("Allocate IP")
submit_url = reverse_lazy( submit_url = reverse_lazy("horizon:project:floating_ips:allocate")
"horizon:project:access_and_security:floating_ips:allocate") success_url = reverse_lazy('horizon:project:floating_ips:index')
success_url = reverse_lazy('horizon:project:access_and_security:index')
def get_object_display(self, obj): def get_object_display(self, obj):
return obj.ip return obj.ip
@ -78,3 +80,51 @@ class AllocateView(forms.ModalFormView):
if not pool_list: if not pool_list:
pool_list = [(None, _("No floating IP pools available"))] pool_list = [(None, _("No floating IP pools available"))]
return {'pool_list': pool_list} return {'pool_list': pool_list}
class IndexView(tables.DataTableView):
table_class = project_tables.FloatingIPsTable
page_title = _("Floating IPs")
def get_data(self):
try:
floating_ips = api.network.tenant_floating_ip_list(self.request)
except neutron_exc.ConnectionFailed:
floating_ips = []
exceptions.handle(self.request)
except Exception:
floating_ips = []
exceptions.handle(self.request,
_('Unable to retrieve floating IP addresses.'))
try:
floating_ip_pools = \
api.network.floating_ip_pools_list(self.request)
except neutron_exc.ConnectionFailed:
floating_ip_pools = []
exceptions.handle(self.request)
except Exception:
floating_ip_pools = []
exceptions.handle(self.request,
_('Unable to retrieve floating IP pools.'))
pool_dict = dict([(obj.id, obj.name) for obj in floating_ip_pools])
attached_instance_ids = [ip.instance_id for ip in floating_ips
if ip.instance_id is not None]
if attached_instance_ids:
instances = []
try:
# TODO(tsufiev): we should pass attached_instance_ids to
# nova.server_list as soon as Nova API allows for this
instances, has_more = api.nova.server_list(self.request)
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve instance list.'))
instances_dict = dict([(obj.id, obj.name) for obj in instances])
for ip in floating_ips:
ip.instance_name = instances_dict.get(ip.instance_id)
ip.pool_name = pool_dict.get(ip.pool, ip.pool)
return floating_ips

View File

@ -26,7 +26,7 @@ from openstack_dashboard import api
from openstack_dashboard.utils import filters from openstack_dashboard.utils import filters
ALLOCATE_URL = "horizon:project:access_and_security:floating_ips:allocate" ALLOCATE_URL = "horizon:project:floating_ips:allocate"
class AssociateIPAction(workflows.Action): class AssociateIPAction(workflows.Action):
@ -72,7 +72,7 @@ class AssociateIPAction(workflows.Action):
def populate_ip_id_choices(self, request, context): def populate_ip_id_choices(self, request, context):
ips = [] ips = []
redirect = reverse('horizon:project:access_and_security:index') redirect = reverse('horizon:project:floating_ips:index')
try: try:
ips = api.network.tenant_floating_ip_list(self.request) ips = api.network.tenant_floating_ip_list(self.request)
except neutron_exc.ConnectionFailed: except neutron_exc.ConnectionFailed:
@ -95,7 +95,7 @@ class AssociateIPAction(workflows.Action):
try: try:
targets = api.network.floating_ip_target_list(self.request) targets = api.network.floating_ip_target_list(self.request)
except Exception: except Exception:
redirect = reverse('horizon:project:access_and_security:index') redirect = reverse('horizon:project:floating_ips:index')
exceptions.handle(self.request, exceptions.handle(self.request,
_('Unable to retrieve instance list.'), _('Unable to retrieve instance list.'),
redirect=redirect) redirect=redirect)
@ -146,7 +146,7 @@ class IPAssociationWorkflow(workflows.Workflow):
finalize_button_name = _("Associate") finalize_button_name = _("Associate")
success_message = _('IP address %s associated.') success_message = _('IP address %s associated.')
failure_message = _('Unable to associate IP address %s.') failure_message = _('Unable to associate IP address %s.')
success_url = "horizon:project:access_and_security:index" success_url = "horizon:project:floating_ips:index"
default_steps = (AssociateIP,) default_steps = (AssociateIP,)
def format_status_message(self, message): def format_status_message(self, message):

View File

@ -36,8 +36,7 @@ from horizon.templatetags import sizeformat
from horizon.utils import filters from horizon.utils import filters
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.dashboards.project.access_and_security.floating_ips \ from openstack_dashboard.dashboards.project.floating_ips import workflows
import workflows
from openstack_dashboard.dashboards.project.instances import tabs from openstack_dashboard.dashboards.project.instances import tabs
from openstack_dashboard.dashboards.project.instances.workflows \ from openstack_dashboard.dashboards.project.instances.workflows \
import resize_instance import resize_instance
@ -617,7 +616,7 @@ class DecryptInstancePassword(tables.LinkAction):
class AssociateIP(policy.PolicyTargetMixin, tables.LinkAction): class AssociateIP(policy.PolicyTargetMixin, tables.LinkAction):
name = "associate" name = "associate"
verbose_name = _("Associate Floating IP") verbose_name = _("Associate Floating IP")
url = "horizon:project:access_and_security:floating_ips:associate" url = "horizon:project:floating_ips:associate"
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "link" icon = "link"
policy_rules = (("compute", "network:associate_floating_ip"),) policy_rules = (("compute", "network:associate_floating_ip"),)

View File

@ -0,0 +1,6 @@
PANEL_DASHBOARD = 'project'
PANEL_GROUP = 'network'
PANEL = 'floating_ips'
ADD_PANEL = \
'openstack_dashboard.dashboards.project.floating_ips.panel.FloatingIps'