Converts nova.access_and_security.keypairs to new tables and modals.
Change-Id: I413b81ba189fc12b957f96aa7d2d14016672db6a
This commit is contained in:
parent
d048d47b99
commit
f0b6114bbd
@ -97,9 +97,6 @@ class FloatingIpViewTests(test.BaseViewTests):
|
||||
api.server_add_floating_ip(IsA(http.HttpRequest), IsA(unicode),
|
||||
IsA(unicode)).\
|
||||
AndReturn(None)
|
||||
self.mox.StubOutWithMock(messages, 'info')
|
||||
messages.info(IsA(http.HttpRequest), IsA(unicode))
|
||||
|
||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
||||
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
|
||||
api.tenant_floating_ip_get(IsA(http.HttpRequest), str(1)).\
|
||||
@ -129,8 +126,8 @@ class FloatingIpViewTests(test.BaseViewTests):
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).\
|
||||
AndReturn(self.security_groups)
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
|
||||
self.mox.StubOutWithMock(api, 'server_add_floating_ip')
|
||||
api.server_add_floating_ip = self.mox.CreateMockAnything()
|
||||
@ -140,9 +137,6 @@ class FloatingIpViewTests(test.BaseViewTests):
|
||||
IsA(unicode)).\
|
||||
AndRaise(exception)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'error')
|
||||
messages.error(IsA(http.HttpRequest), IsA(basestring))
|
||||
|
||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
||||
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
|
||||
api.tenant_floating_ip_get(IsA(http.HttpRequest), IsA(unicode)).\
|
||||
@ -174,8 +168,8 @@ class FloatingIpViewTests(test.BaseViewTests):
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).\
|
||||
AndReturn(self.security_groups)
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
|
||||
self.mox.StubOutWithMock(api, 'server_remove_floating_ip')
|
||||
api.server_remove_floating_ip = self.mox.CreateMockAnything()
|
||||
@ -204,8 +198,8 @@ class FloatingIpViewTests(test.BaseViewTests):
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).\
|
||||
AndReturn(self.security_groups)
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
|
||||
self.mox.StubOutWithMock(api, 'server_remove_floating_ip')
|
||||
exception = novaclient_exceptions.ClientException('ClientException',
|
||||
@ -213,9 +207,6 @@ class FloatingIpViewTests(test.BaseViewTests):
|
||||
api.server_remove_floating_ip(IsA(http.HttpRequest),
|
||||
IsA(int),
|
||||
IsA(int)).AndRaise(exception)
|
||||
self.mox.StubOutWithMock(messages, 'error')
|
||||
messages.error(IsA(http.HttpRequest), IsA(basestring))
|
||||
|
||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
||||
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
|
||||
api.tenant_floating_ip_get(IsA(http.HttpRequest), IsA(unicode)).\
|
||||
|
@ -0,0 +1,81 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 Nebula, 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.
|
||||
|
||||
import logging
|
||||
|
||||
from django import shortcuts
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from horizon import api
|
||||
from horizon import tables
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DeleteKeyPairs(tables.Action):
|
||||
name = "delete"
|
||||
verbose_name = _("Delete")
|
||||
verbose_name_plural = _("Delete Keypairs")
|
||||
classes = ("danger",)
|
||||
|
||||
def handle(self, data_table, request, object_ids):
|
||||
failures = 0
|
||||
deleted = []
|
||||
for obj_id in object_ids:
|
||||
try:
|
||||
api.nova.keypair_delete(request, obj_id)
|
||||
deleted.append(obj_id)
|
||||
except Exception, e:
|
||||
failures += 1
|
||||
messages.error(request, _("Error deleting keypair: %s") % e)
|
||||
LOG.exception("Error deleting keypair.")
|
||||
if failures:
|
||||
messages.info(request, _("Deleted the following keypairs: %s")
|
||||
% ", ".join(deleted))
|
||||
else:
|
||||
messages.success(request, _("Successfully deleted keypairs: %s")
|
||||
% ", ".join(deleted))
|
||||
return shortcuts.redirect('horizon:nova:access_and_security:index')
|
||||
|
||||
|
||||
class ImportKeyPair(tables.LinkAction):
|
||||
name = "import"
|
||||
verbose_name = _("Import Keypair")
|
||||
url = "horizon:nova:access_and_security:keypairs:import"
|
||||
attrs = {"class": "ajax-modal btn"}
|
||||
|
||||
|
||||
class CreateKeyPair(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Keypair")
|
||||
url = "horizon:nova:access_and_security:keypairs:create"
|
||||
attrs = {"class": "ajax-modal btn"}
|
||||
|
||||
|
||||
class KeypairsTable(tables.DataTable):
|
||||
name = tables.Column("name")
|
||||
fingerprint = tables.Column("fingerprint")
|
||||
|
||||
def get_object_id(self, keypair):
|
||||
return keypair.name
|
||||
|
||||
class Meta:
|
||||
name = "keypairs"
|
||||
verbose_name = _("Keypairs")
|
||||
table_actions = (CreateKeyPair, ImportKeyPair, DeleteKeyPairs,)
|
||||
row_actions = (DeleteKeyPairs,)
|
@ -28,6 +28,9 @@ from horizon import api
|
||||
from horizon import test
|
||||
|
||||
|
||||
INDEX_VIEW_URL = reverse('horizon:nova:access_and_security:index')
|
||||
|
||||
|
||||
class KeyPairViewTests(test.BaseViewTests):
|
||||
def setUp(self):
|
||||
super(KeyPairViewTests, self).setUp()
|
||||
@ -38,20 +41,16 @@ class KeyPairViewTests(test.BaseViewTests):
|
||||
def test_delete_keypair(self):
|
||||
KEYPAIR_ID = self.keypairs[0].name
|
||||
formData = {'method': 'DeleteKeypair',
|
||||
'keypair_id': KEYPAIR_ID,
|
||||
}
|
||||
'keypair_id': KEYPAIR_ID}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'keypair_delete')
|
||||
api.keypair_delete(IsA(http.HttpRequest), unicode(KEYPAIR_ID))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(
|
||||
reverse('horizon:nova:access_and_security:index'),
|
||||
formData)
|
||||
res = self.client.post(INDEX_VIEW_URL, formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res,
|
||||
reverse('horizon:nova:access_and_security:index'))
|
||||
self.assertRedirectsNoFollow(res, INDEX_VIEW_URL)
|
||||
|
||||
def test_delete_keypair_exception(self):
|
||||
KEYPAIR_ID = self.keypairs[0].name
|
||||
@ -67,12 +66,9 @@ class KeyPairViewTests(test.BaseViewTests):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(
|
||||
reverse('horizon:nova:access_and_security:index'),
|
||||
formData)
|
||||
res = self.client.post(INDEX_VIEW_URL, formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res,
|
||||
reverse('horizon:nova:access_and_security:index'))
|
||||
self.assertRedirectsNoFollow(res, INDEX_VIEW_URL)
|
||||
|
||||
def test_create_keypair_get(self):
|
||||
res = self.client.get(
|
||||
|
@ -20,10 +20,12 @@
|
||||
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
from .views import IndexView, CreateView, ImportView
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'horizon.dashboards.nova.access_and_security.keypairs.views',
|
||||
url(r'^$', 'index', name='index'),
|
||||
url(r'^create/$', 'create', name='create'),
|
||||
url(r'^import/$', 'import_keypair', name='import'),
|
||||
url(r'^$', IndexView.as_view(), name='index'),
|
||||
url(r'^create/$', CreateView.as_view(), name='create'),
|
||||
url(r'^import/$', ImportView.as_view(), name='import'),
|
||||
)
|
||||
|
@ -31,69 +31,35 @@ from django.utils.translation import ugettext as _
|
||||
from novaclient import exceptions as novaclient_exceptions
|
||||
|
||||
from horizon import api
|
||||
from horizon.dashboards.nova.access_and_security.keypairs.forms import \
|
||||
(CreateKeypair, DeleteKeypair, ImportKeypair)
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
from .forms import CreateKeypair, DeleteKeypair, ImportKeypair
|
||||
from .tables import KeypairsTable
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# FIXME(gabriel): There's a very obvious pattern to these views.
|
||||
# This is a perfect candidate for a class-based view.
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
delete_form, handled = DeleteKeypair.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = KeypairsTable
|
||||
template_name = 'nova/access_and_security/keypairs/index.html'
|
||||
|
||||
def get_data(self):
|
||||
try:
|
||||
keypairs = api.keypair_list(request)
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
keypairs = api.nova.keypair_list(self.request)
|
||||
except Exception, e:
|
||||
keypairs = []
|
||||
LOG.exception("ClientException in keypair index")
|
||||
messages.error(request, _('Error fetching keypairs: %s') % e.message)
|
||||
|
||||
context = {'keypairs': keypairs, 'delete_form': delete_form}
|
||||
|
||||
if request.is_ajax():
|
||||
template = 'nova/access_and_security/keypairs/_list.html'
|
||||
context['hide'] = True
|
||||
else:
|
||||
template = 'nova/access_and_security/keypairs/index.html'
|
||||
|
||||
return shortcuts.render(request, template, context)
|
||||
messages.error(request,
|
||||
_('Error fetching keypairs: %s') % e.message)
|
||||
return keypairs
|
||||
|
||||
|
||||
@login_required
|
||||
def create(request):
|
||||
form, handled = CreateKeypair.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
context = {'form': form}
|
||||
|
||||
if request.is_ajax():
|
||||
template = 'nova/access_and_security/keypairs/_create.html'
|
||||
context['hide'] = True
|
||||
else:
|
||||
template = 'nova/access_and_security/keypairs/create.html'
|
||||
|
||||
return shortcuts.render(request, template, context)
|
||||
class CreateView(forms.ModalFormView):
|
||||
form_class = CreateKeypair
|
||||
template_name = 'nova/access_and_security/keypairs/create.html'
|
||||
|
||||
|
||||
@login_required
|
||||
def import_keypair(request):
|
||||
form, handled = ImportKeypair.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
context = {'form': form}
|
||||
|
||||
if request.is_ajax():
|
||||
template = 'nova/access_and_security/keypairs/_import.html'
|
||||
context['hide'] = True
|
||||
else:
|
||||
template = 'nova/access_and_security/keypairs/import.html'
|
||||
|
||||
return shortcuts.render(request, template, context)
|
||||
class ImportView(forms.ModalFormView):
|
||||
form_class = ImportKeypair
|
||||
template_name = 'nova/access_and_security/keypairs/import.html'
|
||||
|
@ -48,7 +48,7 @@ class AccessAndSecurityTests(test.BaseViewTests):
|
||||
floating_ip.ip = '58.58.58.58'
|
||||
|
||||
self.floating_ip = floating_ip
|
||||
self.floating_ips = [floating_ip, ]
|
||||
self.floating_ips = (floating_ip,)
|
||||
|
||||
security_group = api.SecurityGroup(None)
|
||||
security_group.id = '1'
|
||||
@ -57,13 +57,14 @@ class AccessAndSecurityTests(test.BaseViewTests):
|
||||
|
||||
def test_index(self):
|
||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||
|
||||
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
api.tenant_floating_ip_list(IsA(http.HttpRequest)).\
|
||||
AndReturn(self.floating_ips)
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).\
|
||||
AndReturn(self.security_groups)
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -71,7 +72,8 @@ class AccessAndSecurityTests(test.BaseViewTests):
|
||||
reverse('horizon:nova:access_and_security:index'))
|
||||
|
||||
self.assertTemplateUsed(res, 'nova/access_and_security/index.html')
|
||||
self.assertItemsEqual(res.context['keypairs'], self.keypairs)
|
||||
self.assertItemsEqual(res.context['keypairs_table'].data,
|
||||
self.keypairs)
|
||||
self.assertItemsEqual(res.context['security_groups'],
|
||||
self.security_groups)
|
||||
self.assertItemsEqual(res.context['floating_ips'], self.floating_ips)
|
||||
|
@ -36,14 +36,10 @@ import openstackx.api.exceptions as api_exceptions
|
||||
from horizon import api
|
||||
from horizon import forms
|
||||
from horizon import test
|
||||
from horizon.dashboards.nova.access_and_security.keypairs.forms import \
|
||||
(DeleteKeypair)
|
||||
from horizon.dashboards.nova.access_and_security.security_groups.forms import \
|
||||
(CreateGroup,
|
||||
DeleteGroup)
|
||||
from horizon.dashboards.nova.access_and_security.floating_ips.forms import \
|
||||
(ReleaseFloatingIp,
|
||||
FloatingIpDisassociate,
|
||||
from .keypairs.forms import DeleteKeypair
|
||||
from .keypairs.tables import KeypairsTable
|
||||
from .security_groups.forms import CreateGroup, DeleteGroup
|
||||
from .floating_ips.forms import (ReleaseFloatingIp, FloatingIpDisassociate,
|
||||
FloatingIpAllocate)
|
||||
|
||||
|
||||
@ -53,12 +49,27 @@ LOG = logging.getLogger(__name__)
|
||||
@login_required
|
||||
def index(request):
|
||||
tenant_id = request.user.tenant_id
|
||||
|
||||
for f in (CreateGroup, DeleteGroup, DeleteKeypair, ReleaseFloatingIp,
|
||||
FloatingIpDisassociate, FloatingIpAllocate):
|
||||
_unused, handled = f.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
# NOTE(gabriel): This is all temporary until all tables
|
||||
# in this view are converted to DataTables.
|
||||
try:
|
||||
keypairs = api.nova.keypair_list(request)
|
||||
except Exception, e:
|
||||
keypairs = []
|
||||
LOG.exception("Exception in keypair index")
|
||||
messages.error(request,
|
||||
_('Keypair list is currently unavailable.'))
|
||||
keypairs_table = KeypairsTable(request, keypairs)
|
||||
handled = keypairs_table.maybe_handle()
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
try:
|
||||
security_groups = api.security_group_list(request)
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
@ -73,14 +84,8 @@ def index(request):
|
||||
LOG.exception("ClientException in floating ip index")
|
||||
messages.error(request,
|
||||
_('Error fetching floating ips: %s') % e.message)
|
||||
try:
|
||||
keypairs = api.keypair_list(request)
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
keypairs = []
|
||||
LOG.exception("ClientException in keypair index")
|
||||
messages.error(request, _('Error fetching keypairs: %s') % e.message)
|
||||
|
||||
context = {'keypairs': keypairs,
|
||||
context = {'keypairs_table': keypairs_table,
|
||||
'floating_ips': floating_ips,
|
||||
'security_groups': security_groups,
|
||||
'keypair_delete_form': DeleteKeypair(),
|
||||
|
@ -34,8 +34,8 @@
|
||||
</div>
|
||||
|
||||
<div id="keypairs">
|
||||
{% if keypairs %}
|
||||
{% include 'nova/access_and_security/keypairs/_list.html' %}
|
||||
{% if keypairs_table.data %}
|
||||
{{ keypairs_table.render }}
|
||||
{% else %}
|
||||
<div class="alert-message block-message info">
|
||||
<p><strong>{% trans "Info: " %}</strong>{% trans "There are currently no keypairs." %}</p>
|
||||
|
@ -1,40 +0,0 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="table_title">
|
||||
<h3>{% trans "Keypairs" %}</h3>
|
||||
|
||||
<div class="table_actions">
|
||||
<a id="keypairs_import_link" class="btn primary small ajax-modal" data-controls-modal="import_keypair_modal" data-backdrop="static" href="{% url horizon:nova:access_and_security:keypairs:import %}">{% trans "Import Keypair" %}</a>
|
||||
<a id="keypairs_create_link" class="btn small ajax-modal" data-controls-modal="create_keypair_modal" data-backdrop="static" href="{% url horizon:nova:access_and_security:keypairs:create %}">{% trans "Create New Keypair" %}</a>
|
||||
|
||||
<div class="keypairs table_search">
|
||||
<form action="#">
|
||||
<input class="span3" type="text">
|
||||
</form>
|
||||
</div>
|
||||
<a class="inspect" href="#">{% trans "inspect" %}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<table id="keypairs" class="zebra-striped sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Fingerprint" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for keypair in keypairs %}
|
||||
<tr class="{% cycle 'odd' 'even' %}">
|
||||
<td>{{ keypair.name }}</td>
|
||||
<td>{{ keypair.fingerprint }}</td>
|
||||
<td id="actions" class="single">
|
||||
<ul>
|
||||
<li class="form">{% include "nova/access_and_security/keypairs/_delete.html" with form=keypair_delete_form %}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
@ -7,8 +7,8 @@
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block dash_main %}
|
||||
{% if keypairs %}
|
||||
{% include 'nova/access_and_security/keypairs/_list.html' %}
|
||||
{% if table.data %}
|
||||
{{ table.render }}
|
||||
{% else %}
|
||||
<div class="alert-message block-message info">
|
||||
<p><strong>{% trans "Info: " %}</strong>{% trans "There are currently no keypairs." %}</p>
|
||||
|
0
openstack-dashboard/manage.py
Normal file → Executable file
0
openstack-dashboard/manage.py
Normal file → Executable file
Loading…
x
Reference in New Issue
Block a user