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:
parent
4f654e30c3
commit
99849ad88f
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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 %}
|
|
@ -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)
|
||||||
|
@ -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')),
|
||||||
]
|
]
|
||||||
|
27
openstack_dashboard/dashboards/project/floating_ips/panel.py
Normal file
27
openstack_dashboard/dashboards/project/floating_ips/panel.py
Normal 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)
|
@ -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):
|
@ -0,0 +1,6 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include 'project/floating_ips/_allocate.html' %}
|
||||||
|
{% endblock %}
|
@ -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')
|
@ -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'),
|
||||||
]
|
]
|
@ -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
|
@ -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):
|
@ -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"),)
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
PANEL_DASHBOARD = 'project'
|
||||||
|
PANEL_GROUP = 'network'
|
||||||
|
PANEL = 'floating_ips'
|
||||||
|
|
||||||
|
ADD_PANEL = \
|
||||||
|
'openstack_dashboard.dashboards.project.floating_ips.panel.FloatingIps'
|
Loading…
Reference in New Issue
Block a user