Add support for downloading clouds.yaml files
python-openstackclient, shade, openstacksdk and Ansible's OpenStack modules all support reading client config information from a file called clouds.yaml instead of from environment variables set from openrc files. Unfortunately, the only thing horizon currently offers for download is old-style openrc files. Add support for downloading clouds.yaml files. Change-Id: I0611dd44524b746ad993bff7435ec8628a83a762
This commit is contained in:
parent
21b25ed773
commit
6d315598aa
@ -796,6 +796,29 @@ When set, enables the instance action "Retrieve password" allowing password retr
|
||||
from metadata service.
|
||||
|
||||
|
||||
``OPENSTACK_CLOUDS_YAML_NAME``
|
||||
------------------------------
|
||||
|
||||
.. versionadded:: 12.0.0(Pike)
|
||||
|
||||
Default: ``openstack``
|
||||
|
||||
The name of the entry to put into the user's clouds.yaml file.
|
||||
|
||||
|
||||
``OPENSTACK_CLOUDS_YAML_PROFILE``
|
||||
---------------------------------
|
||||
|
||||
.. versionadded:: 12.0.0(Pike)
|
||||
|
||||
Default: None
|
||||
|
||||
If set, the name of the `vendor profile`_ from `os-client-config`_.
|
||||
|
||||
.. _vendor profile: https://docs.openstack.org/developer/os-client-config/vendor-support.html
|
||||
.. _os-client-config: https://docs.openstack.org/developer/os-client-config
|
||||
|
||||
|
||||
``OPENSTACK_ENDPOINT_TYPE``
|
||||
---------------------------
|
||||
|
||||
|
@ -44,6 +44,14 @@ class DownloadEC2(tables.LinkAction):
|
||||
return api.base.is_service_enabled(request, 'ec2')
|
||||
|
||||
|
||||
class DownloadCloudsYaml(tables.LinkAction):
|
||||
name = "download_clouds_yaml"
|
||||
verbose_name = _("Download OpenStack clouds.yaml File")
|
||||
verbose_name_plural = _("Download OpenStack clouds.yaml File")
|
||||
icon = "download"
|
||||
url = "horizon:project:api_access:clouds.yaml"
|
||||
|
||||
|
||||
class DownloadOpenRC(tables.LinkAction):
|
||||
name = "download_openrc"
|
||||
verbose_name = _("Download OpenStack RC File v3")
|
||||
@ -106,5 +114,6 @@ class EndpointsTable(tables.DataTable):
|
||||
name = "endpoints"
|
||||
verbose_name = _("API Endpoints")
|
||||
multi_select = False
|
||||
table_actions = (DownloadOpenRCv2, DownloadOpenRC, DownloadEC2,
|
||||
table_actions = (DownloadCloudsYaml, DownloadOpenRCv2, DownloadOpenRC,
|
||||
DownloadEC2,
|
||||
ViewCredentials, RecreateCredentials)
|
||||
|
@ -0,0 +1,37 @@
|
||||
# This is a clouds.yaml file, which can be used by OpenStack tools as a source
|
||||
# of configuration on how to connect to a cloud. If this is your only cloud,
|
||||
# just put this file in ~/.config/openstack/clouds.yaml and tools like
|
||||
# python-openstackclient will just work with no further config. (You will need
|
||||
# to add your password to the auth section)
|
||||
# If you have more than one cloud account, add the cloud entry to the clouds
|
||||
# section of your existing file and you can refer to them by name with
|
||||
# OS_CLOUD={{ cloud_name }} or --os-cloud={{ cloud_name }}
|
||||
clouds:
|
||||
{{ cloud_name }}:
|
||||
{% if profile %}
|
||||
profile: {{ profile }}
|
||||
{% endif %}
|
||||
auth:
|
||||
{% if not profile %}
|
||||
auth_url: {{ auth_url }}
|
||||
{% endif %}
|
||||
username: "{{ user.username }}"
|
||||
project_id: {{ tenant_id }}
|
||||
project_name: "{{ tenant_name }}"
|
||||
{% if user_domain_name %}
|
||||
user_domain_name: "{{ user_domain_name }}"
|
||||
{% endif %}
|
||||
{% if not profile %}
|
||||
{% if regions %}
|
||||
regions:
|
||||
{% for r in regions %}
|
||||
- {{ r }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% if region %}
|
||||
region_name: "{{ region }}"
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
interface: "{{ interface }}"
|
||||
identity_api_version: {{ os_identity_api_version }}
|
||||
{% endif %}
|
@ -22,6 +22,8 @@ from openstack_dashboard.dashboards.project.api_access import views
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^ec2/$', views.download_ec2_bundle, name='ec2'),
|
||||
url(r'^clouds.yaml/$',
|
||||
views.download_clouds_yaml_file, name='clouds.yaml'),
|
||||
url(r'^openrc/$', views.download_rc_file, name='openrc'),
|
||||
url(r'^openrcv2/$', views.download_rc_file_v2, name='openrcv2'),
|
||||
url(r'^view_credentials/$', views.CredentialsView.as_view(),
|
||||
|
@ -17,6 +17,7 @@ import logging
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django import http
|
||||
from django import shortcuts
|
||||
@ -143,14 +144,40 @@ def download_rc_file(request):
|
||||
return _download_rc_file_for_template(request, context, template)
|
||||
|
||||
|
||||
def _download_rc_file_for_template(request, context, template):
|
||||
def download_clouds_yaml_file(request):
|
||||
template = 'project/api_access/clouds.yaml.template'
|
||||
context = _get_openrc_credentials(request)
|
||||
context['cloud_name'] = getattr(
|
||||
settings, "OPENSTACK_CLOUDS_YAML_NAME", 'openstack')
|
||||
context['profile'] = getattr(
|
||||
settings, "OPENSTACK_CLOUDS_YAML_PROFILE", None)
|
||||
context['regions'] = [
|
||||
region_tuple[1] for region_tuple in getattr(
|
||||
settings, "AVAILABLE_REGIONS", [])
|
||||
]
|
||||
|
||||
if utils.get_keystone_version() >= 3:
|
||||
# make v3 specific changes
|
||||
context['user_domain_name'] = request.user.user_domain_name
|
||||
# sanity fix for removing v2.0 from the url if present
|
||||
context['auth_url'], _ = utils.fix_auth_url_version_prefix(
|
||||
context['auth_url'])
|
||||
context['os_identity_api_version'] = 3
|
||||
context['os_auth_version'] = 3
|
||||
|
||||
return _download_rc_file_for_template(request, context, template,
|
||||
'clouds.yaml')
|
||||
|
||||
|
||||
def _download_rc_file_for_template(request, context, template, filename=None):
|
||||
try:
|
||||
response = shortcuts.render(request,
|
||||
template,
|
||||
context,
|
||||
content_type="text/plain")
|
||||
tenant_name = context['tenant_name']
|
||||
disposition = 'attachment; filename="%s-openrc.sh"' % tenant_name
|
||||
if not filename:
|
||||
filename = '%s-openrc.sh' % context['tenant_name']
|
||||
disposition = 'attachment; filename="%s"' % filename
|
||||
response['Content-Disposition'] = disposition.encode('utf-8')
|
||||
response['Content-Length'] = str(len(response.content))
|
||||
return response
|
||||
|
@ -239,6 +239,12 @@ LOCALE_PATHS = [
|
||||
'openstack_dashboard/locale',
|
||||
]
|
||||
|
||||
# Set OPENSTACK_CLOUDS_YAML_NAME to provide a nicer name for this cloud for
|
||||
# the clouds.yaml file than "openstack".
|
||||
OPENSTACK_CLOUDS_YAML_NAME = 'openstack'
|
||||
# If this cloud has a vendor profile in os-client-config, put it's name here.
|
||||
OPENSTACK_CLOUDS_YAML_PROFILE = ''
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = '_member_'
|
||||
|
||||
DEFAULT_EXCEPTION_REPORTER_FILTER = 'horizon.exceptions.HorizonReporterFilter'
|
||||
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import yaml
|
||||
|
||||
from django import template
|
||||
from django.template import loader
|
||||
|
||||
@ -95,3 +97,93 @@ class TemplateRenderTest(test.TestCase):
|
||||
template.Context(context))
|
||||
|
||||
self.assertIn("OS_REGION_NAME=\"\"", out)
|
||||
|
||||
def test_clouds_yaml_set_region(self):
|
||||
context = {
|
||||
"cloud_name": "openstack",
|
||||
"user": FakeUser(),
|
||||
"tenant_id": "some-cool-id",
|
||||
"auth_url": "http://example.com",
|
||||
"tenant_name": "Tenant",
|
||||
"region": "Colorado"}
|
||||
out = yaml.load(loader.render_to_string(
|
||||
'project/api_access/clouds.yaml.template',
|
||||
context,
|
||||
template.Context(context)))
|
||||
|
||||
self.assertIn('clouds', out)
|
||||
self.assertIn('openstack', out['clouds'])
|
||||
self.assertNotIn('profile', out['clouds']['openstack'])
|
||||
self.assertEqual(
|
||||
"http://example.com",
|
||||
out['clouds']['openstack']['auth']['auth_url'])
|
||||
self.assertEqual("Colorado", out['clouds']['openstack']['region_name'])
|
||||
self.assertNotIn('regions', out['clouds']['openstack'])
|
||||
|
||||
def test_clouds_yaml_region_not_set(self):
|
||||
context = {
|
||||
"cloud_name": "openstack",
|
||||
"user": FakeUser(),
|
||||
"tenant_id": "some-cool-id",
|
||||
"auth_url": "http://example.com",
|
||||
"tenant_name": "Tenant"}
|
||||
out = yaml.load(loader.render_to_string(
|
||||
'project/api_access/clouds.yaml.template',
|
||||
context,
|
||||
template.Context(context)))
|
||||
|
||||
self.assertIn('clouds', out)
|
||||
self.assertIn('openstack', out['clouds'])
|
||||
self.assertNotIn('profile', out['clouds']['openstack'])
|
||||
self.assertEqual(
|
||||
"http://example.com",
|
||||
out['clouds']['openstack']['auth']['auth_url'])
|
||||
self.assertNotIn('region_name', out['clouds']['openstack'])
|
||||
self.assertNotIn('regions', out['clouds']['openstack'])
|
||||
|
||||
def test_clouds_yaml_regions(self):
|
||||
regions = ['region1', 'region2']
|
||||
context = {
|
||||
"cloud_name": "openstack",
|
||||
"user": FakeUser(),
|
||||
"tenant_id": "some-cool-id",
|
||||
"auth_url": "http://example.com",
|
||||
"tenant_name": "Tenant",
|
||||
"regions": regions}
|
||||
out = yaml.load(loader.render_to_string(
|
||||
'project/api_access/clouds.yaml.template',
|
||||
context,
|
||||
template.Context(context)))
|
||||
|
||||
self.assertIn('clouds', out)
|
||||
self.assertIn('openstack', out['clouds'])
|
||||
self.assertNotIn('profile', out['clouds']['openstack'])
|
||||
self.assertEqual(
|
||||
"http://example.com",
|
||||
out['clouds']['openstack']['auth']['auth_url'])
|
||||
self.assertNotIn('region_name', out['clouds']['openstack'])
|
||||
self.assertIn('regions', out['clouds']['openstack'])
|
||||
self.assertEqual(regions, out['clouds']['openstack']['regions'])
|
||||
|
||||
def test_clouds_yaml_profile(self):
|
||||
regions = ['region1', 'region2']
|
||||
context = {
|
||||
"cloud_name": "openstack",
|
||||
"user": FakeUser(),
|
||||
"profile": "example",
|
||||
"tenant_id": "some-cool-id",
|
||||
"auth_url": "http://example.com",
|
||||
"tenant_name": "Tenant",
|
||||
"regions": regions}
|
||||
out = yaml.load(loader.render_to_string(
|
||||
'project/api_access/clouds.yaml.template',
|
||||
context,
|
||||
template.Context(context)))
|
||||
|
||||
self.assertIn('clouds', out)
|
||||
self.assertIn('openstack', out['clouds'])
|
||||
self.assertIn('profile', out['clouds']['openstack'])
|
||||
self.assertEqual('example', out['clouds']['openstack']['profile'])
|
||||
self.assertNotIn('auth_url', out['clouds']['openstack']['auth'])
|
||||
self.assertNotIn('region_name', out['clouds']['openstack'])
|
||||
self.assertNotIn('regions', out['clouds']['openstack'])
|
||||
|
6
releasenotes/notes/add-clouds-yaml-f72b9a0b7df2aa79.yaml
Normal file
6
releasenotes/notes/add-clouds-yaml-f72b9a0b7df2aa79.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- Add support for horizon offering a clouds.yaml file
|
||||
for download along with the openrc files. For more
|
||||
information on clouds.yaml, see
|
||||
https://docs.openstack.org/developer/os-client-config
|
Loading…
Reference in New Issue
Block a user