Adds API Access information to Access & Security panel.

What this does:

  * Makes the Access & Security panel use tabs for each of
    the tables instead of trying to shove them all inline.
  * Adds an "API Access" tab to the above set of tabs.
  * Combines the features of the API Endpoints table, the
    EC2 Credentials download and the OpenRC file download
    into the API Access tab mentioned above.
  * Uses the service "type" instead of "name" in the Endpoints
    table to be nicer about service API abstraction.

Fixes bug 1065671 and fixes bug 1120627.

Change-Id: Iccc65b32d37dc97a96538443cf8c5c08fcea7250
This commit is contained in:
Gabriel Hurley 2013-02-09 14:34:22 -08:00
parent 216d566c9c
commit cd0a959523
29 changed files with 318 additions and 604 deletions

View File

@ -1,42 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 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.
"""
Methods and interface objects used to interact with external APIs.
API method calls return objects that are in many cases objects with
attributes that are direct maps to the data returned from the API http call.
Unfortunately, these objects are also often constructed dynamically, making
it difficult to know what data is available from the API object. Because of
this, all API calls should wrap their returned object in one defined here,
using only explicitly defined atributes and/or methods.
In other words, Horizon developers not working on openstack_dashboard.api
shouldn't need to understand the finer details of APIs for
Keystone/Nova/Glance/Swift et. al.
"""
from openstack_dashboard.api import base
from openstack_dashboard.api import cinder
from openstack_dashboard.api import glance
from openstack_dashboard.api import keystone
from openstack_dashboard.api import network
from openstack_dashboard.api import nova
from openstack_dashboard.api import quantum
from openstack_dashboard.api import swift

View File

@ -15,6 +15,7 @@
# under the License.
from django.utils.translation import ugettext as _
from django.template.defaultfilters import title
from horizon import tables
@ -23,11 +24,40 @@ def get_endpoint(service):
return service.endpoints[0]['publicURL']
def pretty_service_names(name):
name = name.replace('-', ' ')
if name in ['ec2', 's3']:
name = name.upper()
else:
name = title(name)
return name
class DownloadEC2(tables.LinkAction):
name = "download_ec2"
verbose_name = _("Download EC2 Credentials")
verbose_name_plural = _("Download EC2 Credentials")
classes = ("btn-download",)
url = "horizon:project:access_and_security:api_access:ec2"
class DownloadOpenRC(tables.LinkAction):
name = "download_openrc"
verbose_name = _("Download OpenStack RC File")
verbose_name_plural = _("Download OpenStack RC File")
classes = ("btn-download",)
url = "horizon:project:access_and_security:api_access:openrc"
class EndpointsTable(tables.DataTable):
api_name = tables.Column('name', verbose_name=_("Service Name"))
api_name = tables.Column('type',
verbose_name=_("Service"),
filters=(pretty_service_names,))
api_endpoint = tables.Column(get_endpoint,
verbose_name=_("Service Endpoint"))
class Meta:
name = "endpoints"
verbose_name = _("API Endpoints")
multi_select = False
table_actions = (DownloadOpenRC, DownloadEC2,)

View File

@ -21,35 +21,21 @@ from mox import IsA
from openstack_dashboard import api
from openstack_dashboard.test import helpers as test
from .forms import DownloadX509Credentials
INDEX_URL = reverse("horizon:settings:ec2:index")
EC2_URL = reverse("horizon:project:access_and_security:api_access:ec2")
class EC2SettingsTest(test.TestCase):
class APIAccessTests(test.TestCase):
def test_ec2_download_view(self):
creds = self.ec2.first()
cert = self.certs.first()
self.mox.StubOutWithMock(api.keystone, "tenant_list")
self.mox.StubOutWithMock(api.keystone, "token_create_scoped")
self.mox.StubOutWithMock(api.keystone, "list_ec2_credentials")
self.mox.StubOutWithMock(api.nova, "get_x509_credentials")
self.mox.StubOutWithMock(api.nova, "get_x509_root_certificate")
self.mox.StubOutWithMock(api.keystone, "create_ec2_credentials")
# GET request
api.keystone.tenant_list(IsA(HttpRequest)) \
.AndReturn(self.tenants.list())
# POST request
api.keystone.token_create_scoped(IsA(HttpRequest),
self.tenant.id,
IsA(str)) \
.AndReturn(self.tokens.scoped_token)
api.keystone.tenant_list(IsA(HttpRequest)) \
.AndReturn(self.tenants.list())
api.keystone.list_ec2_credentials(IsA(HttpRequest), self.user.id) \
.AndReturn([])
api.nova.get_x509_credentials(IsA(HttpRequest)).AndReturn(cert)
@ -60,11 +46,6 @@ class EC2SettingsTest(test.TestCase):
self.tenant.id).AndReturn(creds)
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertNoMessages()
res = self.client.get(EC2_URL)
self.assertEqual(res.status_code, 200)
data = {'method': DownloadX509Credentials.__name__,
'tenant': self.tenant.id}
res = self.client.post(INDEX_URL, data)
self.assertEqual(res['content-type'], 'application/zip')

