Merge pull request #143 from cloudbuilders/keypair-import

Keypair import
This commit is contained in:
Joseph Heck
2011-09-30 10:09:48 -07:00
7 changed files with 95 additions and 14 deletions

View File

@@ -502,6 +502,10 @@ def keypair_create(request, name):
return KeyPair(novaclient(request).keypairs.create(name))
def keypair_import(request, name, public_key):
return KeyPair(novaclient(request).keypairs.create(name, public_key))
def keypair_delete(request, keypair_id):
novaclient(request).keypairs.delete(keypair_id)

View File

@@ -55,6 +55,7 @@ urlpatterns += patterns('django_openstack.dash.views.images',
urlpatterns += patterns('django_openstack.dash.views.keypairs',
url(r'^(?P<tenant_id>[^/]+)/keypairs/$', 'index', name='dash_keypairs'),
url(KEYPAIRS % 'create', 'create', name='dash_keypairs_create'),
url(KEYPAIRS % 'import', 'import_keypair', name='dash_keypairs_import'),
)
urlpatterns += patterns('django_openstack.dash.views.floating_ips',

View File

@@ -28,7 +28,7 @@ from django import template
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core import validators
from django import shortcuts
from django.shortcuts import redirect, render_to_response
from django_openstack import api
from django_openstack import forms
@@ -50,7 +50,7 @@ class DeleteKeypair(forms.SelfHandlingForm):
except novaclient_exceptions.ClientException, e:
LOG.exception("ClientException in DeleteKeypair")
messages.error(request, 'Error deleting keypair: %s' % e.message)
return shortcuts.redirect(request.build_absolute_uri())
return redirect(request.build_absolute_uri())
class CreateKeypair(forms.SelfHandlingForm):
@@ -70,7 +70,26 @@ class CreateKeypair(forms.SelfHandlingForm):
except novaclient_exceptions.ClientException, e:
LOG.exception("ClientException in CreateKeyPair")
messages.error(request, 'Error Creating Keypair: %s' % e.message)
return shortcuts.redirect(request.build_absolute_uri())
return redirect(request.build_absolute_uri())
class ImportKeypair(forms.SelfHandlingForm):
name = forms.CharField(max_length="20", label="Keypair Name",
validators=[validators.RegexValidator('\w+')])
public_key = forms.CharField(label='Public Key', widget=forms.Textarea)
def handle(self, request, data):
try:
LOG.info('Importing keypair "%s"' % data['name'])
api.keypair_import(request, data['name'], data['public_key'])
messages.success(request, 'Successfully imported public key: %s'
% data['name'])
return redirect('dash_keypairs', request.user.tenant_id)
except novaclient_exceptions.ClientException, e:
LOG.exception("ClientException in ImportKeypair")
messages.error(request, 'Error Importing Keypair: %s' % e.message)
return redirect(request.build_absolute_uri())
@login_required
@@ -87,8 +106,7 @@ def index(request, tenant_id):
LOG.exception("ClientException in keypair index")
messages.error(request, 'Error fetching keypairs: %s' % e.message)
return shortcuts.render_to_response(
'django_openstack/dash/keypairs/index.html', {
return render_to_response('django_openstack/dash/keypairs/index.html', {
'keypairs': keypairs,
'delete_form': delete_form,
}, context_instance=template.RequestContext(request))
@@ -100,7 +118,17 @@ def create(request, tenant_id):
if handled:
return handled
return shortcuts.render_to_response(
'django_openstack/dash/keypairs/create.html', {
return render_to_response('django_openstack/dash/keypairs/create.html', {
'create_form': form,
}, context_instance=template.RequestContext(request))
@login_required
def import_keypair(request, tenant_id):
form, handled = ImportKeypair.maybe_handle(request)
if handled:
return handled
return render_to_response('django_openstack/dash/keypairs/import.html', {
'create_form': form,
}, context_instance=template.RequestContext(request))

View File

@@ -6,5 +6,5 @@
{{ field.errors }}
{{ field }}
{% endfor %}
<input type="submit" value="Create Keypair" class="large-rounded" />
<input type="submit" value="Add Keypair" class="large-rounded" />
</form>

View File

@@ -0,0 +1,32 @@
{% extends 'django_openstack/dash/base.html' %}
{% block sidebar %}
{% with current_sidebar="keypairs" %}
{{block.super}}
{% endwith %}
{% endblock %}
{% block headerjs %}
{% endblock %}
{% block page_header %}
{# to make searchable false, just remove it from the include statement #}
{% include "django_openstack/common/_page_header.html" with title="Create Keypair" %}
{% endblock page_header %}
{% block dash_main %}
<div class="dash_block">
<div class="left">
{% include 'django_openstack/dash/keypairs/_form.html' with form=create_form %}
<h3><a href="{% url dash_keypairs request.user.tenant_id %}">&lt;&lt; Return to keypairs list</a></h3>
</div>
<div class="right">
<h3>Description:</h3>
<p>Keypairs are ssh credentials which are injected into images when they are launched. Creating a new key pair registers the public key and downloads the private key (a .pem file).</p>
<p>Protect and use the key as you would any normal ssh private key.</p>
</div>
<div class="clear">&nbsp;</div>
</div>
{% endblock %}

View File

@@ -15,11 +15,14 @@
{% block dash_main %}
{% if keypairs %}
{% include 'django_openstack/dash/keypairs/_list.html' %}
<a id="keypairs_create_link" class="action_link large-rounded" href="{% url dash_keypairs_create request.user.tenant_id %}">Create New Keypair</a>
<a id="keypairs_create_link" class="action_link large-rounded" href="{% url dash_keypairs_create request.user.tenant_id %}">Add New Keypair</a>
<a id="keypairs_import_link" class="action_link large-rounded" href="{% url dash_keypairs_import request.user.tenant_id %}">Import Keypair</a>
{% else %}
<div class="message_box info">
<h2>Info</h2>
<p>There are currently no keypairs. <a href='{% url dash_keypairs_create request.user.tenant_id %}'>Create A Keypair &gt;&gt;</a></p>
<p>There are currently no keypairs.</p>
</div>
<a id="keypairs_create_link" class="action_link large-rounded" href="{% url dash_keypairs_create request.user.tenant_id %}">Add New Keypair</a>
<a id="keypairs_import_link" class="action_link large-rounded" href="{% url dash_keypairs_import request.user.tenant_id %}">Import Keypair</a>
{% endif %}
{% endblock %}

View File

@@ -309,13 +309,13 @@ class ApiHelperTests(test.TestCase):
GLANCE_URL = 'http://glance/glanceapi/'
NOVA_URL = 'http://nova/novapi/'
url = api.url_for(self.request, 'image')
url = api.url_for(self.request, 'glance')
self.assertEqual(url, GLANCE_URL + 'internal')
url = api.url_for(self.request, 'image', admin=False)
url = api.url_for(self.request, 'glance', admin=False)
self.assertEqual(url, GLANCE_URL + 'internal')
url = api.url_for(self.request, 'image', admin=True)
url = api.url_for(self.request, 'glance', admin=True)
self.assertEqual(url, GLANCE_URL + 'admin')
url = api.url_for(self.request, 'compute')
@@ -1177,6 +1177,19 @@ class APIExtensionTests(NovaClientTestMixin, test.TestCase):
self.mox.VerifyAll()
def test_keypair_import(self):
novaclient = self.stub_novaclient()
novaclient.keypairs = self.mox.CreateMockAnything()
novaclient.keypairs.create(IsA(str), IsA(str)).AndReturn(self.keypair)
self.mox.ReplayAll()
ret_val = api.keypair_import(self.request, TEST_RETURN, TEST_RETURN)
self.assertIsInstance(ret_val, api.KeyPair)
self.assertEqual(ret_val.name, self.keypair.name)
self.mox.VerifyAll()
def test_keypair_delete(self):
novaclient = self.stub_novaclient()
@@ -1225,7 +1238,7 @@ class GlanceApiTests(test.TestCase):
client_instance.auth_tok = TEST_TOKEN
self.mox.StubOutWithMock(api, 'url_for')
api.url_for(IsA(http.HttpRequest), 'image').AndReturn(TEST_URL)
api.url_for(IsA(http.HttpRequest), 'glance').AndReturn(TEST_URL)
self.mox.ReplayAll()