Merge "Converts nova.access_and_security.keypairs to new tables and modals."

This commit is contained in:
Jenkins 2012-01-09 21:17:49 +00:00 committed by Gerrit Code Review
commit 120b43bd36
11 changed files with 155 additions and 152 deletions

View File

@ -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)).\

View File

@ -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,)

View File

@ -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(

View File

@ -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'),
)

View File

@ -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.
class IndexView(tables.DataTableView):
table_class = KeypairsTable
template_name = 'nova/access_and_security/keypairs/index.html'
@login_required
def index(request):
delete_form, handled = DeleteKeypair.maybe_handle(request)
if handled:
return handled
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, '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)
def get_data(self):
try:
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)
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'

View File

@ -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)

View File

@ -36,15 +36,11 @@ 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,
FloatingIpAllocate)
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)
LOG = logging.getLogger(__name__)
@ -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(),

View File

@ -7,7 +7,7 @@
{% endblock page_header %}
{% block dash_main %}
<div id="floating_ips">
{% if floating_ips %}
{% include 'nova/access_and_security/floating_ips/_list.html' %}
@ -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>

View File

@ -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>

View File

@ -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
View File