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:
parent
216d566c9c
commit
cd0a959523
@ -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
|
|
@ -15,6 +15,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.template.defaultfilters import title
|
||||||
|
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
|
|
||||||
@ -23,11 +24,40 @@ def get_endpoint(service):
|
|||||||
return service.endpoints[0]['publicURL']
|
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):
|
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,
|
api_endpoint = tables.Column(get_endpoint,
|
||||||
verbose_name=_("Service Endpoint"))
|
verbose_name=_("Service Endpoint"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
name = "endpoints"
|
name = "endpoints"
|
||||||
verbose_name = _("API Endpoints")
|
verbose_name = _("API Endpoints")
|
||||||
|
multi_select = False
|
||||||
|
table_actions = (DownloadOpenRC, DownloadEC2,)
|
@ -21,35 +21,21 @@ from mox import IsA
|
|||||||
|
|
||||||
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 .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):
|
def test_ec2_download_view(self):
|
||||||
creds = self.ec2.first()
|
creds = self.ec2.first()
|
||||||
cert = self.certs.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.keystone, "list_ec2_credentials")
|
||||||
self.mox.StubOutWithMock(api.nova, "get_x509_credentials")
|
self.mox.StubOutWithMock(api.nova, "get_x509_credentials")
|
||||||
self.mox.StubOutWithMock(api.nova, "get_x509_root_certificate")
|
self.mox.StubOutWithMock(api.nova, "get_x509_root_certificate")
|
||||||
self.mox.StubOutWithMock(api.keystone, "create_ec2_credentials")
|
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) \
|
api.keystone.list_ec2_credentials(IsA(HttpRequest), self.user.id) \
|
||||||
.AndReturn([])
|
.AndReturn([])
|
||||||
api.nova.get_x509_credentials(IsA(HttpRequest)).AndReturn(cert)
|
api.nova.get_x509_credentials(IsA(HttpRequest)).AndReturn(cert)
|
||||||
@ -60,11 +46,6 @@ class EC2SettingsTest(test.TestCase):
|
|||||||
self.tenant.id).AndReturn(creds)
|
self.tenant.id).AndReturn(creds)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(EC2_URL)
|
||||||
self.assertNoMessages()
|
|
||||||
self.assertEqual(res.status_code, 200)
|
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')
|
self.assertEqual(res['content-type'], 'application/zip')
|
@ -1,5 +1,9 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# 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 Nebula, Inc.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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 django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
from .views import OpenRCView
|
from .views import download_ec2_bundle, download_rc_file
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
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'),
|
||||||
|
)
|
@ -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())
|
@ -126,8 +126,6 @@ class FloatingIpViewTests(test.TestCase):
|
|||||||
def test_disassociate_post(self):
|
def test_disassociate_post(self):
|
||||||
floating_ip = self.floating_ips.first()
|
floating_ip = self.floating_ips.first()
|
||||||
server = self.servers.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_list')
|
||||||
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_get')
|
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_get')
|
||||||
self.mox.StubOutWithMock(api.network, 'floating_ip_disassociate')
|
self.mox.StubOutWithMock(api.network, 'floating_ip_disassociate')
|
||||||
@ -135,10 +133,6 @@ class FloatingIpViewTests(test.TestCase):
|
|||||||
|
|
||||||
api.nova.server_list(IsA(http.HttpRequest),
|
api.nova.server_list(IsA(http.HttpRequest),
|
||||||
all_tenants=True).AndReturn(self.servers.list())
|
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)) \
|
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.floating_ips.list())
|
.AndReturn(self.floating_ips.list())
|
||||||
api.network.floating_ip_disassociate(IsA(http.HttpRequest),
|
api.network.floating_ip_disassociate(IsA(http.HttpRequest),
|
||||||
@ -154,8 +148,6 @@ class FloatingIpViewTests(test.TestCase):
|
|||||||
def test_disassociate_post_with_exception(self):
|
def test_disassociate_post_with_exception(self):
|
||||||
floating_ip = self.floating_ips.first()
|
floating_ip = self.floating_ips.first()
|
||||||
server = self.servers.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_list')
|
||||||
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_get')
|
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_get')
|
||||||
self.mox.StubOutWithMock(api.network, 'floating_ip_disassociate')
|
self.mox.StubOutWithMock(api.network, 'floating_ip_disassociate')
|
||||||
@ -163,10 +155,6 @@ class FloatingIpViewTests(test.TestCase):
|
|||||||
|
|
||||||
api.nova.server_list(IsA(http.HttpRequest),
|
api.nova.server_list(IsA(http.HttpRequest),
|
||||||
all_tenants=True).AndReturn(self.servers.list())
|
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)) \
|
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.floating_ips.list())
|
.AndReturn(self.floating_ips.list())
|
||||||
|
|
||||||
|
@ -36,16 +36,7 @@ class KeyPairViewTests(test.TestCase):
|
|||||||
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
|
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)) \
|
api.nova.keypair_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.keypairs.list())
|
.AndReturn(self.keypairs.list())
|
||||||
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name)
|
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name)
|
||||||
@ -59,16 +50,7 @@ class KeyPairViewTests(test.TestCase):
|
|||||||
keypair = self.keypairs.first()
|
keypair = self.keypairs.first()
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
|
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)) \
|
api.nova.keypair_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.keypairs.list())
|
.AndReturn(self.keypairs.list())
|
||||||
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name) \
|
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name) \
|
||||||
|
@ -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
|
@ -1,21 +1,15 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Access & Security{% endblock %}
|
{% block title %}{% trans "Access & Security" %}{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
{% include "horizon/common/_page_header.html" with title=_("Access & Security") %}
|
{% include "horizon/common/_page_header.html" with title=_("Access & Security") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="security_groups">
|
<div class="row-fluid">
|
||||||
{{ security_groups_table.render }}
|
<div class="span12">
|
||||||
|
{{ tab_group.render }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="keypairs">
|
|
||||||
{{ keypairs_table.render }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="floating_ips">
|
|
||||||
{{ floating_ips_table.render }}
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
from django.conf.urls.defaults import url, patterns, include
|
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 .floating_ips import urls as fip_urls
|
||||||
from .keypairs import urls as keypair_urls
|
from .keypairs import urls as keypair_urls
|
||||||
from .security_groups import urls as sec_group_urls
|
from .security_groups import urls as sec_group_urls
|
||||||
@ -28,6 +29,7 @@ from .views import IndexView
|
|||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$', IndexView.as_view(), name='index'),
|
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'keypairs/', include(keypair_urls, namespace='keypairs')),
|
||||||
url(r'floating_ips/', include(fip_urls, namespace='floating_ips')),
|
url(r'floating_ips/', include(fip_urls, namespace='floating_ips')),
|
||||||
url(r'security_groups/',
|
url(r'security_groups/',
|
||||||
|
@ -22,74 +22,12 @@
|
|||||||
"""
|
"""
|
||||||
Views for Instances and Volumes.
|
Views for Instances and Volumes.
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from horizon import tabs
|
||||||
|
|
||||||
from horizon import exceptions
|
from .tabs import AccessAndSecurityTabs
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
class IndexView(tabs.TabbedTableView):
|
||||||
|
tab_group_class = AccessAndSecurityTabs
|
||||||
|
|
||||||
class IndexView(tables.MultiTableView):
|
|
||||||
table_classes = (KeypairsTable, SecurityGroupsTable, FloatingIPsTable)
|
|
||||||
template_name = 'project/access_and_security/index.html'
|
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
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Containers{% endblock %}
|
{% block title %}{% trans "Containers" %}{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
<div class='page-header'>
|
<div class='page-header'>
|
||||||
<h2>{% trans "Containers" %}
|
<h2>{% trans "Containers" %}</h2>
|
||||||
</div>
|
</div>
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import horizon
|
|||||||
class Settings(horizon.Dashboard):
|
class Settings(horizon.Dashboard):
|
||||||
name = _("Settings")
|
name = _("Settings")
|
||||||
slug = "settings"
|
slug = "settings"
|
||||||
panels = ('user', 'project', 'ec2')
|
panels = ('user',)
|
||||||
default_panel = 'user'
|
default_panel = 'user'
|
||||||
nav = False
|
nav = False
|
||||||
|
|
||||||
|
@ -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
|
|
@ -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)
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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'),
|
|
||||||
)
|
|
@ -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)
|
|
@ -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())
|
|
@ -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)
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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)
|
|
Loading…
x
Reference in New Issue
Block a user