View File

@ -1,5 +1,9 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -16,8 +20,10 @@
from django.conf.urls.defaults import patterns, url
from .views import OpenRCView
from .views import download_ec2_bundle, download_rc_file
urlpatterns = patterns('',
url(r'^$', OpenRCView.as_view(), name='index'))
url(r'^ec2/$', download_ec2_bundle, name='ec2'),
url(r'^openrc/$', download_rc_file, name='openrc'),
)

View File

@ -0,0 +1,135 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Openstack, LLC
#
# 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
import tempfile
import zipfile
from contextlib import closing
from django import http
from django import shortcuts
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import messages
from openstack_dashboard import api
LOG = logging.getLogger(__name__)
def download_ec2_bundle(request):
tenant_id = request.user.tenant_id
tenant_name = request.user.tenant_name
# Gather or create our EC2 credentials
try:
credentials = api.nova.get_x509_credentials(request)
cacert = api.nova.get_x509_root_certificate(request)
all_keys = api.keystone.list_ec2_credentials(request,
request.user.id)
keys = None
for key in all_keys:
if key.tenant_id == tenant_id:
keys = key
if keys is None:
keys = api.keystone.create_ec2_credentials(request,
request.user.id,
tenant_id)
except:
exceptions.handle(request,
_('Unable to fetch EC2 credentials.'),
redirect=request.build_absolute_uri())
# Get our S3 endpoint if it exists
try:
s3_endpoint = api.base.url_for(request,
's3',
endpoint_type='publicURL')
except exceptions.ServiceCatalogException:
s3_endpoint = None
# Get our EC2 endpoint (it should exist since we just got creds for it)
try:
ec2_endpoint = api.base.url_for(request,
'ec2',
endpoint_type='publicURL')
except exceptions.ServiceCatalogException:
ec2_endpoint = None
# Build the context
context = {'ec2_access_key': keys.access,
'ec2_secret_key': keys.secret,
'ec2_endpoint': ec2_endpoint,
's3_endpoint': s3_endpoint}
# Create our file bundle
template = 'project/access_and_security/api_access/ec2rc.sh.template'
try:
temp_zip = tempfile.NamedTemporaryFile(delete=True)
with closing(zipfile.ZipFile(temp_zip.name, mode='w')) as archive:
archive.writestr('pk.pem', credentials.private_key)
archive.writestr('cert.pem', credentials.data)
archive.writestr('cacert.pem', cacert.data)
archive.writestr('ec2rc.sh', render_to_string(template, context))
except:
exceptions.handle(request,
_('Error writing zipfile: %(exc)s'),
redirect=request.build_absolute_uri())
# Send it back
response = http.HttpResponse(mimetype='application/zip')
response.write(temp_zip.read())
response['Content-Disposition'] = ('attachment; '
'filename=%s-x509.zip'
% tenant_name)
response['Content-Length'] = temp_zip.tell()
return response
def download_rc_file(request):
tenant_id = request.user.tenant_id
tenant_name = request.user.tenant_name
template = 'project/access_and_security/api_access/openrc.sh.template'
try:
keystone_url = api.base.url_for(request,
'identity',
endpoint_type='publicURL')
context = {'user': request.user,
'auth_url': keystone_url,
'tenant_id': tenant_id,
'tenant_name': tenant_name}
response = shortcuts.render(request,
template,
context,
content_type="text/plain")
response['Content-Disposition'] = ('attachment; '
'filename=%s-openrc.sh'
% tenant_name)
response['Content-Length'] = str(len(response.content))
return response
except Exception, e:
LOG.exception("Exception in DownloadOpenRCForm.")
messages.error(request, _('Error Downloading RC File: %s') % e)
return shortcuts.redirect(request.build_absolute_uri())

