Adding Domain CRUD in Admin Dashboard.
Add basic support for CRUD on Domain for admin users. This feature is only exposed if the user explicitly set the OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT to True and keystone supports V3. Creating projects, users and groups on a specific domain will be covered separately on domain-context blueprint. Implements blueprint admin-domain-crud Change-Id: If2684331776166dd9579716deebca100770a5ce0
This commit is contained in:
parent
50b7724549
commit
c119343f6a
|
@ -168,6 +168,34 @@ def keystoneclient(request, admin=False):
|
|||
return conn
|
||||
|
||||
|
||||
def domain_create(request, name, description=None, enabled=None):
|
||||
manager = keystoneclient(request, admin=True).domains
|
||||
return manager.create(name,
|
||||
description=description,
|
||||
enabled=enabled)
|
||||
|
||||
|
||||
def domain_get(request, domain_id):
|
||||
manager = keystoneclient(request, admin=True).domains
|
||||
return manager.get(domain_id)
|
||||
|
||||
|
||||
def domain_delete(request, domain_id):
|
||||
manager = keystoneclient(request, admin=True).domains
|
||||
return manager.delete(domain_id)
|
||||
|
||||
|
||||
def domain_list(request):
|
||||
manager = keystoneclient(request, admin=True).domains
|
||||
return manager.list()
|
||||
|
||||
|
||||
def domain_update(request, domain_id, name=None, description=None,
|
||||
enabled=None):
|
||||
manager = keystoneclient(request, admin=True).domains
|
||||
return manager.update(domain_id, name, description, enabled)
|
||||
|
||||
|
||||
def tenant_create(request, name, description=None, enabled=None, domain=None):
|
||||
manager = VERSIONS.get_project_manager(request, admin=True)
|
||||
if VERSIONS.active < 3:
|
||||
|
@ -399,6 +427,11 @@ def get_user_ec2_credentials(request, user_id, access_token):
|
|||
return keystoneclient(request).ec2.get(user_id, access_token)
|
||||
|
||||
|
||||
def keystone_can_edit_domain():
|
||||
backend_settings = getattr(settings, "OPENSTACK_KEYSTONE_BACKEND", {})
|
||||
return backend_settings.get('can_edit_domain', True)
|
||||
|
||||
|
||||
def keystone_can_edit_user():
|
||||
backend_settings = getattr(settings, "OPENSTACK_KEYSTONE_BACKEND", {})
|
||||
return backend_settings.get('can_edit_user', True)
|
||||
|
|
|
@ -23,7 +23,8 @@ class SystemPanels(horizon.PanelGroup):
|
|||
slug = "admin"
|
||||
name = _("System Panel")
|
||||
panels = ('overview', 'instances', 'volumes', 'flavors',
|
||||
'images', 'projects', 'users', 'networks', 'routers', 'info')
|
||||
'images', 'domains', 'projects', 'users',
|
||||
'networks', 'routers', 'info')
|
||||
|
||||
|
||||
class Admin(horizon.Dashboard):
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
DOMAIN_INFO_FIELDS = ("name",
|
||||
"description",
|
||||
"enabled")
|
||||
DOMAINS_INDEX_URL = 'horizon:admin:domains:index'
|
||||
DOMAINS_INDEX_VIEW_TEMPLATE = 'admin/domains/index.html'
|
||||
DOMAINS_CREATE_URL = 'horizon:admin:domains:create'
|
||||
DOMAINS_UPDATE_URL = 'horizon:admin:domains:update'
|
|
@ -0,0 +1,37 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.api.keystone import VERSIONS as IDENTITY_VERSIONS
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
|
||||
|
||||
class Domains(horizon.Panel):
|
||||
name = _("Domains")
|
||||
slug = 'domains'
|
||||
|
||||
|
||||
MULTIDOMAIN_SUPPORT = getattr(settings,
|
||||
'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT',
|
||||
False)
|
||||
|
||||
|
||||
if MULTIDOMAIN_SUPPORT and IDENTITY_VERSIONS.active >= 3:
|
||||
dashboard.Admin.register(Domains)
|
|
@ -0,0 +1,100 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.utils.translation import ugettext_lazy as _
|
||||
|
||||
from keystoneclient.exceptions import ClientException
|
||||
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from .constants import DOMAINS_CREATE_URL, \
|
||||
DOMAINS_UPDATE_URL
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateDomainLink(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Domain")
|
||||
url = DOMAINS_CREATE_URL
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
|
||||
def allowed(self, request, domain):
|
||||
return api.keystone.keystone_can_edit_domain()
|
||||
|
||||
|
||||
class EditDomainLink(tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit")
|
||||
url = DOMAINS_UPDATE_URL
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
def allowed(self, request, domain):
|
||||
return api.keystone.keystone_can_edit_domain()
|
||||
|
||||
|
||||
class DeleteDomainsAction(tables.DeleteAction):
|
||||
name = "delete"
|
||||
data_type_singular = _("Domain")
|
||||
data_type_plural = _("Domains")
|
||||
|
||||
def allowed(self, request, datum):
|
||||
return api.keystone.keystone_can_edit_domain()
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
domain = self.table.get_object_by_id(obj_id)
|
||||
if domain.enabled:
|
||||
msg = _('Domain "%s" must be disabled before it can be deleted.') \
|
||||
% domain.name
|
||||
messages.error(request, msg)
|
||||
raise ClientException(409, msg)
|
||||
else:
|
||||
LOG.info('Deleting domain "%s".' % obj_id)
|
||||
api.keystone.domain_delete(request, obj_id)
|
||||
|
||||
|
||||
class DomainFilterAction(tables.FilterAction):
|
||||
def filter(self, table, domains, filter_string):
|
||||
""" Naive case-insensitive search """
|
||||
q = filter_string.lower()
|
||||
|
||||
def comp(domain):
|
||||
if q in domain.name.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
return filter(comp, domains)
|
||||
|
||||
|
||||
class DomainsTable(tables.DataTable):
|
||||
name = tables.Column('name', verbose_name=_('Name'))
|
||||
description = tables.Column(lambda obj: getattr(obj, 'description', None),
|
||||
verbose_name=_('Description'))
|
||||
id = tables.Column('id', verbose_name=_('Domain ID'))
|
||||
enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True)
|
||||
|
||||
class Meta:
|
||||
name = "domains"
|
||||
verbose_name = _("Domains")
|
||||
row_actions = (EditDomainLink, DeleteDomainsAction)
|
||||
table_actions = (DomainFilterAction, CreateDomainLink,
|
||||
DeleteDomainsAction)
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Domains" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Domains") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,196 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 import http
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from mox import IgnoreArg, IsA
|
||||
|
||||
from horizon.workflows.views import WorkflowView
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
from .constants import DOMAINS_INDEX_VIEW_TEMPLATE, \
|
||||
DOMAINS_INDEX_URL as index_url, \
|
||||
DOMAINS_CREATE_URL as create_url, \
|
||||
DOMAINS_UPDATE_URL as update_url
|
||||
from .workflows import CreateDomain, UpdateDomain
|
||||
|
||||
|
||||
DOMAINS_INDEX_URL = reverse(index_url)
|
||||
DOMAIN_CREATE_URL = reverse(create_url)
|
||||
DOMAIN_UPDATE_URL = reverse(update_url, args=[1])
|
||||
|
||||
|
||||
class DomainsViewTests(test.BaseAdminViewTests):
|
||||
@test.create_stubs({api.keystone: ('domain_list',)})
|
||||
def test_index(self):
|
||||
api.keystone.domain_list(IgnoreArg()).AndReturn(self.domains.list())
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(DOMAINS_INDEX_URL)
|
||||
|
||||
self.assertTemplateUsed(res, DOMAINS_INDEX_VIEW_TEMPLATE)
|
||||
self.assertItemsEqual(res.context['table'].data, self.domains.list())
|
||||
self.assertContains(res, 'Create Domain')
|
||||
self.assertContains(res, 'Edit')
|
||||
self.assertContains(res, 'Delete Domain')
|
||||
|
||||
@test.create_stubs({api.keystone: ('domain_list',
|
||||
'keystone_can_edit_domain')})
|
||||
def test_index_with_keystone_can_edit_domain_false(self):
|
||||
api.keystone.domain_list(IgnoreArg()).AndReturn(self.domains.list())
|
||||
api.keystone.keystone_can_edit_domain() \
|
||||
.MultipleTimes().AndReturn(False)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(DOMAINS_INDEX_URL)
|
||||
|
||||
self.assertTemplateUsed(res, DOMAINS_INDEX_VIEW_TEMPLATE)
|
||||
self.assertItemsEqual(res.context['table'].data, self.domains.list())
|
||||
self.assertNotContains(res, 'Create Domain')
|
||||
self.assertNotContains(res, 'Edit')
|
||||
self.assertNotContains(res, 'Delete Domain')
|
||||
|
||||
@test.create_stubs({api.keystone: ('domain_list',
|
||||
'domain_delete')})
|
||||
def test_delete_domain(self):
|
||||
domain = self.domains.get(id="2")
|
||||
|
||||
api.keystone.domain_list(IgnoreArg()).AndReturn(self.domains.list())
|
||||
api.keystone.domain_delete(IgnoreArg(), domain.id)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
formData = {'action': 'domains__delete__%s' % domain.id}
|
||||
res = self.client.post(DOMAINS_INDEX_URL, formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res, DOMAINS_INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.keystone: ('domain_list', )})
|
||||
def test_delete_with_enabled_domain(self):
|
||||
domain = self.domains.get(id="1")
|
||||
|
||||
api.keystone.domain_list(IgnoreArg()).AndReturn(self.domains.list())
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
formData = {'action': 'domains__delete__%s' % domain.id}
|
||||
res = self.client.post(DOMAINS_INDEX_URL, formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res, DOMAINS_INDEX_URL)
|
||||
self.assertMessageCount(error=2)
|
||||
|
||||
|
||||
class CreateDomainWorkflowTests(test.BaseAdminViewTests):
|
||||
def _get_domain_info(self, domain):
|
||||
domain_info = {"name": domain.name,
|
||||
"description": domain.description,
|
||||
"enabled": domain.enabled}
|
||||
return domain_info
|
||||
|
||||
def _get_workflow_data(self, domain):
|
||||
domain_info = self._get_domain_info(domain)
|
||||
return domain_info
|
||||
|
||||
def test_add_domain_get(self):
|
||||
url = reverse('horizon:admin:domains:create')
|
||||
res = self.client.get(url)
|
||||
|
||||
self.assertTemplateUsed(res, WorkflowView.template_name)
|
||||
|
||||
workflow = res.context['workflow']
|
||||
self.assertEqual(res.context['workflow'].name, CreateDomain.name)
|
||||
|
||||
self.assertQuerysetEqual(workflow.steps,
|
||||
['<CreateDomainInfo: create_domain>', ])
|
||||
|
||||
@test.create_stubs({api.keystone: ('domain_create', )})
|
||||
def test_add_domain_post(self):
|
||||
domain = self.domains.get(id="1")
|
||||
|
||||
api.keystone.domain_create(IsA(http.HttpRequest),
|
||||
description=domain.description,
|
||||
enabled=domain.enabled,
|
||||
name=domain.name).AndReturn(domain)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
workflow_data = self._get_workflow_data(domain)
|
||||
|
||||
res = self.client.post(DOMAIN_CREATE_URL, workflow_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, DOMAINS_INDEX_URL)
|
||||
|
||||
|
||||
class UpdateDomainWorkflowTests(test.BaseAdminViewTests):
|
||||
def _get_domain_info(self, domain):
|
||||
domain_info = {"domain_id": domain.id,
|
||||
"name": domain.name,
|
||||
"description": domain.description,
|
||||
"enabled": domain.enabled}
|
||||
return domain_info
|
||||
|
||||
def _get_workflow_data(self, domain):
|
||||
domain_info = self._get_domain_info(domain)
|
||||
return domain_info
|
||||
|
||||
@test.create_stubs({api.keystone: ('domain_get', )})
|
||||
def test_update_domain_get(self):
|
||||
domain = self.domains.get(id="1")
|
||||
|
||||
api.keystone.domain_get(IsA(http.HttpRequest), '1').AndReturn(domain)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(DOMAIN_UPDATE_URL)
|
||||
|
||||
self.assertTemplateUsed(res, WorkflowView.template_name)
|
||||
|
||||
workflow = res.context['workflow']
|
||||
self.assertEqual(res.context['workflow'].name, UpdateDomain.name)
|
||||
|
||||
self.assertQuerysetEqual(workflow.steps,
|
||||
['<UpdateDomainInfo: update_domain>', ])
|
||||
|
||||
@test.create_stubs({api.keystone: ('domain_get',
|
||||
'domain_update')})
|
||||
def test_update_domain_post(self):
|
||||
domain = self.domains.get(id="1")
|
||||
test_description = 'updated description'
|
||||
|
||||
api.keystone.domain_get(IsA(http.HttpRequest), '1').AndReturn(domain)
|
||||
api.keystone.domain_update(IsA(http.HttpRequest),
|
||||
description=test_description,
|
||||
domain_id=domain.id,
|
||||
enabled=domain.enabled,
|
||||
name=domain.name).AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
workflow_data = self._get_workflow_data(domain)
|
||||
workflow_data['description'] = test_description
|
||||
|
||||
res = self.client.post(DOMAIN_UPDATE_URL, workflow_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertMessageCount(success=1)
|
||||
self.assertRedirectsNoFollow(res, DOMAINS_INDEX_URL)
|
|
@ -0,0 +1,27 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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, CreateDomainView, UpdateDomainView
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', IndexView.as_view(), name='index'),
|
||||
url(r'^create$', CreateDomainView.as_view(), name='create'),
|
||||
url(r'^(?P<domain_id>[^/]+)/update/$',
|
||||
UpdateDomainView.as_view(), name='update')
|
||||
)
|
|
@ -0,0 +1,68 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
from .constants import DOMAIN_INFO_FIELDS, DOMAINS_INDEX_URL, \
|
||||
DOMAINS_INDEX_VIEW_TEMPLATE
|
||||
from .tables import DomainsTable
|
||||
from .workflows import CreateDomain, UpdateDomain
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = DomainsTable
|
||||
template_name = DOMAINS_INDEX_VIEW_TEMPLATE
|
||||
|
||||
def get_data(self):
|
||||
domains = []
|
||||
try:
|
||||
domains = api.keystone.domain_list(self.request)
|
||||
except:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve domain list.'))
|
||||
return domains
|
||||
|
||||
|
||||
class CreateDomainView(workflows.WorkflowView):
|
||||
workflow_class = CreateDomain
|
||||
|
||||
|
||||
class UpdateDomainView(workflows.WorkflowView):
|
||||
workflow_class = UpdateDomain
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(UpdateDomainView, self).get_initial()
|
||||
|
||||
domain_id = self.kwargs['domain_id']
|
||||
initial['domain_id'] = domain_id
|
||||
|
||||
try:
|
||||
# get initial domain info
|
||||
domain_info = api.keystone.domain_get(self.request,
|
||||
domain_id)
|
||||
for field in DOMAIN_INFO_FIELDS:
|
||||
initial[field] = getattr(domain_info, field, None)
|
||||
except:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve domain details.'),
|
||||
redirect=reverse(DOMAINS_INDEX_URL))
|
||||
return initial
|
|
@ -0,0 +1,127 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import workflows
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from .constants import DOMAINS_INDEX_URL
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateDomainInfoAction(workflows.Action):
|
||||
name = forms.CharField(label=_("Name"),
|
||||
required=True)
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(),
|
||||
label=_("Description"),
|
||||
required=False)
|
||||
enabled = forms.BooleanField(label=_("Enabled"),
|
||||
required=False,
|
||||
initial=True)
|
||||
|
||||
class Meta:
|
||||
name = _("Domain Info")
|
||||
slug = "create_domain"
|
||||
help_text = _("From here you can create a new domain to organize "
|
||||
"projects, groups and users.")
|
||||
|
||||
|
||||
class CreateDomainInfo(workflows.Step):
|
||||
action_class = CreateDomainInfoAction
|
||||
contributes = ("domain_id",
|
||||
"name",
|
||||
"description",
|
||||
"enabled")
|
||||
|
||||
|
||||
class CreateDomain(workflows.Workflow):
|
||||
slug = "create_domain"
|
||||
name = _("Create Domain")
|
||||
finalize_button_name = _("Create Domain")
|
||||
success_message = _('Created new domain "%s".')
|
||||
failure_message = _('Unable to create domain "%s".')
|
||||
success_url = DOMAINS_INDEX_URL
|
||||
default_steps = (CreateDomainInfo, )
|
||||
|
||||
def format_status_message(self, message):
|
||||
return message % self.context.get('name', 'unknown domain')
|
||||
|
||||
def handle(self, request, data):
|
||||
# create the domain
|
||||
try:
|
||||
LOG.info('Creating domain with name "%s"' % data['name'])
|
||||
desc = data['description']
|
||||
new_domain = api.keystone.domain_create(request,
|
||||
name=data['name'],
|
||||
description=desc,
|
||||
enabled=data['enabled'])
|
||||
except:
|
||||
exceptions.handle(request, ignore=True)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class UpdateDomainInfoAction(CreateDomainInfoAction):
|
||||
|
||||
class Meta:
|
||||
name = _("Domain Info")
|
||||
slug = 'update_domain'
|
||||
help_text = _("From here you can edit the domain details.")
|
||||
|
||||
|
||||
class UpdateDomainInfo(workflows.Step):
|
||||
action_class = UpdateDomainInfoAction
|
||||
depends_on = ("domain_id",)
|
||||
contributes = ("name",
|
||||
"description",
|
||||
"enabled")
|
||||
|
||||
|
||||
class UpdateDomain(workflows.Workflow):
|
||||
slug = "update_domain"
|
||||
name = _("Edit Domain")
|
||||
finalize_button_name = _("Save")
|
||||
success_message = _('Modified domain "%s".')
|
||||
failure_message = _('Unable to modify domain "%s".')
|
||||
success_url = DOMAINS_INDEX_URL
|
||||
default_steps = (UpdateDomainInfo, )
|
||||
|
||||
def format_status_message(self, message):
|
||||
return message % self.context.get('name', 'unknown domain')
|
||||
|
||||
def handle(self, request, data):
|
||||
domain_id = data.pop('domain_id')
|
||||
|
||||
try:
|
||||
LOG.info('Updating domain with name "%s"' % data['name'])
|
||||
api.keystone.domain_update(request,
|
||||
domain_id=domain_id,
|
||||
name=data['name'],
|
||||
description=data['description'],
|
||||
enabled=data['enabled'])
|
||||
except:
|
||||
exceptions.handle(request, ignore=True)
|
||||
return False
|
||||
return True
|
|
@ -136,7 +136,8 @@ OPENSTACK_KEYSTONE_DEFAULT_ROLE = "Member"
|
|||
OPENSTACK_KEYSTONE_BACKEND = {
|
||||
'name': 'native',
|
||||
'can_edit_user': True,
|
||||
'can_edit_project': True
|
||||
'can_edit_project': True,
|
||||
'can_edit_domain': True
|
||||
}
|
||||
|
||||
OPENSTACK_HYPERVISOR_FEATURES = {
|
||||
|
|
|
@ -70,10 +70,14 @@ AVAILABLE_REGIONS = [
|
|||
OPENSTACK_KEYSTONE_URL = "http://localhost:5000/v2.0"
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "Member"
|
||||
|
||||
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
|
||||
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'test_domain'
|
||||
|
||||
OPENSTACK_KEYSTONE_BACKEND = {
|
||||
'name': 'native',
|
||||
'can_edit_user': True,
|
||||
'can_edit_project': True
|
||||
'can_edit_project': True,
|
||||
'can_edit_domain': True
|
||||
}
|
||||
|
||||
OPENSTACK_QUANTUM_NETWORK = {
|
||||
|
|
|
@ -18,6 +18,7 @@ from django.conf import settings
|
|||
from django.utils import datetime_safe
|
||||
|
||||
from keystoneclient.v2_0 import users, tenants, tokens, roles, ec2
|
||||
from keystoneclient.v3 import domains
|
||||
|
||||
from .utils import TestDataContainer
|
||||
|
||||
|
@ -88,6 +89,7 @@ SERVICE_CATALOG = [
|
|||
def data(TEST):
|
||||
TEST.service_catalog = SERVICE_CATALOG
|
||||
TEST.tokens = TestDataContainer()
|
||||
TEST.domains = TestDataContainer()
|
||||
TEST.users = TestDataContainer()
|
||||
TEST.tenants = TestDataContainer()
|
||||
TEST.roles = TestDataContainer()
|
||||
|
@ -103,6 +105,19 @@ def data(TEST):
|
|||
TEST.roles.admin = admin_role
|
||||
TEST.roles.member = member_role
|
||||
|
||||
domain_dict = {'id': "1",
|
||||
'name': 'test_domain',
|
||||
'description': "a test domain.",
|
||||
'enabled': True}
|
||||
domain_dict_2 = {'id': "2",
|
||||
'name': 'disabled_domain',
|
||||
'description': "a disabled test domain.",
|
||||
'enabled': False}
|
||||
domain = domains.Domain(domains.DomainManager, domain_dict)
|
||||
disabled_domain = domains.Domain(domains.DomainManager, domain_dict_2)
|
||||
TEST.domains.add(domain, disabled_domain)
|
||||
TEST.domain = domain # Your "current" domain
|
||||
|
||||
user_dict = {'id': "1",
|
||||
'name': 'test_user',
|
||||
'email': 'test@example.com',
|
||||
|
|
Loading…
Reference in New Issue