Enable using multiple API versions for Identity Service.

Introduces a mechanism for switching between API versions,
and implements it in a proof-of-concept fashion for the
Keystone v3 API. Converts the existing Users and Projects
API methods to use it.

This changed some method signatures, and therefore altered
a number of views and tests. However, all code related to
the version cahnges is contained in the api.keystone module.
This seems like a sane process going forwards.

Future TODOs are marked in the code, including auto-detection
of API versions and better endpoint URL construction.

Partially implements blueprint api-capability-detection

Change-Id: Ied04200fe6c257aac2241d36628965a3bb6658b9
This commit is contained in:
Gabriel Hurley 2013-04-30 17:46:18 -07:00
parent a424a35da4
commit 68a55e3fe8
22 changed files with 476 additions and 360 deletions

View File

@ -33,6 +33,38 @@ __all__ = ('APIResourceWrapper', 'APIDictWrapper',
LOG = logging.getLogger(__name__)
class APIVersionManager(object):
""" Object to store and manage API versioning data and utility methods. """
SETTINGS_KEY = "OPENSTACK_API_VERSIONS"
def __init__(self, service_type, preferred_version=None):
self.service_type = service_type
self.preferred = preferred_version
self._active = None
self.supported = {}
@property
def active(self):
if self._active is None:
self.get_active_version()
return self._active
def load_supported_version(self, version, data):
self.supported[version] = data
def get_active_version(self):
if self._active is not None:
return self.supported[self._active]
key = getattr(settings, self.SETTINGS_KEY, {}).get(self.service_type)
if key is None:
# TODO: support API version discovery here; we'll leave the setting
# in as a way of overriding the latest available version.
key = self.preferred
self._active = key
return self.supported[self._active]
class APIResourceWrapper(object):
""" Simple wrapper for api objects

View File

@ -21,18 +21,17 @@
import logging
import urlparse
from pkg_resources import get_distribution
from django.conf import settings
from django.contrib.auth import logout
from django.utils.translation import ugettext_lazy as _
from keystoneclient import service_catalog
from keystoneclient.v2_0 import client as keystone_client
from keystoneclient.v2_0 import tokens
from keystoneclient.exceptions import ClientException
from openstack_auth.backend import KEYSTONE_CLIENT_ATTR
from horizon import exceptions
from horizon import messages
from openstack_dashboard.api import base
@ -41,6 +40,39 @@ LOG = logging.getLogger(__name__)
DEFAULT_ROLE = None
# Set up our data structure for managing Identity API versions, and
# add a couple utility methods to it.
class IdentityAPIVersionManager(base.APIVersionManager):
def upgrade_v2_user(self, user):
if getattr(user, "project_id", None) is None:
user.project_id = getattr(user, "tenantId", None)
return user
def get_project_manager(self, *args, **kwargs):
if VERSIONS.active < 3:
manager = keystoneclient(*args, **kwargs).tenants
else:
manager = keystoneclient(*args, **kwargs).projects
return manager
VERSIONS = IdentityAPIVersionManager("identity", preferred_version=3)
# Import from oldest to newest so that "preferred" takes correct precedence.
try:
from keystoneclient.v2_0 import client as keystone_client_v2
VERSIONS.load_supported_version(2.0, {"client": keystone_client_v2})
except ImportError:
pass
try:
from keystoneclient.v3 import client as keystone_client_v3
VERSIONS.load_supported_version(3, {"client": keystone_client_v3})
except ImportError:
pass
class Service(base.APIDictWrapper):
""" Wrapper for a dict based on the service data from keystone. """
_attrs = ['id', 'type', 'name']
@ -55,8 +87,7 @@ class Service(base.APIDictWrapper):
def __unicode__(self):
if(self.type == "identity"):
return _("%(type)s (%(backend)s backend)") \
% {"type": self.type,
"backend": keystone_backend_name()}
% {"type": self.type, "backend": keystone_backend_name()}
else:
return self.type
@ -66,11 +97,20 @@ class Service(base.APIDictWrapper):
def _get_endpoint_url(request, endpoint_type, catalog=None):
if getattr(request.user, "service_catalog", None):
return base.url_for(request,
service_type='identity',
endpoint_type=endpoint_type)
return request.session.get('region_endpoint',
getattr(settings, 'OPENSTACK_KEYSTONE_URL'))
url = base.url_for(request,
service_type='identity',
endpoint_type=endpoint_type)
else:
auth_url = getattr(settings, 'OPENSTACK_KEYSTONE_URL')
url = request.session.get('region_endpoint', auth_url)
# TODO: When the Service Catalog no longer contains API versions
# in the endpoints this can be removed.
bits = urlparse.urlparse(url)
root = "://".join((bits.scheme, bits.netloc))
url = "%s/v%s" % (root, VERSIONS.active)
return url
def keystoneclient(request, admin=False):
@ -105,6 +145,8 @@ def keystoneclient(request, admin=False):
'OPENSTACK_ENDPOINT_TYPE',
'internalURL')
api_version = VERSIONS.get_active_version()
# Take care of client connection caching/fetching a new client.
# Admin vs. non-admin clients are cached separately for token matching.
cache_attr = "_keystoneclient_admin" if admin else KEYSTONE_CLIENT_ATTR
@ -116,102 +158,168 @@ def keystoneclient(request, admin=False):
endpoint = _get_endpoint_url(request, endpoint_type)
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
LOG.debug("Creating a new keystoneclient connection to %s." % endpoint)
conn = keystone_client.Client(
token=user.token.id, endpoint=endpoint,
original_ip=request.environ.get('REMOTE_ADDR', ''),
insecure=insecure)
remote_addr = request.environ.get('REMOTE_ADDR', '')
conn = api_version['client'].Client(token=user.token.id,
endpoint=endpoint,
original_ip=remote_addr,
insecure=insecure,
debug=settings.DEBUG)
setattr(request, cache_attr, conn)
return conn
def tenant_create(request, tenant_name, description, enabled):
return keystoneclient(request, admin=True).tenants.create(tenant_name,
description,
enabled)
def tenant_get(request, tenant_id, admin=False):
return keystoneclient(request, admin=admin).tenants.get(tenant_id)
def tenant_delete(request, tenant_id):
keystoneclient(request, admin=True).tenants.delete(tenant_id)
def tenant_list(request, admin=False):
return keystoneclient(request, admin=admin).tenants.list()
def tenant_update(request, tenant_id, tenant_name, description, enabled):
return keystoneclient(request, admin=True).tenants.update(tenant_id,
tenant_name,
description,
enabled)
def token_create_scoped(request, tenant, token):
'''
Creates a scoped token using the tenant id and unscoped token; retrieves
the service catalog for the given tenant.
'''
if hasattr(request, '_keystone'):
del request._keystone
c = keystoneclient(request)
raw_token = c.tokens.authenticate(tenant_id=tenant,
token=token,
return_raw=True)
c.service_catalog = service_catalog.ServiceCatalog(raw_token)
if request.user.is_superuser:
c.management_url = c.service_catalog.url_for(service_type='identity',
endpoint_type='adminURL')
def tenant_create(request, name, description=None, enabled=None, domain=None):
manager = VERSIONS.get_project_manager(request, admin=True)
if VERSIONS.active < 3:
return manager.create(name, description, enabled)
else:
endpoint_type = getattr(settings,
'OPENSTACK_ENDPOINT_TYPE',
'internalURL')
c.management_url = c.service_catalog.url_for(
service_type='identity', endpoint_type=endpoint_type)
scoped_token = tokens.Token(tokens.TokenManager, raw_token)
return scoped_token
return manager.create(name, domain,
description=description,
enabled=enabled)
def user_list(request, tenant_id=None):
return keystoneclient(request, admin=True).users.list(tenant_id=tenant_id)
# TODO(gabriel): Is there ever a valid case for admin to be false here?
# A quick search through the codebase reveals that it's always called with
# admin=true so I suspect we could eliminate it entirely as with the other
# tenant commands.
def tenant_get(request, project, admin=True):
manager = VERSIONS.get_project_manager(request, admin=admin)
return manager.get(project)
def user_create(request, user_id, email, password, tenant_id, enabled):
return keystoneclient(request, admin=True).users.create(user_id,
password,
email,
tenant_id,
enabled)
def tenant_delete(request, project):
manager = VERSIONS.get_project_manager(request, admin=True)
return manager.delete(project)
def tenant_list(request, domain=None, user=None):
manager = VERSIONS.get_project_manager(request, admin=True)
if VERSIONS.active < 3:
return manager.list()
else:
return manager.list(domain=domain, user=user)
def tenant_update(request, project, name=None, description=None,
enabled=None, domain=None):
manager = VERSIONS.get_project_manager(request, admin=True)
if VERSIONS.active < 3:
return manager.update(project, name, description, enabled)
else:
return manager.update(project, name=name, description=description,
enabled=enabled, domain=domain)
def user_list(request, project=None, domain=None, group=None):
if VERSIONS.active < 3:
kwargs = {"tenant_id": project}
else:
kwargs = {
"project": project,
"domain": domain,
"group": group
}
users = keystoneclient(request, admin=True).users.list(**kwargs)
return [VERSIONS.upgrade_v2_user(user) for user in users]
def user_create(request, name=None, email=None, password=None, project=None,
enabled=None, domain=None):
manager = keystoneclient(request, admin=True).users
if VERSIONS.active < 3:
user = manager.create(name, password, email, project, enabled)
return VERSIONS.upgrade_v2_user(user)
else:
return manager.create(name, password=password, email=email,
project=project, enabled=enabled, domain=domain)
def user_delete(request, user_id):
keystoneclient(request, admin=True).users.delete(user_id)
return keystoneclient(request, admin=True).users.delete(user_id)
def user_get(request, user_id, admin=True):
return keystoneclient(request, admin=admin).users.get(user_id)
user = keystoneclient(request, admin=admin).users.get(user_id)
return VERSIONS.upgrade_v2_user(user)
def user_update(request, user, **data):
return keystoneclient(request, admin=True).users.update(user, **data)
manager = keystoneclient(request, admin=True).users
error = None
if not keystone_can_edit_user():
raise ClientException(405, _("Identity service does not allow "
"editing user data."))
# The v2 API updates user model, password and default project separately
if VERSIONS.active < 3:
password = data.pop('password')
project = data.pop('project')
# Update user details
try:
user = manager.update(user, **data)
except:
error = exceptions.handle(request, ignore=True)
# Update default tenant
try:
user_update_tenant(request, user, project)
user.tenantId = project
except:
error = exceptions.handle(request, ignore=True)
# Check for existing roles
# Show a warning if no role exists for the project
user_roles = roles_for_user(request, user, project)
if not user_roles:
messages.warning(request,
_('User %s has no role defined for '
'that project.')
% data.get('name', None))
# If present, update password
# FIXME(gabriel): password change should be its own form + view
if password:
try:
user_update_password(request, user, password)
if user == request.user.id:
logout(request)
except:
error = exceptions.handle(request, ignore=True)
if error is not None:
raise error
# v3 API is so much simpler...
else:
user = manager.update(user, **data)
return VERSIONS.upgrade_v2_user(user)
def user_update_enabled(request, user_id, enabled):
return keystoneclient(request, admin=True).users.update_enabled(user_id,
enabled)
def user_update_enabled(request, user, enabled):
manager = keystoneclient(request, admin=True).users
if VERSIONS.active < 3:
return manager.update_enabled(user, enabled)
else:
return manager.update(user, enabled=enabled)
def user_update_password(request, user_id, password, admin=True):
return keystoneclient(request, admin=admin).users.update_password(user_id,
password)
def user_update_password(request, user, password, admin=True):
manager = keystoneclient(request, admin=admin).users
if VERSIONS.active < 3:
return manager.update_password(user, password)
else:
return manager.update(user, password=password)
def user_update_tenant(request, user_id, tenant_id, admin=True):
return keystoneclient(request, admin=admin).users.update_tenant(user_id,
tenant_id)
def user_update_tenant(request, user, project, admin=True):
manager = keystoneclient(request, admin=admin).users
if VERSIONS.active < 3:
return manager.update_tenant(user, project)
else:
return manager.update(user, project=project)
def role_list(request):
@ -220,29 +328,42 @@ def role_list(request):
def roles_for_user(request, user, project):
return keystoneclient(request, admin=True).roles.roles_for_user(user,
project)
manager = keystoneclient(request, admin=True).roles
if VERSIONS.active < 3:
return manager.roles_for_user(user, project)
else:
return manager.list(user=user, project=project)
def add_tenant_user_role(request, tenant_id, user_id, role_id):
def add_tenant_user_role(request, project=None, user=None, role=None,
group=None, domain=None):
""" Adds a role for a user on a tenant. """
return keystoneclient(request, admin=True).roles.add_user_role(user_id,
role_id,
tenant_id)
manager = keystoneclient(request, admin=True).roles
if VERSIONS.active < 3:
return manager.add_user_role(user, role, project)
else:
return manager.grant(role, user=user, project=project,
group=group, domain=domain)
def remove_tenant_user_role(request, tenant_id, user_id, role_id):
def remove_tenant_user_role(request, project=None, user=None, role=None,
group=None, domain=None):
""" Removes a given single role for a user from a tenant. """
client = keystoneclient(request, admin=True)
client.roles.remove_user_role(user_id, role_id, tenant_id)
manager = keystoneclient(request, admin=True).roles
if VERSIONS.active < 3:
return manager.remove_user_role(user, role, project)
else:
return manager.revoke(role, user=user, project=project,
group=group, domain=domain)
def remove_tenant_user(request, tenant_id, user_id):
def remove_tenant_user(request, project=None, user=None, domain=None):
""" Removes all roles from a user on a tenant, removing them from it. """
client = keystoneclient(request, admin=True)
roles = client.roles.roles_for_user(user_id, tenant_id)
roles = client.roles.roles_for_user(user, project)
for role in roles:
client.roles.remove_user_role(user_id, role.id, tenant_id)
remove_tenant_user_role(request, user=user, role=role.id,
project=project, domain=domain)
def get_default_role(request):

View File

@ -33,7 +33,7 @@ class InstanceViewTest(test.BaseAdminViewTests):
servers = self.servers.list()
flavors = self.flavors.list()
tenants = self.tenants.list()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\
api.keystone.tenant_list(IsA(http.HttpRequest)).\
AndReturn(tenants)
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest),
@ -62,7 +62,7 @@ class InstanceViewTest(test.BaseAdminViewTests):
.AndReturn([servers, False])
api.nova.flavor_list(IsA(http.HttpRequest)). \
AndRaise(self.exceptions.nova)
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\
api.keystone.tenant_list(IsA(http.HttpRequest)).\
AndReturn(tenants)
for server in servers:
api.nova.flavor_get(IsA(http.HttpRequest), server.flavor["id"]). \
@ -93,7 +93,7 @@ class InstanceViewTest(test.BaseAdminViewTests):
.AndReturn([servers, False])
api.nova.flavor_list(IsA(http.HttpRequest)). \
AndReturn(flavors)
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\
api.keystone.tenant_list(IsA(http.HttpRequest)).\
AndReturn(tenants)
for server in servers:
api.nova.flavor_get(IsA(http.HttpRequest), server.flavor["id"]). \
@ -153,7 +153,7 @@ class InstanceViewTest(test.BaseAdminViewTests):
@test.create_stubs({api.nova: ('flavor_list', 'server_list',),
api.keystone: ('tenant_list',)})
def test_index_options_before_migrate(self):
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\
api.keystone.tenant_list(IsA(http.HttpRequest)).\
AndReturn(self.tenants.list())
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest),
@ -173,8 +173,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
def test_index_options_after_migrate(self):
server = self.servers.first()
server.status = "VERIFY_RESIZE"
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\
AndReturn(self.tenants.list())
api.keystone.tenant_list(IsA(http.HttpRequest)) \
.AndReturn(self.tenants.list())
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest),
all_tenants=True, search_opts=search_opts) \

View File

@ -73,7 +73,7 @@ class AdminIndexView(tables.DataTableView):
# Gather our tenants to correlate against IDs
try:
tenants = api.keystone.tenant_list(self.request, admin=True)
tenants = api.keystone.tenant_list(self.request)
except:
tenants = []
msg = _('Unable to retrieve instance tenant information.')

View File

@ -48,7 +48,7 @@ class CreateNetwork(forms.SelfHandlingForm):
def __init__(self, request, *args, **kwargs):
super(CreateNetwork, self).__init__(request, *args, **kwargs)
tenant_choices = [('', _("Select a project"))]
for tenant in api.keystone.tenant_list(request, admin=True):
for tenant in api.keystone.tenant_list(request):
if tenant.enabled:
tenant_choices.append((tenant.id, tenant.name))
self.fields['tenant_id'].choices = tenant_choices

View File

@ -35,7 +35,7 @@ class NetworkTests(test.BaseAdminViewTests):
tenants = self.tenants.list()
api.quantum.network_list(IsA(http.HttpRequest)) \
.AndReturn(self.networks.list())
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn(tenants)
self.mox.ReplayAll()
@ -151,7 +151,7 @@ class NetworkTests(test.BaseAdminViewTests):
@test.create_stubs({api.keystone: ('tenant_list',)})
def test_network_create_get(self):
tenants = self.tenants.list()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn(tenants)
self.mox.ReplayAll()
@ -166,7 +166,7 @@ class NetworkTests(test.BaseAdminViewTests):
tenants = self.tenants.list()
tenant_id = self.tenants.first().id
network = self.networks.first()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn(tenants)
params = {'name': network.name,
'tenant_id': tenant_id,
@ -194,7 +194,7 @@ class NetworkTests(test.BaseAdminViewTests):
tenants = self.tenants.list()
tenant_id = self.tenants.first().id
network = self.networks.first()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn(tenants)
params = {'name': network.name,
'tenant_id': tenant_id,
@ -301,7 +301,7 @@ class NetworkTests(test.BaseAdminViewTests):
def test_delete_network(self):
tenants = self.tenants.list()
network = self.networks.first()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn(tenants)
api.quantum.network_list(IsA(http.HttpRequest))\
.AndReturn([network])
@ -320,7 +320,7 @@ class NetworkTests(test.BaseAdminViewTests):
def test_delete_network_exception(self):
tenants = self.tenants.list()
network = self.networks.first()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn(tenants)
api.quantum.network_list(IsA(http.HttpRequest))\
.AndReturn([network])

View File

@ -42,7 +42,7 @@ class IndexView(tables.DataTableView):
def _get_tenant_list(self):
if not hasattr(self, "_tenants"):
try:
tenants = api.keystone.tenant_list(self.request, admin=True)
tenants = api.keystone.tenant_list(self.request)
except:
tenants = []
msg = _('Unable to retrieve instance tenant information.')

View File

@ -45,7 +45,7 @@ class UsageViewTests(test.BaseAdminViewTests):
now = timezone.now()
usage_obj = api.nova.NovaUsage(self.usages.first())
quota_data = self.quota_usages.first()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \
api.keystone.tenant_list(IsA(http.HttpRequest)) \
.AndReturn(self.tenants.list())
api.nova.usage_list(IsA(http.HttpRequest),
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
@ -77,7 +77,7 @@ class UsageViewTests(test.BaseAdminViewTests):
now = timezone.now()
usage_obj = api.nova.NovaUsage(self.usages.first())
quota_data = self.quota_usages.first()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \
api.keystone.tenant_list(IsA(http.HttpRequest)) \
.AndReturn(self.tenants.list())
api.nova.usage_list(IsA(http.HttpRequest),
datetime.datetime(now.year, now.month, 1, 0, 0, 0),

View File

@ -41,7 +41,7 @@ class GlobalOverview(usage.UsageView):
data = super(GlobalOverview, self).get_data()
# Pre-fill tenant names
try:
tenants = api.keystone.tenant_list(self.request, admin=True)
tenants = api.keystone.tenant_list(self.request)
except:
tenants = []
exceptions.handle(self.request,

View File

@ -25,9 +25,9 @@ from openstack_dashboard.dashboards.admin.users.forms import CreateUserForm
class CreateUser(CreateUserForm):
role_id = forms.ChoiceField(widget=forms.HiddenInput())
tenant_id = forms.CharField(widget=forms.HiddenInput())
project = forms.CharField(widget=forms.HiddenInput())
def __init__(self, request, *args, **kwargs):
super(CreateUser, self).__init__(request, *args, **kwargs)
tenant_id = self.request.path.split("/")[-1]
self.fields['tenant_id'].initial = tenant_id
project = self.request.path.split("/")[-1]
self.fields['project'].initial = project

View File

@ -34,7 +34,7 @@ INDEX_URL = reverse('horizon:admin:projects:index')
class TenantsViewTests(test.BaseAdminViewTests):
def test_index(self):
self.mox.StubOutWithMock(api.keystone, 'tenant_list')
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \
api.keystone.tenant_list(IsA(http.HttpRequest)) \
.AndReturn(self.tenants.list())
self.mox.ReplayAll()
@ -45,7 +45,7 @@ class TenantsViewTests(test.BaseAdminViewTests):
class CreateProjectWorkflowTests(test.BaseAdminViewTests):
def _get_project_info(self, project):
project_info = {"tenant_name": project.name,
project_info = {"name": project.name,
"description": project.description,
"enabled": project.enabled}
return project_info
@ -146,9 +146,9 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
ulist = workflow_data["role_" + role.id]
for user_id in ulist:
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id=user_id,
role_id=role.id)
project=self.tenant.id,
user=user_id,
role=role.id)
nova_updated_quota = dict([(key, quota_data[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
@ -276,9 +276,9 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
ulist = workflow_data["role_" + role.id]
for user_id in ulist:
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id=user_id,
role_id=role.id)
project=self.tenant.id,
user=user_id,
role=role.id)
nova_updated_quota = dict([(key, quota_data[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
@ -338,9 +338,9 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
ulist = workflow_data["role_" + role.id]
for user_id in ulist:
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id=user_id,
role_id=role.id) \
project=self.tenant.id,
user=user_id,
role=role.id) \
.AndRaise(self.exceptions.keystone)
break
break
@ -506,8 +506,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
quota.metadata_items = 444
quota.volumes = 444
updated_project = {"tenant_name": project._info["name"],
"tenant_id": project.id,
updated_project = {"name": project._info["name"],
"description": project._info["description"],
"enabled": project.enabled}
updated_quota = self._get_quota_info(quota)
@ -516,12 +515,14 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
api.keystone.role_list(IsA(http.HttpRequest)).AndReturn(roles)
# handle
api.keystone.tenant_update(IsA(http.HttpRequest), **updated_project) \
api.keystone.tenant_update(IsA(http.HttpRequest),
project.id,
**updated_project) \
.AndReturn(project)
api.keystone.role_list(IsA(http.HttpRequest)).AndReturn(roles)
api.keystone.user_list(IsA(http.HttpRequest),
tenant_id=self.tenant.id).AndReturn(users)
project=self.tenant.id).AndReturn(users)
# admin user - try to remove all roles on current project, warning
api.keystone.roles_for_user(IsA(http.HttpRequest), '1',
@ -534,14 +535,14 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
.AndReturn((roles[0],))
# remove role 1
api.keystone.remove_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id='2',
role_id='1')
project=self.tenant.id,
user='2',
role='1')
# add role 2
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id='2',
role_id='2')
project=self.tenant.id,
user='2',
role='2')
# member user 3 - has role 2
api.keystone.roles_for_user(IsA(http.HttpRequest), '3',
@ -549,14 +550,14 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
.AndReturn((roles[1],))
# remove role 2
api.keystone.remove_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id='3',
role_id='2')
project=self.tenant.id,
user='3',
role='2')
# add role 1
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id='3',
role_id='1')
project=self.tenant.id,
user='3',
role='1')
nova_updated_quota = dict([(key, updated_quota[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
@ -647,8 +648,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
quota.metadata_items = 444
quota.volumes = 444
updated_project = {"tenant_name": project._info["name"],
"tenant_id": project.id,
updated_project = {"name": project._info["name"],
"description": project._info["description"],
"enabled": project.enabled}
updated_quota = self._get_quota_info(quota)
@ -657,7 +657,9 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
api.keystone.role_list(IsA(http.HttpRequest)).AndReturn(roles)
# handle
api.keystone.tenant_update(IsA(http.HttpRequest), **updated_project) \
api.keystone.tenant_update(IsA(http.HttpRequest),
project.id,
**updated_project) \
.AndRaise(self.exceptions.keystone)
self.mox.ReplayAll()
@ -722,8 +724,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
quota[0].limit = 444
quota[1].limit = -1
updated_project = {"tenant_name": project._info["name"],
"tenant_id": project.id,
updated_project = {"name": project._info["name"],
"description": project._info["description"],
"enabled": project.enabled}
updated_quota = self._get_quota_info(quota)
@ -733,12 +734,14 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
# handle
# handle
api.keystone.tenant_update(IsA(http.HttpRequest), **updated_project) \
api.keystone.tenant_update(IsA(http.HttpRequest),
project.id,
**updated_project) \
.AndReturn(project)
api.keystone.role_list(IsA(http.HttpRequest)).AndReturn(roles)
api.keystone.user_list(IsA(http.HttpRequest),
tenant_id=self.tenant.id).AndReturn(users)
project=self.tenant.id).AndReturn(users)
# admin user - try to remove all roles on current project, warning
api.keystone.roles_for_user(IsA(http.HttpRequest), '1',
@ -756,9 +759,9 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
.AndReturn((roles[0],))
# add role 2
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id='3',
role_id='2')
project=self.tenant.id,
user='3',
role='2')
nova_updated_quota = dict([(key, updated_quota[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
@ -827,8 +830,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
quota.metadata_items = 444
quota.volumes = 444
updated_project = {"tenant_name": project._info["name"],
"tenant_id": project.id,
updated_project = {"name": project._info["name"],
"description": project._info["description"],
"enabled": project.enabled}
updated_quota = self._get_quota_info(quota)
@ -837,41 +839,40 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
api.keystone.role_list(IsA(http.HttpRequest)).AndReturn(roles)
# handle
api.keystone.tenant_update(IsA(http.HttpRequest), **updated_project) \
api.keystone.tenant_update(IsA(http.HttpRequest),
project.id,
**updated_project) \
.AndReturn(project)
api.keystone.role_list(IsA(http.HttpRequest)).AndReturn(roles)
api.keystone.user_list(IsA(http.HttpRequest),
tenant_id=self.tenant.id).AndReturn(users)
project=self.tenant.id).AndReturn(users)
# admin user - try to remove all roles on current project, warning
api.keystone.roles_for_user(IsA(http.HttpRequest), '1',
self.tenant.id) \
.AndReturn(roles)
self.tenant.id).AndReturn(roles)
# member user 1 - has role 1, will remove it
api.keystone.roles_for_user(IsA(http.HttpRequest), '2',
self.tenant.id) \
.AndReturn((roles[1],))
self.tenant.id).AndReturn((roles[1],))
# member user 3 - has role 2
api.keystone.roles_for_user(IsA(http.HttpRequest), '3',
self.tenant.id) \
.AndReturn((roles[0],))
self.tenant.id).AndReturn((roles[0],))
# add role 2
api.keystone.add_tenant_user_role(IsA(http.HttpRequest),
tenant_id=self.tenant.id,
user_id='3',
role_id='2')\
.AndRaise(self.exceptions.nova)
project=self.tenant.id,
user='3',
role='2')\
.AndRaise(self.exceptions.keystone)
self.mox.ReplayAll()
# submit form data
project_data = {"name": project._info["name"],
"id": project.id,
"description": project._info["description"],
"enabled": project.enabled}
"id": project.id,
"description": project._info["description"],
"enabled": project.enabled}
workflow_data.update(project_data)
workflow_data.update(updated_quota)
url = reverse('horizon:admin:projects:update',

View File

@ -71,7 +71,7 @@ class IndexView(tables.DataTableView):
def get_data(self):
tenants = []
try:
tenants = api.keystone.tenant_list(self.request, admin=True)
tenants = api.keystone.tenant_list(self.request)
except:
exceptions.handle(self.request,
_("Unable to retrieve project list."))
@ -183,7 +183,7 @@ class CreateUserView(CreateView):
def get_initial(self):
default_role = api.keystone.get_default_role(self.request)
return {'role_id': getattr(default_role, "id", None),
'tenant_id': self.kwargs['tenant_id']}
'project': self.kwargs['tenant_id']}
def get_context_data(self, **kwargs):
context = super(CreateUserView, self).get_context_data(**kwargs)

View File

@ -218,7 +218,7 @@ class CreateProject(workflows.Workflow):
try:
desc = data['description']
self.object = api.keystone.tenant_create(request,
tenant_name=data['name'],
name=data['name'],
description=desc,
enabled=data['enabled'])
except:
@ -242,9 +242,9 @@ class CreateProject(workflows.Workflow):
users_added = 0
for user in role_list:
api.keystone.add_tenant_user_role(request,
tenant_id=project_id,
user_id=user,
role_id=role.id)
project=project_id,
user=user,
role=role.id)
users_added += 1
users_to_add -= users_added
except:
@ -300,12 +300,16 @@ class UpdateProject(workflows.Workflow):
return message % self.context.get('name', 'unknown project')
def handle(self, request, data):
# FIXME(gabriel): This should be refactored to use Python's built-in
# sets and do this all in a single "roles to add" and "roles to remove"
# pass instead of the multi-pass thing happening now.
project_id = data['project_id']
# update project info
try:
api.keystone.tenant_update(request,
tenant_id=project_id,
tenant_name=data['name'],
project_id,
name=data['name'],
description=data['description'],
enabled=data['enabled'])
except:
@ -315,63 +319,80 @@ class UpdateProject(workflows.Workflow):
# update project members
users_to_modify = 0
try:
# Get our role options
available_roles = api.keystone.role_list(request)
# Get the users currently associated with this project so we
# can diff against it.
project_members = api.keystone.user_list(request,
tenant_id=project_id)
project=project_id)
users_to_modify = len(project_members)
for user in project_members:
current_roles = [role for role in
api.keystone.roles_for_user(self.request,
user.id,
project_id)]
effective_roles = []
# Check if there have been any changes in the roles of
# Existing project members.
current_roles = api.keystone.roles_for_user(self.request,
user.id,
project_id)
current_role_ids = [role.id for role in current_roles]
for role in available_roles:
role_list = data["role_" + role.id]
if user.id in role_list:
effective_roles.append(role)
if role not in current_roles:
# Check if the user is in the list of users with this role.
if user.id in data["role_" + role.id]:
# Add it if necessary
if role.id not in current_role_ids:
# user role has changed
api.keystone.add_tenant_user_role(
request,
tenant_id=project_id,
user_id=user.id,
role_id=role.id)
project=project_id,
user=user.id,
role=role.id)
else:
# user role is unchanged
current_roles.pop(current_roles.index(role))
if user.id == request.user.id and \
project_id == request.user.tenant_id and \
any(x.name == 'admin' for x in current_roles):
# Cannot remove "admin" role on current(admin) project
msg = _('You cannot remove the "admin" role from the '
'project you are currently logged into. Please '
'switch to another project with admin permissions '
'or remove the role manually via the CLI')
messages.warning(request, msg)
# User role is unchanged, so remove it from the
# remaining roles list to avoid removing it later.
index = current_role_ids.index(role.id)
current_role_ids.pop(index)
# Prevent admins from doing stupid things to themselves.
is_current_user = user.id == request.user.id
is_current_project = project_id == request.user.tenant_id
admin_roles = [role for role in current_roles
if role.name.lower() == 'admin']
if len(admin_roles):
removing_admin = any([role.id in current_role_ids
for role in admin_roles])
else:
# delete user's removed roles
for to_delete in current_roles:
removing_admin = False
if is_current_user and is_current_project and removing_admin:
# Cannot remove "admin" role on current(admin) project
msg = _('You cannot revoke your administrative privileges '
'from the project you are currently logged into. '
'Please switch to another project with '
'administrative privileges or remove the '
'administrative role manually via the CLI.')
messages.warning(request, msg)
# Otherwise go through and revoke any removed roles.
else:
for id_to_delete in current_role_ids:
api.keystone.remove_tenant_user_role(
request,
tenant_id=project_id,
user_id=user.id,
role_id=to_delete.id)
project=project_id,
user=user.id,
role=id_to_delete)
users_to_modify -= 1
# add new roles to project
# Grant new roles on the project.
for role in available_roles:
# count how many users may be added for exception handling
role_list = data["role_" + role.id]
users_to_modify += len(role_list)
# Count how many users may be added for exception handling.
users_to_modify += len(data["role_" + role.id])
for role in available_roles:
role_list = data["role_" + role.id]
users_added = 0
for user_id in role_list:
for user_id in data["role_" + role.id]:
if not filter(lambda x: user_id == x.id, project_members):
api.keystone.add_tenant_user_role(request,
tenant_id=project_id,
user_id=user_id,
role_id=role.id)
project=project_id,
user=user_id,
role=role.id)
users_added += 1
users_to_modify -= users_added
except:

View File

@ -34,8 +34,7 @@ class RouterTests(test.BaseAdminViewTests, r_test.RouterTests):
api.quantum.router_list(
IsA(http.HttpRequest),
search_opts=None).AndReturn(self.routers.list())
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
.AndReturn(tenants)
api.keystone.tenant_list(IsA(http.HttpRequest)).AndReturn(tenants)
self._mock_external_network_list()
self.mox.ReplayAll()
@ -49,7 +48,6 @@ class RouterTests(test.BaseAdminViewTests, r_test.RouterTests):
@test.create_stubs({api.quantum: ('router_list',),
api.keystone: ('tenant_list',)})
def test_index_router_list_exception(self):
tenants = self.tenants.list()
api.quantum.router_list(
IsA(http.HttpRequest),
search_opts=None).AndRaise(self.exceptions.quantum)

View File

@ -21,7 +21,6 @@
import logging
from django.forms import ValidationError
from django.utils.encoding import force_unicode
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.debug import sensitive_variables
@ -31,7 +30,6 @@ from horizon import messages
from horizon.utils import validators
from openstack_dashboard import api
from django.contrib.auth import logout
LOG = logging.getLogger(__name__)
@ -40,13 +38,13 @@ LOG = logging.getLogger(__name__)
class BaseUserForm(forms.SelfHandlingForm):
def __init__(self, request, *args, **kwargs):
super(BaseUserForm, self).__init__(request, *args, **kwargs)
# Populate tenant choices
tenant_choices = [('', _("Select a project"))]
# Populate project choices
project_choices = [('', _("Select a project"))]
for tenant in api.keystone.tenant_list(request, admin=True):
if tenant.enabled:
tenant_choices.append((tenant.id, tenant.name))
self.fields['tenant_id'].choices = tenant_choices
for project in api.keystone.tenant_list(request):
if project.enabled:
project_choices.append((project.id, project.name))
self.fields['project'].choices = project_choices
def clean(self):
'''Check to make sure password fields match.'''
@ -64,16 +62,16 @@ class CreateUserForm(BaseUserForm):
name = forms.CharField(label=_("User Name"))
email = forms.EmailField(label=_("Email"))
password = forms.RegexField(
label=_("Password"),
widget=forms.PasswordInput(render_value=False),
regex=validators.password_validator(),
error_messages={'invalid': validators.password_validator_msg()})
label=_("Password"),
widget=forms.PasswordInput(render_value=False),
regex=validators.password_validator(),
error_messages={'invalid': validators.password_validator_msg()})
confirm_password = forms.CharField(
label=_("Confirm Password"),
required=False,
widget=forms.PasswordInput(render_value=False))
tenant_id = forms.DynamicChoiceField(label=_("Primary Project"),
add_item_link=ADD_PROJECT_URL)
label=_("Confirm Password"),
required=False,
widget=forms.PasswordInput(render_value=False))
project = forms.DynamicChoiceField(label=_("Primary Project"),
add_item_link=ADD_PROJECT_URL)
role_id = forms.ChoiceField(label=_("Role"))
def __init__(self, *args, **kwargs):
@ -92,7 +90,7 @@ class CreateUserForm(BaseUserForm):
data['name'],
data['email'],
data['password'],
data['tenant_id'],
data['project'],
True)
messages.success(request,
_('User "%s" was successfully created.')
@ -100,9 +98,9 @@ class CreateUserForm(BaseUserForm):
if data['role_id']:
try:
api.keystone.add_tenant_user_role(request,
data['tenant_id'],
new_user.id,
data['role_id'])
data['project'],
new_user.id,
data['role_id'])
except:
exceptions.handle(request,
_('Unable to add user'
@ -116,17 +114,17 @@ class UpdateUserForm(BaseUserForm):
id = forms.CharField(label=_("ID"), widget=forms.HiddenInput)
name = forms.CharField(label=_("User Name"))
email = forms.EmailField(label=_("Email"))
password = forms.RegexField(label=_("Password"),
widget=forms.PasswordInput(render_value=False),
regex=validators.password_validator(),
required=False,
error_messages={'invalid':
validators.password_validator_msg()})
password = forms.RegexField(
label=_("Password"),
widget=forms.PasswordInput(render_value=False),
regex=validators.password_validator(),
required=False,
error_messages={'invalid': validators.password_validator_msg()})
confirm_password = forms.CharField(
label=_("Confirm Password"),
widget=forms.PasswordInput(render_value=False),
required=False)
tenant_id = forms.ChoiceField(label=_("Primary Project"))
label=_("Confirm Password"),
widget=forms.PasswordInput(render_value=False),
required=False)
project = forms.ChoiceField(label=_("Primary Project"))
def __init__(self, request, *args, **kwargs):
super(UpdateUserForm, self).__init__(request, *args, **kwargs)
@ -139,62 +137,16 @@ class UpdateUserForm(BaseUserForm):
# password and confirm_password strings.
@sensitive_variables('data', 'password')
def handle(self, request, data):
failed, succeeded = [], []
user_is_editable = api.keystone.keystone_can_edit_user()
user = data.pop('id')
tenant = data.pop('tenant_id')
if user_is_editable:
password = data.pop('password')
data.pop('confirm_password', None)
# Throw away the password confirmation, we're done with it.
data.pop('confirm_password', None)
if user_is_editable:
# Update user details
msg_bits = (_('name'), _('email'))
try:
api.keystone.user_update(request, user, **data)
succeeded.extend(msg_bits)
except:
failed.extend(msg_bits)
exceptions.handle(request, ignore=True)
# Update default tenant
msg_bits = (_('primary project'),)
try:
api.keystone.user_update_tenant(request, user, tenant)
succeeded.extend(msg_bits)
api.keystone.user_update(request, user, **data)
messages.success(request,
_('User has been updated successfully.'))
except:
failed.append(msg_bits)
exceptions.handle(request, ignore=True)
# Check for existing roles
# Show a warning if no role exists for the tenant
user_roles = api.keystone.roles_for_user(request, user, tenant)
if not user_roles:
messages.warning(request,
_('The user %s has no role defined for' +
' that project.')
% data.get('name', None))
if user_is_editable:
# If present, update password
# FIXME(gabriel): password change should be its own form and view
if password:
msg_bits = (_('password'),)
try:
api.keystone.user_update_password(request, user, password)
succeeded.extend(msg_bits)
if user == request.user.id:
logout(request)
except:
failed.extend(msg_bits)
exceptions.handle(request, ignore=True)
if succeeded:
messages.success(request, _('User has been updated successfully.'))
if failed:
failed = map(force_unicode, failed)
messages.error(request,
_('Unable to update %(attributes)s for the user.')
% {"attributes": ", ".join(failed)})
messages.error(request, _('Unable to update the user.'))
return True

View File

@ -55,8 +55,7 @@ class UsersViewTests(test.BaseAdminViewTests):
user = self.users.get(id="1")
role = self.roles.first()
api.keystone.tenant_list(IgnoreArg(), admin=True) \
.AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
api.keystone.user_create(IgnoreArg(),
user.name,
user.email,
@ -74,7 +73,7 @@ class UsersViewTests(test.BaseAdminViewTests):
'name': user.name,
'email': user.email,
'password': user.password,
'tenant_id': self.tenant.id,
'project': self.tenant.id,
'role_id': self.roles.first().id,
'confirm_password': user.password}
res = self.client.post(USER_CREATE_URL, formData)
@ -88,8 +87,7 @@ class UsersViewTests(test.BaseAdminViewTests):
def test_create_with_password_mismatch(self):
user = self.users.get(id="1")
api.keystone.tenant_list(IgnoreArg(), admin=True) \
.AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
api.keystone.get_default_role(IgnoreArg()) \
.AndReturn(self.roles.first())
@ -100,7 +98,7 @@ class UsersViewTests(test.BaseAdminViewTests):
'name': user.name,
'email': user.email,
'password': user.password,
'tenant_id': self.tenant.id,
'project': self.tenant.id,
'role_id': self.roles.first().id,
'confirm_password': "doesntmatch"}
@ -114,8 +112,7 @@ class UsersViewTests(test.BaseAdminViewTests):
def test_create_validation_for_password_too_short(self):
user = self.users.get(id="1")
api.keystone.tenant_list(IgnoreArg(), admin=True) \
.AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
api.keystone.get_default_role(IgnoreArg()) \
.AndReturn(self.roles.first())
@ -127,7 +124,7 @@ class UsersViewTests(test.BaseAdminViewTests):
'name': user.name,
'email': user.email,
'password': 'four',
'tenant_id': self.tenant.id,
'project': self.tenant.id,
'role_id': self.roles.first().id,
'confirm_password': 'four'}
@ -143,8 +140,7 @@ class UsersViewTests(test.BaseAdminViewTests):
def test_create_validation_for_password_too_long(self):
user = self.users.get(id="1")
api.keystone.tenant_list(IgnoreArg(), admin=True) \
.AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
api.keystone.get_default_role(IgnoreArg()) \
.AndReturn(self.roles.first())
@ -156,7 +152,7 @@ class UsersViewTests(test.BaseAdminViewTests):
'name': user.name,
'email': user.email,
'password': 'MoreThanEighteenChars',
'tenant_id': self.tenant.id,
'project': self.tenant.id,
'role_id': self.roles.first().id,
'confirm_password': 'MoreThanEighteenChars'}
@ -174,24 +170,17 @@ class UsersViewTests(test.BaseAdminViewTests):
'roles_for_user', )})
def test_update(self):
user = self.users.get(id="1")
test_password = 'normalpwd'
api.keystone.user_get(IsA(http.HttpRequest), '1',
admin=True).AndReturn(user)
api.keystone.tenant_list(IgnoreArg(),
admin=True).AndReturn(self.tenants.list())
admin=True).AndReturn(user)
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
api.keystone.user_update(IsA(http.HttpRequest),
user.id,
email=u'test@example.com',
name=u'test_user').AndReturn(None)
api.keystone.user_update_tenant(IsA(http.HttpRequest),
user.id,
self.tenant.id).AndReturn(None)
api.keystone.roles_for_user(IsA(http.HttpRequest),
user.id,
self.tenant.id).AndReturn(None)
api.keystone.user_update_password(IsA(http.HttpRequest),
user.id,
IgnoreArg()).AndReturn(None)
name=u'test_user',
password=test_password,
project=self.tenant.id).AndReturn(None)
self.mox.ReplayAll()
@ -199,14 +188,13 @@ class UsersViewTests(test.BaseAdminViewTests):
'id': user.id,
'name': user.name,
'email': user.email,
'password': 'normalpwd',
'tenant_id': self.tenant.id,
'confirm_password': 'normalpwd'}
'password': test_password,
'project': self.tenant.id,
'confirm_password': test_password}
res = self.client.post(USER_UPDATE_URL, formData)
self.assertNoFormErrors(res)
self.assertMessageCount(warning=1)
@test.create_stubs({api.keystone: ('user_get',
'tenant_list',
@ -219,28 +207,21 @@ class UsersViewTests(test.BaseAdminViewTests):
api.keystone.user_get(IsA(http.HttpRequest),
'1',
admin=True).AndReturn(user)
api.keystone.tenant_list(IgnoreArg(), admin=True) \
.AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
api.keystone.keystone_can_edit_user().AndReturn(False)
api.keystone.keystone_can_edit_user().AndReturn(False)
api.keystone.user_update_tenant(IsA(http.HttpRequest),
user.id,
self.tenant.id).AndReturn(None)
api.keystone.roles_for_user(IsA(http.HttpRequest),
user.id,
self.tenant.id).AndReturn(None)
self.mox.ReplayAll()
formData = {'method': 'UpdateUserForm',
'id': user.id,
'name': user.name,
'tenant_id': self.tenant.id, }
'project': self.tenant.id, }
res = self.client.post(USER_UPDATE_URL, formData)
self.assertNoFormErrors(res)
self.assertMessageCount(warning=1)
self.assertMessageCount(error=1)
@test.create_stubs({api.keystone: ('user_get', 'tenant_list')})
def test_update_validation_for_password_too_short(self):
@ -248,8 +229,7 @@ class UsersViewTests(test.BaseAdminViewTests):
api.keystone.user_get(IsA(http.HttpRequest), '1',
admin=True).AndReturn(user)
api.keystone.tenant_list(IgnoreArg(),
admin=True).AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
self.mox.ReplayAll()
@ -258,7 +238,7 @@ class UsersViewTests(test.BaseAdminViewTests):
'name': user.name,
'email': user.email,
'password': 't',
'tenant_id': self.tenant.id,
'project': self.tenant.id,
'confirm_password': 't'}
res = self.client.post(USER_UPDATE_URL, formData)
@ -273,8 +253,7 @@ class UsersViewTests(test.BaseAdminViewTests):
api.keystone.user_get(IsA(http.HttpRequest), '1',
admin=True).AndReturn(user)
api.keystone.tenant_list(IgnoreArg(),
admin=True).AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
self.mox.ReplayAll()
@ -283,7 +262,7 @@ class UsersViewTests(test.BaseAdminViewTests):
'name': user.name,
'email': user.email,
'password': 'ThisIsASuperLongPassword',
'tenant_id': self.tenant.id,
'project': self.tenant.id,
'confirm_password': 'ThisIsASuperLongPassword'}
res = self.client.post(USER_UPDATE_URL, formData)
@ -374,8 +353,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
'role_list',
'user_list')})
def test_modal_create_user_with_passwords_not_matching(self):
api.keystone.tenant_list(IgnoreArg(), admin=True) \
.AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
api.keystone.user_list(IgnoreArg()).AndReturn(self.users.list())
api.keystone.get_default_role(IgnoreArg()) \
@ -406,8 +384,7 @@ class SeleniumTests(test.SeleniumAdminTestCase):
def test_update_user_with_passwords_not_matching(self):
api.keystone.user_get(IsA(http.HttpRequest), '1',
admin=True).AndReturn(self.user)
api.keystone.tenant_list(IgnoreArg(), admin=True) \
.AndReturn(self.tenants.list())
api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list())
self.mox.ReplayAll()
self.selenium.get("%s%s" % (self.live_server_url, USER_UPDATE_URL))

View File

@ -80,7 +80,7 @@ class UpdateView(forms.ModalFormView):
user = self.get_object()
return {'id': user.id,
'name': user.name,
'tenant_id': getattr(user, 'tenantId', None),
'project': user.project_id,
'email': user.email}

View File

@ -36,8 +36,8 @@ class VolumeTests(test.BaseAdminViewTests):
AndReturn([self.servers.list(), False])
cinder.volume_type_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_types.list())
keystone.tenant_list(IsA(http.HttpRequest),
admin=True).AndReturn(self.tenants.list())
keystone.tenant_list(IsA(http.HttpRequest)) \
.AndReturn(self.tenants.list())
self.mox.ReplayAll()
@ -80,8 +80,8 @@ class VolumeTests(test.BaseAdminViewTests):
AndReturn(self.volume_types.list())
cinder.volume_type_delete(IsA(http.HttpRequest),
str(volume_type.id))
keystone.tenant_list(IsA(http.HttpRequest),
admin=True).AndReturn(self.tenants.list())
keystone.tenant_list(IsA(http.HttpRequest)) \
.AndReturn(self.tenants.list())
self.mox.ReplayAll()
res = self.client.post(reverse('horizon:admin:volumes:index'),

View File

@ -45,7 +45,7 @@ class IndexView(tables.MultiTableView, VolumeTableMixIn):
# Gather our tenants to correlate against IDs
try:
tenants = keystone.tenant_list(self.request, admin=True)
tenants = keystone.tenant_list(self.request)
except:
tenants = []
msg = _('Unable to retrieve volume tenant information.')

View File

@ -26,6 +26,15 @@ TEMPLATE_DEBUG = DEBUG
#CSRF_COOKIE_SECURE = True
#SESSION_COOKIE_SECURE = True
# Overrides for OpenStack API versions. Use this setting to force the
# OpenStack dashboard to use a specfic API version for a given service API.
# NOTE: The version should be formatted as it appears in the URL for the
# service API. For example, The identity service APIs have inconsistent
# use of the decimal point, so valid options would be "2.0" or "3".
# OPENSTACK_API_VERSIONS = {
# "identity": 3
# }
# Default OpenStack Dashboard configuration.
HORIZON_CONFIG = {
'dashboards': ('project', 'admin', 'settings',),

View File

@ -66,9 +66,11 @@ class RoleAPITests(test.APITestCase):
keystoneclient.roles.roles_for_user(self.user.id,
tenant.id).AndReturn(self.roles)
for role in self.roles:
keystoneclient.roles.remove_user_role(self.user.id,
role.id,
tenant.id)
keystoneclient.roles.revoke(role.id,
domain=None,
group=None,
project=tenant.id,
user=self.user.id)
self.mox.ReplayAll()
api.keystone.remove_tenant_user(self.request, tenant.id, self.user.id)

View File

@ -108,6 +108,7 @@ def data(TEST):
'email': 'test@example.com',
'password': 'password',
'token': 'test_token',
'project_id': '1',
'enabled': True}
user = users.User(users.UserManager(None), user_dict)
user_dict = {'id': "2",
@ -115,6 +116,7 @@ def data(TEST):
'email': 'two@example.com',
'password': 'password',
'token': 'test_token',
'project_id': '1',
'enabled': True}
user2 = users.User(users.UserManager(None), user_dict)
user_dict = {'id': "3",
@ -122,6 +124,7 @@ def data(TEST):
'email': 'three@example.com',
'password': 'password',
'token': 'test_token',
'project_id': '1',
'enabled': True}
user3 = users.User(users.UserManager(None), user_dict)
TEST.users.add(user, user2, user3)