View File

@ -126,8 +126,6 @@ class FloatingIpViewTests(test.TestCase):
def test_disassociate_post(self):
floating_ip = self.floating_ips.first()
server = self.servers.first()
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api.nova, 'security_group_list')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_get')
self.mox.StubOutWithMock(api.network, 'floating_ip_disassociate')
@ -135,10 +133,6 @@ class FloatingIpViewTests(test.TestCase):
api.nova.server_list(IsA(http.HttpRequest),
all_tenants=True).AndReturn(self.servers.list())
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.network.floating_ip_disassociate(IsA(http.HttpRequest),
@ -154,8 +148,6 @@ class FloatingIpViewTests(test.TestCase):
def test_disassociate_post_with_exception(self):
floating_ip = self.floating_ips.first()
server = self.servers.first()
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api.nova, 'security_group_list')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_get')
self.mox.StubOutWithMock(api.network, 'floating_ip_disassociate')
@ -163,10 +155,6 @@ class FloatingIpViewTests(test.TestCase):
api.nova.server_list(IsA(http.HttpRequest),
all_tenants=True).AndReturn(self.servers.list())
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())

View File

@ -36,16 +36,7 @@ class KeyPairViewTests(test.TestCase):
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
self.mox.StubOutWithMock(api.nova, 'security_group_list')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.nova, 'server_list')
api.nova.server_list(IsA(http.HttpRequest),
all_tenants=True).AndReturn(self.servers.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name)
@ -59,16 +50,7 @@ class KeyPairViewTests(test.TestCase):
keypair = self.keypairs.first()
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
self.mox.StubOutWithMock(api.nova, 'security_group_list')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.nova, 'server_list')
api.nova.server_list(IsA(http.HttpRequest),
all_tenants=True).AndReturn(self.servers.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name) \

View File

@ -0,0 +1,126 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
# Copyright 2012 OpenStack LLC
#
# 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.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import messages
from horizon import tabs
from openstack_dashboard.api import keystone
from openstack_dashboard.api import network
from openstack_dashboard.api import nova
from .keypairs.tables import KeypairsTable
from .floating_ips.tables import FloatingIPsTable
from .security_groups.tables import SecurityGroupsTable
from .api_access.tables import EndpointsTable
class SecurityGroupsTab(tabs.TableTab):
table_classes = (SecurityGroupsTable,)
name = _("Security Groups")
slug = "security_groups_tab"
template_name = "horizon/common/_detail_table.html"
def get_security_groups_data(self):
try:
security_groups = nova.security_group_list(self.request)
except:
security_groups = []
exceptions.handle(self.request,
_('Unable to retrieve security groups.'))
return security_groups
class KeypairsTab(tabs.TableTab):
table_classes = (KeypairsTable,)
name = _("Keypairs")
slug = "keypairs_tab"
template_name = "horizon/common/_detail_table.html"
def get_keypairs_data(self):
try:
keypairs = nova.keypair_list(self.request)
except:
keypairs = []
exceptions.handle(self.request,
_('Unable to retrieve keypair list.'))
return keypairs
class FloatingIPsTab(tabs.TableTab):
table_classes = (FloatingIPsTable,)
name = _("Floating IPs")
slug = "floating_ips_tab"
template_name = "horizon/common/_detail_table.html"
def get_floating_ips_data(self):
try:
floating_ips = network.tenant_floating_ip_list(self.request)
except:
floating_ips = []
exceptions.handle(self.request,
_('Unable to retrieve floating IP addresses.'))
try:
floating_ip_pools = network.floating_ip_pools_list(self.request)
except:
floating_ip_pools = []
messages.warning(self.request,
_('Unable to retrieve floating IP pools.'))
pool_dict = dict([(obj.id, obj.name) for obj in floating_ip_pools])
instances = []
try:
instances = nova.server_list(self.request, all_tenants=True)
except:
exceptions.handle(self.request,
_('Unable to retrieve instance list.'))
instances_dict = dict([(obj.id, obj) for obj in instances])
for ip in floating_ips:
ip.instance_name = instances_dict[ip.instance_id].name \
if ip.instance_id in instances_dict else None
ip.pool_name = pool_dict.get(ip.pool, ip.pool)
return floating_ips
class APIAccessTab(tabs.TableTab):
table_classes = (EndpointsTable,)
name = _("API Access")
slug = "api_access_tab"
template_name = "horizon/common/_detail_table.html"
def get_endpoints_data(self):
services = []
for i, service in enumerate(self.request.user.service_catalog):
service['id'] = i
services.append(keystone.Service(service))
return services
class AccessAndSecurityTabs(tabs.TabGroup):
slug = "access_security_tabs"
tabs = (SecurityGroupsTab, KeypairsTab, FloatingIPsTab, APIAccessTab)
sticky = True

View File

@ -1,21 +1,15 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}Access & Security{% endblock %}
{% block title %}{% trans "Access & Security" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Access & Security") %}
{% endblock page_header %}
{% block main %}
<div id="security_groups">
{{ security_groups_table.render }}
</div>
<div id="keypairs">
{{ keypairs_table.render }}
</div>
<div id="floating_ips">
{{ floating_ips_table.render }}
<div class="row-fluid">
<div class="span12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -20,6 +20,7 @@
from django.conf.urls.defaults import url, patterns, include
from .api_access import urls as api_access_urls
from .floating_ips import urls as fip_urls
from .keypairs import urls as keypair_urls
from .security_groups import urls as sec_group_urls
@ -28,6 +29,7 @@ from .views import IndexView
urlpatterns = patterns('',
url(r'^$', IndexView.as_view(), name='index'),
url(r'api_access/', include(api_access_urls, namespace='api_access')),
url(r'keypairs/', include(keypair_urls, namespace='keypairs')),
url(r'floating_ips/', include(fip_urls, namespace='floating_ips')),
url(r'security_groups/',

View File

@ -22,74 +22,12 @@
"""
Views for Instances and Volumes.
"""
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from horizon import exceptions
from horizon import messages
from horizon import tables
from openstack_dashboard.api import network
from openstack_dashboard.api import nova
from .keypairs.tables import KeypairsTable
from .floating_ips.tables import FloatingIPsTable
from .security_groups.tables import SecurityGroupsTable
from .tabs import AccessAndSecurityTabs
LOG = logging.getLogger(__name__)
class IndexView(tables.MultiTableView):
table_classes = (KeypairsTable, SecurityGroupsTable, FloatingIPsTable)
class IndexView(tabs.TabbedTableView):
tab_group_class = AccessAndSecurityTabs
template_name = 'project/access_and_security/index.html'
def get_keypairs_data(self):
try:
keypairs = nova.keypair_list(self.request)
except:
keypairs = []
exceptions.handle(self.request,
_('Unable to retrieve keypair list.'))
return keypairs
def get_security_groups_data(self):
try:
security_groups = nova.security_group_list(self.request)
except:
security_groups = []
exceptions.handle(self.request,
_('Unable to retrieve security groups.'))
return security_groups
def get_floating_ips_data(self):
try:
floating_ips = network.tenant_floating_ip_list(self.request)
except:
floating_ips = []
exceptions.handle(self.request,
_('Unable to retrieve floating IP addresses.'))
try:
floating_ip_pools = network.floating_ip_pools_list(self.request)
except:
floating_ip_pools = []
messages.warning(self.request,
_('Unable to retrieve floating IP pools.'))
pool_dict = dict([(obj.id, obj.name) for obj in floating_ip_pools])
instances = []
try:
instances = nova.server_list(self.request, all_tenants=True)
except:
exceptions.handle(self.request,
_('Unable to retrieve instance list.'))
instances_dict = dict([(obj.id, obj) for obj in instances])
for ip in floating_ips:
ip.instance_name = instances_dict[ip.instance_id].name \
if ip.instance_id in instances_dict else None
ip.pool_name = pool_dict.get(ip.pool, ip.pool)
return floating_ips

View File

@ -7,7 +7,7 @@
{% endblock page_header %}
{% block main %}
{% include 'project/containers/_copy.html' %}
{% include 'project/containers/_copy.html' %}
{% endblock %}

View File

@ -1,10 +1,10 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}Containers{% endblock %}
{% block title %}{% trans "Containers" %}{% endblock %}
{% block page_header %}
<div class='page-header'>
<h2>{% trans "Containers" %}
<h2>{% trans "Containers" %}</h2>
</div>
{% endblock page_header %}

View File

@ -23,7 +23,7 @@ import horizon
class Settings(horizon.Dashboard):
name = _("Settings")
slug = "settings"
panels = ('user', 'project', 'ec2')
panels = ('user',)
default_panel = 'user'
nav = False

View File

@ -1,111 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Openstack, LLC
#
# 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
import tempfile
import zipfile
from contextlib import closing
from django import http
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from openstack_dashboard import api
LOG = logging.getLogger(__name__)
class DownloadX509Credentials(forms.SelfHandlingForm):
tenant = forms.ChoiceField(label=_("Select a Project"))
def __init__(self, request, *args, **kwargs):
super(DownloadX509Credentials, self).__init__(request, *args, **kwargs)
# Populate tenant choices
tenant_choices = []
try:
tenant_list = api.keystone.tenant_list(request)
except:
tenant_list = []
exceptions.handle(request, _("Unable to retrieve tenant list."))
for tenant in tenant_list:
if tenant.enabled:
tenant_choices.append((tenant.id, tenant.name))
if not tenant_choices:
self.fields['tenant'].choices = [('', 'No Available Tenants')]
else:
self.fields['tenant'].choices = tenant_choices
def handle(self, request, data):
def find_or_create_access_keys(request, tenant_id):
keys = api.keystone.list_ec2_credentials(request, request.user.id)
for key in keys:
if key.tenant_id == tenant_id:
return key
return api.keystone.create_ec2_credentials(request,
request.user.id,
tenant_id)
try:
# NOTE(jakedahn): Keystone errors unless we specifically scope
# the token to tenant before making the call.
api.keystone.token_create_scoped(request,
data.get('tenant'),
request.user.token.id)
credentials = api.nova.get_x509_credentials(request)
cacert = api.nova.get_x509_root_certificate(request)
keys = find_or_create_access_keys(request, data.get('tenant'))
context = {'ec2_access_key': keys.access,
'ec2_secret_key': keys.secret,
'ec2_endpoint': api.base.url_for(
request,
'ec2',
endpoint_type='publicURL')}
try:
s3_endpoint = api.base.url_for(request,
's3',
endpoint_type='publicURL')
except exceptions.ServiceCatalogException:
s3_endpoint = None
context['s3_endpoint'] = s3_endpoint
except:
exceptions.handle(request,
_('Unable to fetch EC2 credentials.'),
redirect=request.build_absolute_uri())
try:
temp_zip = tempfile.NamedTemporaryFile(delete=True)
with closing(zipfile.ZipFile(temp_zip.name, mode='w')) as archive:
archive.writestr('pk.pem', credentials.private_key)
archive.writestr('cert.pem', credentials.data)
archive.writestr('cacert.pem', cacert.data)
archive.writestr('ec2rc.sh', render_to_string(
'settings/ec2/ec2rc.sh.template', context))
except:
exceptions.handle(request,
_('Error writing zipfile: %(exc)s'),
redirect=request.build_absolute_uri())
response = http.HttpResponse(mimetype='application/zip')
response.write(temp_zip.read())
response['Content-Disposition'] = 'attachment; \
filename=%s-x509.zip' \
% data.get('tenant')
response['Content-Length'] = temp_zip.tell()
return response

View File

@ -1,30 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Openstack, LLC
#
# 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.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.settings import dashboard
class EC2Panel(horizon.Panel):
name = _("EC2 Credentials")
slug = 'ec2'
permissions = ('openstack.services.ec2',)
dashboard.Settings.register(EC2Panel)

View File

@ -1,25 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_id %}x509_download_form{% endblock %}
{% block form_action %}{% url horizon:settings:ec2:index %}{% endblock %}
{% block modal_id %}x509_download_modal{% endblock %}
{% block modal-header %}{% trans "Download EC2 Credentials" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Clicking "Download EC2 Credentials" will download a zip file which includes an rc file with your access/secret keys, as well as your x509 private key and certificate.' %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right always-enabled" type="submit" value="{% trans "Download EC2 Credentials" %}" />
{% if hide %}<a href="{% url horizon:settings:ec2:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>{% endif %}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Download EC2 Credentials" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Download EC2 Credentials") %}
{% endblock page_header %}
{% block main %}
{% include "settings/ec2/download_form.html" %}
{% endblock %}

View File

@ -1,23 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Openstack, LLC
#
# 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.urls.defaults import patterns, url
from .views import IndexView
urlpatterns = patterns('openstack_dashboard.dashboards.settings.ec2.views',
url(r'^$', IndexView.as_view(), name='index'),
)

View File

@ -1,32 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Openstack, LLC
#
# 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 horizon import forms
from .forms import DownloadX509Credentials
LOG = logging.getLogger(__name__)
class IndexView(forms.ModalFormView):
form_class = DownloadX509Credentials
template_name = 'settings/ec2/index.html'
def form_valid(self, form):
return form.handle(self.request, form.cleaned_data)

View File

@ -1,78 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 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.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import messages
from openstack_dashboard import api
LOG = logging.getLogger(__name__)
class DownloadOpenRCForm(forms.SelfHandlingForm):
tenant = forms.ChoiceField(label=_("Select a Project"))
def __init__(self, request, *args, **kwargs):
super(DownloadOpenRCForm, self).__init__(request, *args, **kwargs)
# Populate tenant choices
tenant_choices = []
try:
tenants = api.keystone.tenant_list(request)
except:
tenants = []
exceptions.handle(request, _("Unable to retrieve project list."))
for tenant in tenants:
if tenant.enabled:
tenant_choices.append((tenant.id, tenant.name))
self.fields['tenant'].choices = tenant_choices
def handle(self, request, data):
try:
tenant_id = data['tenant']
tenant_name = dict(self.fields['tenant'].choices)[tenant_id]
keystone_url = api.base.url_for(request,
'identity',
endpoint_type='publicURL')
context = {'user': request.user,
'auth_url': keystone_url,
'tenant_id': tenant_id,
'tenant_name': tenant_name}
response = shortcuts.render(request,
'settings/project/openrc.sh.template',
context,
content_type="text/plain")
response['Content-Disposition'] = 'attachment; filename=openrc.sh'
response['Content-Length'] = str(len(response.content))
return response
except Exception, e:
LOG.exception("Exception in DownloadOpenRCForm.")
messages.error(request, _('Error Downloading RC File: %s') % e)
return shortcuts.redirect(request.build_absolute_uri())

View File

@ -1,29 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 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.
from django.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.settings import dashboard
class TenantPanel(horizon.Panel):
name = _("OpenStack API")
slug = 'project'
dashboard.Settings.register(TenantPanel)

View File

@ -1,32 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_id %}openrc_download_form{% endblock %}
{% block form_action %}{% url horizon:settings:project:index %}{% endblock %}
{% block modal_id %}language_settings_modal{% endblock %}
{% block modal-header %}{% trans "OpenStack API" %}{% endblock %}
{% block modal-body %}
<div>
{{ endpoints.render }}
</div>
<div>
<h3>{% trans "Download OpenStack RC File" %}</h3>
<hr />
</div>
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Download the RC file for the selected project, then type "source openrc" in the terminal to configure your environment to communicate with OpenStack.' %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<button class="btn btn-primary pull-right always-enabled" type="submit">{% trans "Download RC File" %}</button>
{% if hide %}<a href="{% url horizon:settings:project:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>{% endif %}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "OpenStack API" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("OpenStack API") %}
{% endblock page_header %}
{% block main %}
{% include "settings/project/_openrc.html" %}
{% endblock %}

View File

@ -1,44 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 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.
from horizon.forms import ModalFormView
from openstack_dashboard.api import keystone
from .forms import DownloadOpenRCForm
from .tables import EndpointsTable
class OpenRCView(ModalFormView):
form_class = DownloadOpenRCForm
template_name = 'settings/project/settings.html'
def get_data(self):
services = []
for i, service in enumerate(self.request.user.service_catalog):
service['id'] = i
services.append(keystone.Service(service))
return services
def get_context_data(self, **kwargs):
context = super(OpenRCView, self).get_context_data(**kwargs)
context["endpoints"] = EndpointsTable(self.request, self.get_data())
return context
def get_initial(self):
return {'tenant': self.request.user.tenant_id}
def form_valid(self, form):
return form.handle(self.request, form.cleaned_data)