Refinements for Signup
Reworking NewProject as a standalone and making NewProjectWithUser for use with signup style tasks. NewProject and NewProjectWithUser now create the project and user at post_approve and then resets the user password at submit. - This change allows signup tokens to expire and a new signup to use the reset feature to still get access. The process still appears exactly the same to the end user. - Existing users creating a new project will also get created at post_approve step, but as they needed no token this functionality does not change from an outside perspective. Fixing a project creation issue with keystone V3, wasn't setting domain. More standardisation in action handling functions. Duplicate error now returns 409 rather than 400 for clarity. Adding an "approved_by" values to tasks both for auditing and for possible future logic checks. Reworking of Network resource creation into two variant actions. Reworking AddAdminToProject to be more generic and allow a list of users. Fixing issues with logic for task approval and task updating. Change-Id: Ieba9907e5632dd441a86c41de291c6a7d0c8764a
This commit is contained in:
parent
e5084a84ed
commit
57b54baabe
@ -50,7 +50,7 @@ KEYSTONE:
|
|||||||
project_name: admin
|
project_name: admin
|
||||||
# MUST BE V3 API:
|
# MUST BE V3 API:
|
||||||
auth_url: http://localhost:5000/v3
|
auth_url: http://localhost:5000/v3
|
||||||
DEFAULT_REGION: RegionOne
|
domain_id: default
|
||||||
|
|
||||||
TOKEN_SUBMISSION_URL: http://192.168.122.160:8080/token/
|
TOKEN_SUBMISSION_URL: http://192.168.122.160:8080/token/
|
||||||
|
|
||||||
@ -129,8 +129,8 @@ TASK_SETTINGS:
|
|||||||
# Additonal actions for views
|
# Additonal actions for views
|
||||||
# These will run after the default actions, in the given order.
|
# These will run after the default actions, in the given order.
|
||||||
additional_actions:
|
additional_actions:
|
||||||
- AddAdminToProject
|
- AddDefaultUsersToProject
|
||||||
- DefaultProjectResources
|
- NewProjectDefaultNetwork
|
||||||
notifications:
|
notifications:
|
||||||
standard:
|
standard:
|
||||||
EmailNotification:
|
EmailNotification:
|
||||||
@ -144,6 +144,10 @@ TASK_SETTINGS:
|
|||||||
- signups@example.com
|
- signups@example.com
|
||||||
RTNotification:
|
RTNotification:
|
||||||
queue: signups
|
queue: signups
|
||||||
|
default_region: RegionOne
|
||||||
|
# If 'None' (null in yaml), will default to domain as parent.
|
||||||
|
# If domain isn't set explicity, will service user domain (see KEYSTONE).
|
||||||
|
default_parent_id: null
|
||||||
invite_user:
|
invite_user:
|
||||||
emails:
|
emails:
|
||||||
# To not send this email, set the value to null
|
# To not send this email, set the value to null
|
||||||
@ -187,6 +191,12 @@ TASK_SETTINGS:
|
|||||||
|
|
||||||
# Action settings:
|
# Action settings:
|
||||||
ACTION_SETTINGS:
|
ACTION_SETTINGS:
|
||||||
|
NewProject:
|
||||||
|
default_roles:
|
||||||
|
- project_admin
|
||||||
|
- project_mod
|
||||||
|
- heat_stack_owner
|
||||||
|
- _member_
|
||||||
NewUser:
|
NewUser:
|
||||||
allowed_roles:
|
allowed_roles:
|
||||||
- project_admin
|
- project_admin
|
||||||
@ -196,16 +206,21 @@ ACTION_SETTINGS:
|
|||||||
ResetUser:
|
ResetUser:
|
||||||
blacklisted_roles:
|
blacklisted_roles:
|
||||||
- admin
|
- admin
|
||||||
DefaultProjectResources:
|
NewDefaultNetwork:
|
||||||
RegionOne:
|
RegionOne:
|
||||||
network_name: somenetwork
|
network_name: default_network
|
||||||
subnet_name: somesubnet
|
subnet_name: default_subnet
|
||||||
router_name: somerouter
|
router_name: default_router
|
||||||
public_network: 3cb50d61-5bce-4c03-96e6-8e262e12bb35
|
public_network: 3cb50d61-5bce-4c03-96e6-8e262e12bb35
|
||||||
DNS_NAMESERVERS:
|
DNS_NAMESERVERS:
|
||||||
- 193.168.1.2
|
- 193.168.1.2
|
||||||
- 193.168.1.3
|
- 193.168.1.3
|
||||||
SUBNET_CIDR: 192.168.1.0/24
|
SUBNET_CIDR: 192.168.1.0/24
|
||||||
|
AddDefaultUsersToProject:
|
||||||
|
default_users:
|
||||||
|
- admin
|
||||||
|
default_roles:
|
||||||
|
- admin
|
||||||
|
|
||||||
# mapping between roles and managable roles
|
# mapping between roles and managable roles
|
||||||
ROLES_MAPPING:
|
ROLES_MAPPING:
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@ -379,35 +380,22 @@ class NewUser(UserNameAction):
|
|||||||
% (self.username, self.roles, self.project_id))
|
% (self.username, self.roles, self.project_id))
|
||||||
|
|
||||||
|
|
||||||
class NewProject(UserNameAction):
|
class ProjectCreateBase(object):
|
||||||
"""
|
"""Mixin with functions for project creation."""
|
||||||
Similar functionality as the NewUser action,
|
|
||||||
but will create the project if valid. Will setup
|
|
||||||
the user (existing or new) with the 'default_role'.
|
|
||||||
"""
|
|
||||||
|
|
||||||
required = [
|
def _validate_parent_project(self):
|
||||||
'project_name',
|
# NOTE(adriant): If parent id is None, Keystone defaults to the domain.
|
||||||
'username',
|
# So we only care to validate if parent_id is not None.
|
||||||
'email'
|
if self.parent_id:
|
||||||
]
|
parent = self.id_manager.get_project(self.parent_id)
|
||||||
|
if not parent:
|
||||||
# NOTE(adriant): move these to a config somewhere?
|
self.add_note("Parent id: '%s' does not exist." %
|
||||||
default_roles = {
|
self.project_name)
|
||||||
"project_admin", "project_mod", "_member_", "heat_stack_owner"
|
return False
|
||||||
}
|
return True
|
||||||
|
|
||||||
def _validate(self):
|
|
||||||
project_valid = self._validate_project()
|
|
||||||
user_valid = self._validate_user()
|
|
||||||
|
|
||||||
self.action.valid = project_valid and user_valid
|
|
||||||
self.action.save()
|
|
||||||
|
|
||||||
def _validate_project(self):
|
def _validate_project(self):
|
||||||
id_manager = user_store.IdentityManager()
|
project = self.id_manager.find_project(self.project_name)
|
||||||
|
|
||||||
project = id_manager.find_project(self.project_name)
|
|
||||||
if project:
|
if project:
|
||||||
self.add_note("Existing project with name '%s'." %
|
self.add_note("Existing project with name '%s'." %
|
||||||
self.project_name)
|
self.project_name)
|
||||||
@ -417,9 +405,148 @@ class NewProject(UserNameAction):
|
|||||||
self.project_name)
|
self.project_name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _create_project(self):
|
||||||
|
try:
|
||||||
|
project = self.id_manager.create_project(
|
||||||
|
self.project_name, created_on=str(timezone.now()),
|
||||||
|
parent=self.parent_id)
|
||||||
|
except Exception as e:
|
||||||
|
self.add_note(
|
||||||
|
"Error: '%s' while creating project: %s" %
|
||||||
|
(e, self.project_name))
|
||||||
|
raise
|
||||||
|
# put project_id into action cache:
|
||||||
|
self.action.task.cache['project_id'] = project.id
|
||||||
|
self.set_cache('project_id', project.id)
|
||||||
|
self.add_note("New project '%s' created." % project.name)
|
||||||
|
|
||||||
|
def _grant_roles(self, user, roles, project_id):
|
||||||
|
ks_roles = []
|
||||||
|
for role in roles:
|
||||||
|
ks_role = self.id_manager.find_role(role)
|
||||||
|
if ks_role:
|
||||||
|
ks_roles.append(ks_role)
|
||||||
|
else:
|
||||||
|
raise TypeError("Keystone missing role: %s" % role)
|
||||||
|
|
||||||
|
for role in ks_roles:
|
||||||
|
self.id_manager.add_user_role(user, role, project_id)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(adriant): Write tests for this action.
|
||||||
|
class NewProject(BaseAction, ProjectCreateBase):
|
||||||
|
"""
|
||||||
|
Creates a new project for the current keystone_user.
|
||||||
|
|
||||||
|
This action can only be used for an autheticated taskview.
|
||||||
|
"""
|
||||||
|
|
||||||
|
required = [
|
||||||
|
'parent_id',
|
||||||
|
'project_name',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(NewProject, self).__init__(*args, **kwargs)
|
||||||
|
self.id_manager = user_store.IdentityManager()
|
||||||
|
|
||||||
|
def _validate(self):
|
||||||
|
valid_parent = self._validate_parent_project()
|
||||||
|
valid_project = self._validate_project()
|
||||||
|
self.action.valid = valid_project and valid_parent
|
||||||
|
self.action.save()
|
||||||
|
|
||||||
|
def _validate_parent_project(self):
|
||||||
|
if self.parent_id:
|
||||||
|
keystone_user = self.action.task.keystone_user
|
||||||
|
|
||||||
|
if self.parent_id != keystone_user['project_id']:
|
||||||
|
self.add_note(
|
||||||
|
'Parent id does not match keystone user project.')
|
||||||
|
return False
|
||||||
|
return super(NewProject, self)._validate_parent_project()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _pre_approve(self):
|
||||||
|
self._validate()
|
||||||
|
|
||||||
|
def _post_approve(self):
|
||||||
|
project_id = self.get_cache('project_id')
|
||||||
|
if project_id:
|
||||||
|
self.action.task.cache['project_id'] = project_id
|
||||||
|
self.add_note("Project already created.")
|
||||||
|
else:
|
||||||
|
self._validate()
|
||||||
|
|
||||||
|
if not self.valid:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._create_project()
|
||||||
|
|
||||||
|
user_id = self.get_cache('user_id')
|
||||||
|
if user_id:
|
||||||
|
self.action.task.cache['user_id'] = user_id
|
||||||
|
self.add_note("User already given roles.")
|
||||||
|
else:
|
||||||
|
default_roles = settings.ACTION_SETTINGS.get(
|
||||||
|
'NewProject', {}).get("default_roles", {})
|
||||||
|
|
||||||
|
project_id = self.get_cache('project_id')
|
||||||
|
keystone_user = self.action.task.keystone_user
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = self.id_manager.get_user(keystone_user['user_id'])
|
||||||
|
|
||||||
|
self._grant_roles(user, default_roles, project_id)
|
||||||
|
except Exception as e:
|
||||||
|
self.add_note(
|
||||||
|
("Error: '%s' while adding roles %s "
|
||||||
|
"to user '%s' on project '%s'") %
|
||||||
|
(e, self.username, default_roles, project_id))
|
||||||
|
raise
|
||||||
|
|
||||||
|
# put user_id into action cache:
|
||||||
|
self.action.task.cache['user_id'] = user.id
|
||||||
|
self.set_cache('user_id', user.id)
|
||||||
|
self.add_note(("Existing user '%s' attached to project %s" +
|
||||||
|
" with roles: %s")
|
||||||
|
% (self.username, project_id,
|
||||||
|
default_roles))
|
||||||
|
|
||||||
|
def _submit(self, token_data):
|
||||||
|
"""
|
||||||
|
Nothing to do here. Everything is done at post_approve.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewProjectWithUser(UserNameAction, ProjectCreateBase):
|
||||||
|
"""
|
||||||
|
Makes a new project for the given username. Will create the user if it
|
||||||
|
doesn't exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
required = [
|
||||||
|
'parent_id',
|
||||||
|
'project_name',
|
||||||
|
'username',
|
||||||
|
'email'
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(NewProjectWithUser, self).__init__(*args, **kwargs)
|
||||||
|
self.id_manager = user_store.IdentityManager()
|
||||||
|
|
||||||
|
def _validate(self):
|
||||||
|
valid_parent = self._validate_parent_project()
|
||||||
|
project_valid = self._validate_project()
|
||||||
|
user_valid = self._validate_user()
|
||||||
|
|
||||||
|
self.action.valid = valid_parent and project_valid and user_valid
|
||||||
|
self.action.save()
|
||||||
|
|
||||||
def _validate_user(self):
|
def _validate_user(self):
|
||||||
id_manager = user_store.IdentityManager()
|
user = self.id_manager.find_user(self.username)
|
||||||
user = id_manager.find_user(self.username)
|
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
if user.email == self.email:
|
if user.email == self.email:
|
||||||
@ -441,104 +568,135 @@ class NewProject(UserNameAction):
|
|||||||
|
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
|
def _validate_user_submit(self):
|
||||||
|
user_id = self.get_cache('user_id')
|
||||||
|
project_id = self.get_cache('project_id')
|
||||||
|
|
||||||
|
user = self.id_manager.get_user(user_id)
|
||||||
|
project = self.id_manager.get_project(project_id)
|
||||||
|
|
||||||
|
if user and project:
|
||||||
|
self.action.valid = True
|
||||||
|
else:
|
||||||
|
self.action.valid = False
|
||||||
|
|
||||||
|
self.action.save()
|
||||||
|
|
||||||
def _pre_approve(self):
|
def _pre_approve(self):
|
||||||
self._validate()
|
self._validate()
|
||||||
|
|
||||||
def _post_approve(self):
|
def _post_approve(self):
|
||||||
"""
|
"""
|
||||||
Approving a registration means we set up the project itself,
|
Approving a new project means we set up the project itself,
|
||||||
and then the user registration token is valid for submission and
|
and if the user doesn't exist, create it right away. An existing
|
||||||
creating the user themselves.
|
user automatically gets added to the new project.
|
||||||
"""
|
"""
|
||||||
project_id = self.get_cache('project_id')
|
project_id = self.get_cache('project_id')
|
||||||
if project_id:
|
if project_id:
|
||||||
self.action.task.cache['project_id'] = project_id
|
self.action.task.cache['project_id'] = project_id
|
||||||
self.add_note("Project already created.")
|
self.add_note("Project already created.")
|
||||||
return
|
else:
|
||||||
|
self.action.valid = (
|
||||||
|
self._validate_project() and self._validate_parent_project())
|
||||||
|
self.action.save()
|
||||||
|
|
||||||
self._validate()
|
if not self.valid:
|
||||||
if not self.valid:
|
return
|
||||||
return
|
|
||||||
|
|
||||||
id_manager = user_store.IdentityManager()
|
self._create_project()
|
||||||
try:
|
|
||||||
project = id_manager.create_project(
|
user_id = self.get_cache('user_id')
|
||||||
self.project_name, created_on=str(timezone.now()))
|
if user_id:
|
||||||
except Exception as e:
|
self.action.task.cache['user_id'] = user_id
|
||||||
self.add_note(
|
self.add_note("User already created.")
|
||||||
"Error: '%s' while creating project: %s" %
|
else:
|
||||||
(e, self.project_name))
|
self.action.valid = self._validate_user()
|
||||||
raise
|
self.action.save()
|
||||||
# put project_id into action cache:
|
|
||||||
self.action.task.cache['project_id'] = project.id
|
if not self.valid:
|
||||||
self.set_cache('project_id', project.id)
|
return
|
||||||
self.add_note("New project '%s' created." % self.project_name)
|
|
||||||
|
default_roles = settings.ACTION_SETTINGS.get(
|
||||||
|
'NewProject', {}).get("default_roles", {})
|
||||||
|
|
||||||
|
project_id = self.get_cache('project_id')
|
||||||
|
|
||||||
|
if self.action.state == "default":
|
||||||
|
try:
|
||||||
|
# Generate a temporary password:
|
||||||
|
password = uuid4().hex + uuid4().hex
|
||||||
|
|
||||||
|
user = self.id_manager.create_user(
|
||||||
|
name=self.username, password=password,
|
||||||
|
email=self.email, project_id=project_id)
|
||||||
|
|
||||||
|
self._grant_roles(user, default_roles, project_id)
|
||||||
|
except Exception as e:
|
||||||
|
self.add_note(
|
||||||
|
"Error: '%s' while creating user: %s with roles: %s" %
|
||||||
|
(e, self.username, default_roles))
|
||||||
|
raise
|
||||||
|
|
||||||
|
# put user_id into action cache:
|
||||||
|
self.action.task.cache['user_id'] = user.id
|
||||||
|
self.set_cache('user_id', user.id)
|
||||||
|
self.add_note(
|
||||||
|
"New user '%s' created for project %s with roles: %s" %
|
||||||
|
(self.username, project_id, default_roles))
|
||||||
|
elif self.action.state == "existing":
|
||||||
|
try:
|
||||||
|
user = self.id_manager.find_user(self.username)
|
||||||
|
|
||||||
|
self._grant_roles(user, default_roles, project_id)
|
||||||
|
except Exception as e:
|
||||||
|
self.add_note(
|
||||||
|
"Error: '%s' while attaching user: %s with roles: %s" %
|
||||||
|
(e, self.username, default_roles))
|
||||||
|
raise
|
||||||
|
|
||||||
|
# put user_id into action cache:
|
||||||
|
self.action.task.cache['user_id'] = user.id
|
||||||
|
self.set_cache('user_id', user.id)
|
||||||
|
self.add_note(("Existing user '%s' attached to project %s" +
|
||||||
|
" with roles: %s")
|
||||||
|
% (self.username, project_id,
|
||||||
|
default_roles))
|
||||||
|
|
||||||
def _submit(self, token_data):
|
def _submit(self, token_data):
|
||||||
"""
|
"""
|
||||||
The submit action is prformed when a token is submitted.
|
The submit action is performed when a token is submitted.
|
||||||
This is done for a user account only, and so should now only
|
This is done to set a user password only, and so should now only
|
||||||
set up the user, not the project, which was done in approve.
|
change the user password. The project and user themselves are created
|
||||||
|
on post_approve.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id_manager = user_store.IdentityManager()
|
self._validate_user_submit()
|
||||||
|
|
||||||
self.action.valid = self._validate_user()
|
|
||||||
self.action.save()
|
|
||||||
|
|
||||||
if not self.valid:
|
if not self.valid:
|
||||||
return
|
return
|
||||||
|
|
||||||
project_id = self.get_cache('project_id')
|
project_id = self.get_cache('project_id')
|
||||||
self.action.task.cache['project_id'] = project_id
|
self.action.task.cache['project_id'] = project_id
|
||||||
|
user_id = self.get_cache('user_id')
|
||||||
project = id_manager.get_project(project_id)
|
self.action.task.cache['user_id'] = user_id
|
||||||
|
|
||||||
if self.action.state == "default":
|
if self.action.state == "default":
|
||||||
|
user = self.id_manager.get_user(user_id)
|
||||||
try:
|
try:
|
||||||
roles = []
|
self.id_manager.update_user_password(
|
||||||
for role in self.default_roles:
|
user, token_data['password'])
|
||||||
ks_role = id_manager.find_role(role)
|
|
||||||
if ks_role:
|
|
||||||
roles.append(ks_role)
|
|
||||||
else:
|
|
||||||
raise TypeError("Keystone missing role: %s" % role)
|
|
||||||
|
|
||||||
user = id_manager.create_user(
|
|
||||||
name=self.username, password=token_data['password'],
|
|
||||||
email=self.email, project_id=project.id)
|
|
||||||
|
|
||||||
for role in roles:
|
|
||||||
id_manager.add_user_role(user, role, project.id)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.add_note(
|
self.add_note(
|
||||||
"Error: '%s' while creating user: %s with roles: %s" %
|
"Error: '%s' while changing password for user: %s" %
|
||||||
(e, self.username, self.default_roles))
|
(e, self.username))
|
||||||
raise
|
raise
|
||||||
|
self.add_note('User %s password has been changed.' % self.username)
|
||||||
|
|
||||||
self.add_note(
|
|
||||||
"New user '%s' created for project %s with roles: %s" %
|
|
||||||
(self.username, self.project_name, self.default_roles))
|
|
||||||
elif self.action.state == "existing":
|
elif self.action.state == "existing":
|
||||||
try:
|
# do nothing, everything is already done.
|
||||||
user = id_manager.find_user(self.username)
|
self.add_note(
|
||||||
|
"Existing user '%s' already attached to project %s" % (
|
||||||
roles = []
|
user_id, project_id))
|
||||||
for role in self.default_roles:
|
|
||||||
roles.append(id_manager.find_role(role))
|
|
||||||
|
|
||||||
for role in roles:
|
|
||||||
id_manager.add_user_role(user, role, project.id)
|
|
||||||
except Exception as e:
|
|
||||||
self.add_note(
|
|
||||||
"Error: '%s' while attaching user: %s with roles: %s" %
|
|
||||||
(e, self.username, self.default_roles))
|
|
||||||
raise
|
|
||||||
|
|
||||||
self.add_note(("Existing user '%s' attached to project %s" +
|
|
||||||
" with roles: %s")
|
|
||||||
% (self.username, self.project_name,
|
|
||||||
self.default_roles))
|
|
||||||
|
|
||||||
|
|
||||||
class ResetUser(UserNameAction):
|
class ResetUser(UserNameAction):
|
||||||
@ -761,6 +919,7 @@ def register_action_class(action_class, serializer_class):
|
|||||||
|
|
||||||
# Register each action model
|
# Register each action model
|
||||||
register_action_class(NewUser, serializers.NewUserSerializer)
|
register_action_class(NewUser, serializers.NewUserSerializer)
|
||||||
register_action_class(NewProject, serializers.NewProjectSerializer)
|
register_action_class(
|
||||||
|
NewProjectWithUser, serializers.NewProjectWithUserSerializer)
|
||||||
register_action_class(ResetUser, serializers.ResetUserSerializer)
|
register_action_class(ResetUser, serializers.ResetUserSerializer)
|
||||||
register_action_class(EditUserRoles, serializers.EditUserSerializer)
|
register_action_class(EditUserRoles, serializers.EditUserSerializer)
|
||||||
|
@ -29,21 +29,23 @@ def get_keystoneclient():
|
|||||||
password=settings.KEYSTONE['password'],
|
password=settings.KEYSTONE['password'],
|
||||||
project_name=settings.KEYSTONE['project_name'],
|
project_name=settings.KEYSTONE['project_name'],
|
||||||
auth_url=settings.KEYSTONE['auth_url'],
|
auth_url=settings.KEYSTONE['auth_url'],
|
||||||
user_domain_name="default",
|
user_domain_id=settings.KEYSTONE.get('domain_id', "default"),
|
||||||
project_domain_name="default",
|
project_domain_id=settings.KEYSTONE.get('domain_id', "default"),
|
||||||
)
|
)
|
||||||
sess = session.Session(auth=auth)
|
sess = session.Session(auth=auth)
|
||||||
auth = ks_client.Client(session=sess)
|
auth = ks_client.Client(session=sess)
|
||||||
return auth
|
return auth
|
||||||
|
|
||||||
|
|
||||||
def get_neutronclient():
|
def get_neutronclient(region):
|
||||||
# TODO(Adriant): Add region support.
|
auth = v3.Password(
|
||||||
neutron = neutron_client.Client(
|
|
||||||
username=settings.KEYSTONE['username'],
|
username=settings.KEYSTONE['username'],
|
||||||
password=settings.KEYSTONE['password'],
|
password=settings.KEYSTONE['password'],
|
||||||
tenant_name=settings.KEYSTONE['project_name'],
|
project_name=settings.KEYSTONE['project_name'],
|
||||||
auth_url=settings.KEYSTONE['auth_url'],
|
auth_url=settings.KEYSTONE['auth_url'],
|
||||||
region_name=settings.DEFAULT_REGION
|
user_domain_id=settings.KEYSTONE.get('domain_id', "default"),
|
||||||
|
project_domain_id=settings.KEYSTONE.get('domain_id', "default"),
|
||||||
)
|
)
|
||||||
|
sess = session.Session(auth=auth)
|
||||||
|
neutron = neutron_client.Client(session=sess, region_name=region)
|
||||||
return neutron
|
return neutron
|
||||||
|
@ -44,7 +44,15 @@ class NewUserSerializer(BaseUserNameSerializer):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NewProjectSerializer(BaseUserNameSerializer):
|
class NewProjectSerializer(serializers.Serializer):
|
||||||
|
parent_id = serializers.CharField(
|
||||||
|
max_length=200, default=None, allow_null=True)
|
||||||
|
project_name = serializers.CharField(max_length=200)
|
||||||
|
|
||||||
|
|
||||||
|
class NewProjectWithUserSerializer(BaseUserNameSerializer):
|
||||||
|
parent_id = serializers.CharField(
|
||||||
|
max_length=200, default=None, allow_null=True)
|
||||||
project_name = serializers.CharField(max_length=200)
|
project_name = serializers.CharField(max_length=200)
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,13 +13,14 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from stacktask.actions.models import BaseAction
|
from stacktask.actions.models import BaseAction
|
||||||
from stacktask.actions.tenant_setup.serializers import DefaultProjectResourcesSerializer
|
from stacktask.actions.tenant_setup.serializers import (
|
||||||
|
NewDefaultNetworkSerializer, NewProjectDefaultNetworkSerializer)
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from stacktask.actions.user_store import IdentityManager
|
from stacktask.actions.user_store import IdentityManager
|
||||||
from stacktask.actions import openstack_clients
|
from stacktask.actions import openstack_clients
|
||||||
|
|
||||||
|
|
||||||
class DefaultProjectResources(BaseAction):
|
class NewDefaultNetwork(BaseAction):
|
||||||
"""
|
"""
|
||||||
This action will setup all required basic networking
|
This action will setup all required basic networking
|
||||||
resources so that a new user can launch instances
|
resources so that a new user can launch instances
|
||||||
@ -27,36 +28,62 @@ class DefaultProjectResources(BaseAction):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
required = [
|
required = [
|
||||||
'setup_resources'
|
'setup_network',
|
||||||
|
'project_id',
|
||||||
|
'region',
|
||||||
]
|
]
|
||||||
|
|
||||||
region = settings.DEFAULT_REGION
|
|
||||||
|
|
||||||
defaults = settings.ACTION_SETTINGS['DefaultProjectResources'][region]
|
|
||||||
|
|
||||||
def _validate(self):
|
def _validate(self):
|
||||||
|
|
||||||
project_id = self.action.task.cache.get('project_id', None)
|
# Default state is invalid
|
||||||
|
self.action.valid = False
|
||||||
|
|
||||||
valid = False
|
if not self.project_id:
|
||||||
if project_id:
|
|
||||||
valid = True
|
|
||||||
self.add_note('project_id given: %s' % project_id)
|
|
||||||
else:
|
|
||||||
self.add_note('No project_id given.')
|
self.add_note('No project_id given.')
|
||||||
return valid
|
return
|
||||||
|
|
||||||
def _setup_resources(self):
|
if not self.region:
|
||||||
neutron = openstack_clients.get_neutronclient()
|
self.add_note('No region given.')
|
||||||
|
return
|
||||||
|
|
||||||
project_id = self.action.task.cache['project_id']
|
keystone_user = self.action.task.keystone_user
|
||||||
|
if keystone_user.get('project_id') != self.project_id:
|
||||||
|
self.add_note('Project id does not match keystone user project.')
|
||||||
|
return
|
||||||
|
|
||||||
|
id_manager = IdentityManager()
|
||||||
|
|
||||||
|
project = id_manager.get_project(self.project_id)
|
||||||
|
if not project:
|
||||||
|
self.add_note('Project does not exist.')
|
||||||
|
return
|
||||||
|
self.add_note('Project_id: %s exists.' % project.id)
|
||||||
|
|
||||||
|
region = id_manager.find_region(self.region)
|
||||||
|
if not region:
|
||||||
|
self.add_note('Region does not exist.')
|
||||||
|
return
|
||||||
|
self.add_note('Region: %s exists.' % self.region)
|
||||||
|
|
||||||
|
self.defaults = settings.ACTION_SETTINGS.get(
|
||||||
|
'NewDefaultNetwork', {}).get(self.region, {})
|
||||||
|
|
||||||
|
if not self.defaults:
|
||||||
|
self.add_note('ERROR: No default settings for given region.')
|
||||||
|
return
|
||||||
|
|
||||||
|
self.action.valid = True
|
||||||
|
|
||||||
|
def _create_network(self):
|
||||||
|
|
||||||
|
neutron = openstack_clients.get_neutronclient(region=self.region)
|
||||||
|
|
||||||
if not self.get_cache('network_id'):
|
if not self.get_cache('network_id'):
|
||||||
try:
|
try:
|
||||||
network_body = {
|
network_body = {
|
||||||
"network": {
|
"network": {
|
||||||
"name": self.defaults['network_name'],
|
"name": self.defaults['network_name'],
|
||||||
'tenant_id': project_id,
|
'tenant_id': self.project_id,
|
||||||
"admin_state_up": True
|
"admin_state_up": True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,11 +96,11 @@ class DefaultProjectResources(BaseAction):
|
|||||||
self.set_cache('network_id', network['network']['id'])
|
self.set_cache('network_id', network['network']['id'])
|
||||||
self.add_note("Network %s created for project %s" %
|
self.add_note("Network %s created for project %s" %
|
||||||
(self.defaults['network_name'],
|
(self.defaults['network_name'],
|
||||||
self.action.task.cache['project_id']))
|
self.project_id))
|
||||||
else:
|
else:
|
||||||
self.add_note("Network %s already created for project %s" %
|
self.add_note("Network %s already created for project %s" %
|
||||||
(self.defaults['network_name'],
|
(self.defaults['network_name'],
|
||||||
self.action.task.cache['project_id']))
|
self.project_id))
|
||||||
|
|
||||||
if not self.get_cache('subnet_id'):
|
if not self.get_cache('subnet_id'):
|
||||||
try:
|
try:
|
||||||
@ -81,7 +108,7 @@ class DefaultProjectResources(BaseAction):
|
|||||||
"subnet": {
|
"subnet": {
|
||||||
"network_id": self.get_cache('network_id'),
|
"network_id": self.get_cache('network_id'),
|
||||||
"ip_version": 4,
|
"ip_version": 4,
|
||||||
'tenant_id': project_id,
|
'tenant_id': self.project_id,
|
||||||
'dns_nameservers': self.defaults['DNS_NAMESERVERS'],
|
'dns_nameservers': self.defaults['DNS_NAMESERVERS'],
|
||||||
"cidr": self.defaults['SUBNET_CIDR']
|
"cidr": self.defaults['SUBNET_CIDR']
|
||||||
}
|
}
|
||||||
@ -106,7 +133,7 @@ class DefaultProjectResources(BaseAction):
|
|||||||
"external_gateway_info": {
|
"external_gateway_info": {
|
||||||
"network_id": self.defaults['public_network']
|
"network_id": self.defaults['public_network']
|
||||||
},
|
},
|
||||||
'tenant_id': project_id,
|
'tenant_id': self.project_id,
|
||||||
"admin_state_up": True
|
"admin_state_up": True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,10 +145,10 @@ class DefaultProjectResources(BaseAction):
|
|||||||
raise
|
raise
|
||||||
self.set_cache('router_id', router['router']['id'])
|
self.set_cache('router_id', router['router']['id'])
|
||||||
self.add_note("Router created for project %s" %
|
self.add_note("Router created for project %s" %
|
||||||
self.action.task.cache['project_id'])
|
self.project_id)
|
||||||
else:
|
else:
|
||||||
self.add_note("Router already created for project %s" %
|
self.add_note("Router already created for project %s" %
|
||||||
self.action.task.cache['project_id'])
|
self.project_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
interface_body = {
|
interface_body = {
|
||||||
@ -136,76 +163,207 @@ class DefaultProjectResources(BaseAction):
|
|||||||
self.add_note("Interface added to router for subnet")
|
self.add_note("Interface added to router for subnet")
|
||||||
|
|
||||||
def _pre_approve(self):
|
def _pre_approve(self):
|
||||||
# Not exactly valid, but not exactly invalid.
|
self._validate()
|
||||||
self.action.valid = True
|
|
||||||
self.action.save()
|
self.action.save()
|
||||||
|
|
||||||
def _post_approve(self):
|
def _post_approve(self):
|
||||||
self.action.valid = self._validate()
|
self._validate()
|
||||||
self.action.save()
|
self.action.save()
|
||||||
|
|
||||||
if self.setup_resources and self.valid:
|
if self.setup_network and self.valid:
|
||||||
self._setup_resources()
|
self._create_network()
|
||||||
|
|
||||||
def _submit(self, token_data):
|
def _submit(self, token_data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AddAdminToProject(BaseAction):
|
class NewProjectDefaultNetwork(NewDefaultNetwork):
|
||||||
"""
|
"""
|
||||||
Action to add 'admin' user to project for
|
A variant of NewDefaultNetwork that expects the project
|
||||||
monitoring purposes.
|
to not be created until after post_approve.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
required = [
|
||||||
|
'setup_network',
|
||||||
|
'region',
|
||||||
|
]
|
||||||
|
|
||||||
|
def _pre_validate(self):
|
||||||
|
|
||||||
|
# Default state is invalid
|
||||||
|
self.action.valid = False
|
||||||
|
|
||||||
|
# We don't check project here as it doesn't exist yet.
|
||||||
|
|
||||||
|
if not self.region:
|
||||||
|
self.add_note('No region given.')
|
||||||
|
return
|
||||||
|
|
||||||
|
id_manager = IdentityManager()
|
||||||
|
|
||||||
|
region = id_manager.find_region(self.region)
|
||||||
|
if not region:
|
||||||
|
self.add_note('Region does not exist.')
|
||||||
|
return
|
||||||
|
self.add_note('Region: %s exists.' % self.region)
|
||||||
|
|
||||||
|
self.defaults = settings.ACTION_SETTINGS.get(
|
||||||
|
'NewDefaultNetwork', {}).get(self.region, {})
|
||||||
|
|
||||||
|
if not self.defaults:
|
||||||
|
self.add_note('ERROR: No default settings for given region.')
|
||||||
|
return
|
||||||
|
|
||||||
|
self.action.valid = True
|
||||||
|
|
||||||
def _validate(self):
|
def _validate(self):
|
||||||
|
|
||||||
project_id = self.action.task.cache.get('project_id', None)
|
# Default state is invalid
|
||||||
|
self.action.valid = False
|
||||||
|
|
||||||
valid = False
|
self.project_id = self.action.task.cache.get('project_id', None)
|
||||||
if project_id:
|
|
||||||
valid = True
|
if not self.project_id:
|
||||||
self.add_note('project_id given: %s' % project_id)
|
|
||||||
else:
|
|
||||||
self.add_note('No project_id given.')
|
self.add_note('No project_id given.')
|
||||||
return valid
|
return
|
||||||
|
|
||||||
|
if not self.region:
|
||||||
|
self.add_note('No region given.')
|
||||||
|
return
|
||||||
|
|
||||||
|
id_manager = IdentityManager()
|
||||||
|
|
||||||
|
project = id_manager.get_project(self.project_id)
|
||||||
|
if not project:
|
||||||
|
self.add_note('Project does not exist.')
|
||||||
|
return
|
||||||
|
self.add_note('Project_id: %s exists.' % project.id)
|
||||||
|
|
||||||
|
region = id_manager.find_region(self.region)
|
||||||
|
if not region:
|
||||||
|
self.add_note('Region does not exist.')
|
||||||
|
return
|
||||||
|
self.add_note('Region: %s exists.' % self.region)
|
||||||
|
|
||||||
|
self.defaults = settings.ACTION_SETTINGS.get(
|
||||||
|
'NewDefaultNetwork', {}).get(self.region, {})
|
||||||
|
|
||||||
|
if not self.defaults:
|
||||||
|
self.add_note('ERROR: No default settings for given region.')
|
||||||
|
return
|
||||||
|
|
||||||
|
self.action.valid = True
|
||||||
|
|
||||||
def _pre_approve(self):
|
def _pre_approve(self):
|
||||||
# Not yet exactly valid, but not exactly invalid.
|
self._pre_validate()
|
||||||
self.action.valid = True
|
|
||||||
self.action.save()
|
self.action.save()
|
||||||
|
|
||||||
def _post_approve(self):
|
def _post_approve(self):
|
||||||
self.action.valid = self._validate()
|
self._validate()
|
||||||
self.action.save()
|
self.action.save()
|
||||||
|
|
||||||
|
if self.setup_network and self.valid:
|
||||||
|
self._create_network()
|
||||||
|
|
||||||
|
|
||||||
|
class AddDefaultUsersToProject(BaseAction):
|
||||||
|
"""
|
||||||
|
The purpose of this action is to add a given set of users after
|
||||||
|
the creation of a new Project. This is mainly for administrative
|
||||||
|
purposes, and for users involved with migrations, monitoring, and
|
||||||
|
general admin tasks that should de present by default.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _validate_users(self):
|
||||||
|
|
||||||
|
self.users = settings.ACTION_SETTINGS.get(
|
||||||
|
'AddDefaultUsersToProject', {}).get('default_users', [])
|
||||||
|
self.roles = settings.ACTION_SETTINGS.get(
|
||||||
|
'AddDefaultUsersToProject', {}).get('default_roles', [])
|
||||||
|
|
||||||
|
id_manager = IdentityManager()
|
||||||
|
|
||||||
|
all_found = True
|
||||||
|
for user in self.users:
|
||||||
|
ks_user = id_manager.find_user(user)
|
||||||
|
if ks_user:
|
||||||
|
self.add_note('User: %s exists.' % user)
|
||||||
|
else:
|
||||||
|
self.add_note('ERROR: User: %s does not exist.' % user)
|
||||||
|
all_found = False
|
||||||
|
|
||||||
|
for role in self.roles:
|
||||||
|
ks_role = id_manager.find_role(role)
|
||||||
|
if ks_role:
|
||||||
|
self.add_note('Role: %s exists.' % role)
|
||||||
|
else:
|
||||||
|
self.add_note('ERROR: Role: %s does not exist.' % role)
|
||||||
|
all_found = False
|
||||||
|
|
||||||
|
if all_found:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _validate_project(self):
|
||||||
|
|
||||||
|
self.project_id = self.action.task.cache.get('project_id', None)
|
||||||
|
|
||||||
|
id_manager = IdentityManager()
|
||||||
|
|
||||||
|
project = id_manager.get_project(self.project_id)
|
||||||
|
if not project:
|
||||||
|
self.add_note('Project does not exist.')
|
||||||
|
return False
|
||||||
|
self.add_note('Project_id: %s exists.' % project.id)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _validate(self):
|
||||||
|
if self._validate_users() and self._validate_project():
|
||||||
|
self.action.valid = True
|
||||||
|
else:
|
||||||
|
self.action.valid = False
|
||||||
|
self.action.save()
|
||||||
|
|
||||||
|
def _pre_approve(self):
|
||||||
|
self.action.valid = self._validate_users()
|
||||||
|
self.action.save()
|
||||||
|
|
||||||
|
def _post_approve(self):
|
||||||
|
self._validate()
|
||||||
|
|
||||||
if self.valid and not self.action.state == "completed":
|
if self.valid and not self.action.state == "completed":
|
||||||
id_manager = IdentityManager()
|
id_manager = IdentityManager()
|
||||||
|
|
||||||
project = id_manager.get_project(
|
project = id_manager.get_project(self.project_id)
|
||||||
self.action.task.cache['project_id'])
|
|
||||||
try:
|
try:
|
||||||
user = id_manager.find_user(name="admin")
|
for user in self.users:
|
||||||
role = id_manager.find_role(name="admin")
|
ks_user = id_manager.find_user(name=user)
|
||||||
id_manager.add_user_role(user, role, project.id)
|
for role in self.roles:
|
||||||
|
ks_role = id_manager.find_role(name=role)
|
||||||
|
id_manager.add_user_role(ks_user, ks_role, project.id)
|
||||||
|
self.add_note(
|
||||||
|
'User: "%s" given role: %s on project: %s.' %
|
||||||
|
(ks_user.name, ks_role.name, project.id))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.add_note(
|
self.add_note(
|
||||||
"Error: '%s' while adding admin to project: %s" %
|
"Error: '%s' while adding users to project: %s" %
|
||||||
(e, project.id))
|
(e, project.id))
|
||||||
raise
|
raise
|
||||||
self.action.state = "completed"
|
self.action.state = "completed"
|
||||||
self.action.save()
|
self.action.save()
|
||||||
self.add_note(
|
self.add_note("All users added.")
|
||||||
'Admin has been added to %s.' %
|
|
||||||
self.action.task.cache['project_id'])
|
|
||||||
|
|
||||||
def _submit(self, token_data):
|
def _submit(self, token_data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
action_classes = {
|
action_classes = {
|
||||||
'DefaultProjectResources': (DefaultProjectResources,
|
'NewDefaultNetwork': (
|
||||||
DefaultProjectResourcesSerializer),
|
NewDefaultNetwork, NewDefaultNetworkSerializer),
|
||||||
'AddAdminToProject': (AddAdminToProject, None)
|
'NewProjectDefaultNetwork': (
|
||||||
|
NewProjectDefaultNetwork, NewProjectDefaultNetworkSerializer),
|
||||||
|
'AddDefaultUsersToProject': (AddDefaultUsersToProject, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,5 +15,12 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
class DefaultProjectResourcesSerializer(serializers.Serializer):
|
class NewDefaultNetworkSerializer(serializers.Serializer):
|
||||||
setup_resources = serializers.BooleanField(default=False)
|
setup_network = serializers.BooleanField(default=True)
|
||||||
|
project_id = serializers.CharField(max_length=100)
|
||||||
|
region = serializers.CharField(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
|
class NewProjectDefaultNetworkSerializer(serializers.Serializer):
|
||||||
|
setup_network = serializers.BooleanField(default=False)
|
||||||
|
region = serializers.CharField(max_length=100)
|
||||||
|
@ -17,7 +17,7 @@ from django.test import TestCase
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from stacktask.actions.tenant_setup.models import (
|
from stacktask.actions.tenant_setup.models import (
|
||||||
AddAdminToProject, DefaultProjectResources)
|
NewDefaultNetwork, NewProjectDefaultNetwork, AddDefaultUsersToProject)
|
||||||
from stacktask.api.models import Task
|
from stacktask.api.models import Task
|
||||||
from stacktask.api.v1 import tests
|
from stacktask.api.v1 import tests
|
||||||
from stacktask.api.v1.tests import FakeManager, setup_temp_cache
|
from stacktask.api.v1.tests import FakeManager, setup_temp_cache
|
||||||
@ -69,32 +69,44 @@ def setup_neutron_cache():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_fake_neutron():
|
def get_fake_neutron(region):
|
||||||
return FakeNeutronClient()
|
return FakeNeutronClient()
|
||||||
|
|
||||||
|
|
||||||
class TenantSetupActionTests(TestCase):
|
class ProjectSetupActionTests(TestCase):
|
||||||
|
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
FakeManager)
|
FakeManager)
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.openstack_clients.get_neutronclient',
|
@mock.patch(
|
||||||
get_fake_neutron)
|
'stacktask.actions.tenant_setup.models.' +
|
||||||
def test_resource_setup(self):
|
'openstack_clients.get_neutronclient',
|
||||||
|
get_fake_neutron)
|
||||||
|
def test_network_setup(self):
|
||||||
"""
|
"""
|
||||||
Base case, setup resources, no issues.
|
Base case, setup a new network , no issues.
|
||||||
"""
|
"""
|
||||||
setup_neutron_cache()
|
setup_neutron_cache()
|
||||||
task = Task.objects.create(
|
task = Task.objects.create(
|
||||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin']})
|
ip_address="0.0.0.0",
|
||||||
|
keystone_user={
|
||||||
|
'roles': ['admin'],
|
||||||
|
'project_id': 'test_project_id'})
|
||||||
|
|
||||||
task.cache = {'project_id': "1"}
|
project = mock.Mock()
|
||||||
|
project.id = 'test_project_id'
|
||||||
|
project.name = 'test_project'
|
||||||
|
project.roles = {}
|
||||||
|
|
||||||
|
setup_temp_cache({'test_project': project}, {})
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'setup_resources': True,
|
'setup_network': True,
|
||||||
|
'region': 'RegionOne',
|
||||||
|
'project_id': 'test_project_id',
|
||||||
}
|
}
|
||||||
|
|
||||||
action = DefaultProjectResources(data, task=task,
|
action = NewDefaultNetwork(
|
||||||
order=1)
|
data, task=task, order=1)
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
@ -116,56 +128,36 @@ class TenantSetupActionTests(TestCase):
|
|||||||
|
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
FakeManager)
|
FakeManager)
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.openstack_clients.get_neutronclient',
|
@mock.patch(
|
||||||
get_fake_neutron)
|
'stacktask.actions.tenant_setup.models.' +
|
||||||
def test_resource_setup_no_id(self):
|
'openstack_clients.get_neutronclient',
|
||||||
"""
|
get_fake_neutron)
|
||||||
No project id given, should do nothing.
|
def test_network_setup_no_setup(self):
|
||||||
"""
|
|
||||||
setup_neutron_cache()
|
|
||||||
task = Task.objects.create(
|
|
||||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin']})
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'setup_resources': True,
|
|
||||||
}
|
|
||||||
|
|
||||||
action = DefaultProjectResources(data, task=task,
|
|
||||||
order=1)
|
|
||||||
|
|
||||||
action.pre_approve()
|
|
||||||
self.assertEquals(action.valid, True)
|
|
||||||
|
|
||||||
action.post_approve()
|
|
||||||
self.assertEquals(action.valid, False)
|
|
||||||
|
|
||||||
self.assertEquals(action.action.cache, {})
|
|
||||||
|
|
||||||
global neutron_cache
|
|
||||||
self.assertEquals(len(neutron_cache['networks']), 0)
|
|
||||||
self.assertEquals(len(neutron_cache['routers']), 0)
|
|
||||||
self.assertEquals(len(neutron_cache['subnets']), 0)
|
|
||||||
|
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
|
||||||
FakeManager)
|
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.openstack_clients.get_neutronclient',
|
|
||||||
get_fake_neutron)
|
|
||||||
def test_resource_setup_no_setup(self):
|
|
||||||
"""
|
"""
|
||||||
Told not to setup, should do nothing.
|
Told not to setup, should do nothing.
|
||||||
"""
|
"""
|
||||||
setup_neutron_cache()
|
setup_neutron_cache()
|
||||||
task = Task.objects.create(
|
task = Task.objects.create(
|
||||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin']})
|
ip_address="0.0.0.0",
|
||||||
|
keystone_user={
|
||||||
|
'roles': ['admin'],
|
||||||
|
'project_id': 'test_project_id'})
|
||||||
|
|
||||||
|
project = mock.Mock()
|
||||||
|
project.id = 'test_project_id'
|
||||||
|
project.name = 'test_project'
|
||||||
|
project.roles = {}
|
||||||
|
|
||||||
|
setup_temp_cache({'test_project': project}, {})
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'setup_resources': False,
|
'setup_network': False,
|
||||||
|
'region': 'RegionOne',
|
||||||
|
'project_id': 'test_project_id',
|
||||||
}
|
}
|
||||||
|
|
||||||
task.cache = {'project_id': "1"}
|
action = NewDefaultNetwork(
|
||||||
|
data, task=task, order=1)
|
||||||
action = DefaultProjectResources(data, task=task,
|
|
||||||
order=1)
|
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
@ -182,25 +174,37 @@ class TenantSetupActionTests(TestCase):
|
|||||||
|
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
FakeManager)
|
FakeManager)
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.openstack_clients.get_neutronclient',
|
@mock.patch(
|
||||||
get_fake_neutron)
|
'stacktask.actions.tenant_setup.models.' +
|
||||||
def test_resource_setup_fail(self):
|
'openstack_clients.get_neutronclient',
|
||||||
|
get_fake_neutron)
|
||||||
|
def test_network_setup_fail(self):
|
||||||
"""
|
"""
|
||||||
Should fail, but on re_approve will continue where it left off.
|
Should fail, but on re_approve will continue where it left off.
|
||||||
"""
|
"""
|
||||||
setup_neutron_cache()
|
setup_neutron_cache()
|
||||||
global neutron_cache
|
global neutron_cache
|
||||||
task = Task.objects.create(
|
task = Task.objects.create(
|
||||||
ip_address="0.0.0.0", keystone_user={'roles': ['admin']})
|
ip_address="0.0.0.0",
|
||||||
|
keystone_user={
|
||||||
|
'roles': ['admin'],
|
||||||
|
'project_id': 'test_project_id'})
|
||||||
|
|
||||||
|
project = mock.Mock()
|
||||||
|
project.id = 'test_project_id'
|
||||||
|
project.name = 'test_project'
|
||||||
|
project.roles = {}
|
||||||
|
|
||||||
|
setup_temp_cache({'test_project': project}, {})
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'setup_resources': True,
|
'setup_network': True,
|
||||||
|
'region': 'RegionOne',
|
||||||
|
'project_id': 'test_project_id',
|
||||||
}
|
}
|
||||||
|
|
||||||
task.cache = {'project_id': "1"}
|
action = NewDefaultNetwork(
|
||||||
|
data, task=task, order=1)
|
||||||
action = DefaultProjectResources(data, task=task,
|
|
||||||
order=1)
|
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
@ -240,9 +244,216 @@ class TenantSetupActionTests(TestCase):
|
|||||||
|
|
||||||
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
FakeManager)
|
FakeManager)
|
||||||
def test_add_admin(self):
|
@mock.patch(
|
||||||
|
'stacktask.actions.tenant_setup.models.' +
|
||||||
|
'openstack_clients.get_neutronclient',
|
||||||
|
get_fake_neutron)
|
||||||
|
def test_new_project_network_setup(self):
|
||||||
|
"""
|
||||||
|
Base case, setup network after a new project, no issues.
|
||||||
|
"""
|
||||||
|
setup_neutron_cache()
|
||||||
|
task = Task.objects.create(
|
||||||
|
ip_address="0.0.0.0", keystone_user={'roles': ['admin']})
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'setup_network': True,
|
||||||
|
'region': 'RegionOne',
|
||||||
|
}
|
||||||
|
|
||||||
|
action = NewProjectDefaultNetwork(
|
||||||
|
data, task=task, order=1)
|
||||||
|
|
||||||
|
action.pre_approve()
|
||||||
|
self.assertEquals(action.valid, True)
|
||||||
|
|
||||||
|
# Now we add the project data as this is where the project
|
||||||
|
# would be created:
|
||||||
|
project = mock.Mock()
|
||||||
|
project.id = 'test_project_id'
|
||||||
|
project.name = 'test_project'
|
||||||
|
project.roles = {}
|
||||||
|
|
||||||
|
setup_temp_cache({'test_project': project}, {})
|
||||||
|
|
||||||
|
task.cache = {'project_id': "test_project_id"}
|
||||||
|
|
||||||
|
action.post_approve()
|
||||||
|
self.assertEquals(action.valid, True)
|
||||||
|
|
||||||
|
self.assertEquals(
|
||||||
|
action.action.cache,
|
||||||
|
{'network_id': 'net_id_0',
|
||||||
|
'router_id': 'router_id_2',
|
||||||
|
'subnet_id': 'subnet_id_1'}
|
||||||
|
)
|
||||||
|
|
||||||
|
global neutron_cache
|
||||||
|
self.assertEquals(len(neutron_cache['networks']), 1)
|
||||||
|
self.assertEquals(len(neutron_cache['routers']), 1)
|
||||||
|
self.assertEquals(len(neutron_cache['subnets']), 1)
|
||||||
|
|
||||||
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
|
FakeManager)
|
||||||
|
@mock.patch(
|
||||||
|
'stacktask.actions.tenant_setup.models.' +
|
||||||
|
'openstack_clients.get_neutronclient',
|
||||||
|
get_fake_neutron)
|
||||||
|
def test_new_project_network_setup_no_id(self):
|
||||||
|
"""
|
||||||
|
No project id given, should do nothing.
|
||||||
|
"""
|
||||||
|
setup_neutron_cache()
|
||||||
|
task = Task.objects.create(
|
||||||
|
ip_address="0.0.0.0", keystone_user={'roles': ['admin']})
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'setup_network': True,
|
||||||
|
'region': 'RegionOne',
|
||||||
|
}
|
||||||
|
|
||||||
|
action = NewProjectDefaultNetwork(
|
||||||
|
data, task=task, order=1)
|
||||||
|
|
||||||
|
action.pre_approve()
|
||||||
|
self.assertEquals(action.valid, True)
|
||||||
|
|
||||||
|
action.post_approve()
|
||||||
|
self.assertEquals(action.valid, False)
|
||||||
|
|
||||||
|
self.assertEquals(action.action.cache, {})
|
||||||
|
|
||||||
|
global neutron_cache
|
||||||
|
self.assertEquals(len(neutron_cache['networks']), 0)
|
||||||
|
self.assertEquals(len(neutron_cache['routers']), 0)
|
||||||
|
self.assertEquals(len(neutron_cache['subnets']), 0)
|
||||||
|
|
||||||
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
|
FakeManager)
|
||||||
|
@mock.patch(
|
||||||
|
'stacktask.actions.tenant_setup.models.' +
|
||||||
|
'openstack_clients.get_neutronclient',
|
||||||
|
get_fake_neutron)
|
||||||
|
def test_new_project_network_setup_no_setup(self):
|
||||||
|
"""
|
||||||
|
Told not to setup, should do nothing.
|
||||||
|
"""
|
||||||
|
setup_neutron_cache()
|
||||||
|
task = Task.objects.create(
|
||||||
|
ip_address="0.0.0.0", keystone_user={'roles': ['admin']})
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'setup_network': False,
|
||||||
|
'region': 'RegionOne',
|
||||||
|
}
|
||||||
|
|
||||||
|
action = NewProjectDefaultNetwork(
|
||||||
|
data, task=task, order=1)
|
||||||
|
|
||||||
|
action.pre_approve()
|
||||||
|
self.assertEquals(action.valid, True)
|
||||||
|
|
||||||
|
# Now we add the project data as this is where the project
|
||||||
|
# would be created:
|
||||||
|
project = mock.Mock()
|
||||||
|
project.id = 'test_project_id'
|
||||||
|
project.name = 'test_project'
|
||||||
|
project.roles = {}
|
||||||
|
|
||||||
|
setup_temp_cache({'test_project': project}, {})
|
||||||
|
|
||||||
|
task.cache = {'project_id': "test_project_id"}
|
||||||
|
|
||||||
|
action.post_approve()
|
||||||
|
self.assertEquals(action.valid, True)
|
||||||
|
|
||||||
|
self.assertEquals(action.action.cache, {})
|
||||||
|
|
||||||
|
global neutron_cache
|
||||||
|
self.assertEquals(len(neutron_cache['networks']), 0)
|
||||||
|
self.assertEquals(len(neutron_cache['routers']), 0)
|
||||||
|
self.assertEquals(len(neutron_cache['subnets']), 0)
|
||||||
|
|
||||||
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
|
FakeManager)
|
||||||
|
@mock.patch(
|
||||||
|
'stacktask.actions.tenant_setup.models.' +
|
||||||
|
'openstack_clients.get_neutronclient',
|
||||||
|
get_fake_neutron)
|
||||||
|
def test_new_project_network_setup_fail(self):
|
||||||
|
"""
|
||||||
|
Should fail, but on re_approve will continue where it left off.
|
||||||
|
"""
|
||||||
|
setup_neutron_cache()
|
||||||
|
global neutron_cache
|
||||||
|
task = Task.objects.create(
|
||||||
|
ip_address="0.0.0.0", keystone_user={'roles': ['admin']})
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'setup_network': True,
|
||||||
|
'region': 'RegionOne',
|
||||||
|
}
|
||||||
|
|
||||||
|
action = NewProjectDefaultNetwork(
|
||||||
|
data, task=task, order=1)
|
||||||
|
|
||||||
|
action.pre_approve()
|
||||||
|
self.assertEquals(action.valid, True)
|
||||||
|
|
||||||
|
neutron_cache['routers'] = []
|
||||||
|
|
||||||
|
# Now we add the project data as this is where the project
|
||||||
|
# would be created:
|
||||||
|
project = mock.Mock()
|
||||||
|
project.id = 'test_project_id'
|
||||||
|
project.name = 'test_project'
|
||||||
|
project.roles = {}
|
||||||
|
|
||||||
|
setup_temp_cache({'test_project': project}, {})
|
||||||
|
|
||||||
|
task.cache = {'project_id': "test_project_id"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
action.post_approve()
|
||||||
|
self.fail("Shouldn't get here.")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEquals(
|
||||||
|
action.action.cache,
|
||||||
|
{'network_id': 'net_id_0',
|
||||||
|
'subnet_id': 'subnet_id_1'}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEquals(len(neutron_cache['networks']), 1)
|
||||||
|
self.assertEquals(len(neutron_cache['subnets']), 1)
|
||||||
|
self.assertEquals(len(neutron_cache['routers']), 0)
|
||||||
|
|
||||||
|
neutron_cache['routers'] = {}
|
||||||
|
|
||||||
|
action.post_approve()
|
||||||
|
|
||||||
|
self.assertEquals(
|
||||||
|
action.action.cache,
|
||||||
|
{'network_id': 'net_id_0',
|
||||||
|
'router_id': 'router_id_2',
|
||||||
|
'subnet_id': 'subnet_id_1'}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEquals(len(neutron_cache['networks']), 1)
|
||||||
|
self.assertEquals(len(neutron_cache['routers']), 1)
|
||||||
|
self.assertEquals(len(neutron_cache['subnets']), 1)
|
||||||
|
|
||||||
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
|
FakeManager)
|
||||||
|
def test_add_default_users(self):
|
||||||
"""
|
"""
|
||||||
Base case, adds admin user with admin role to project.
|
Base case, adds admin user with admin role to project.
|
||||||
|
|
||||||
|
NOTE(adriant): both the lists of users, and the roles to add
|
||||||
|
come from test_settings. This test assumes the conf setting of:
|
||||||
|
default_users = ['admin']
|
||||||
|
default_roles = ['admin']
|
||||||
"""
|
"""
|
||||||
project = mock.Mock()
|
project = mock.Mock()
|
||||||
project.id = 'test_project_id'
|
project.id = 'test_project_id'
|
||||||
@ -256,7 +467,7 @@ class TenantSetupActionTests(TestCase):
|
|||||||
|
|
||||||
task.cache = {'project_id': "test_project_id"}
|
task.cache = {'project_id': "test_project_id"}
|
||||||
|
|
||||||
action = AddAdminToProject({}, task=task, order=1)
|
action = AddDefaultUsersToProject({}, task=task, order=1)
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
@ -285,7 +496,7 @@ class TenantSetupActionTests(TestCase):
|
|||||||
|
|
||||||
task.cache = {'project_id': "test_project_id"}
|
task.cache = {'project_id': "test_project_id"}
|
||||||
|
|
||||||
action = AddAdminToProject({}, task=task, order=1)
|
action = AddDefaultUsersToProject({}, task=task, order=1)
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
|
@ -17,7 +17,7 @@ from django.test import TestCase
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from stacktask.actions.models import (
|
from stacktask.actions.models import (
|
||||||
EditUserRoles, NewProject, NewUser, ResetUser)
|
EditUserRoles, NewProjectWithUser, NewUser, ResetUser)
|
||||||
from stacktask.api.models import Task
|
from stacktask.api.models import Task
|
||||||
from stacktask.api.v1 import tests
|
from stacktask.api.v1 import tests
|
||||||
from stacktask.api.v1.tests import FakeManager, setup_temp_cache
|
from stacktask.api.v1.tests import FakeManager, setup_temp_cache
|
||||||
@ -201,8 +201,8 @@ class ActionTests(TestCase):
|
|||||||
"""
|
"""
|
||||||
Base case, no project, no user.
|
Base case, no project, no user.
|
||||||
|
|
||||||
Project created at post_approve step,
|
Project and user created at post_approve step,
|
||||||
user at submit step.
|
user password at submit step.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
setup_temp_cache({}, {})
|
setup_temp_cache({}, {})
|
||||||
@ -213,11 +213,12 @@ class ActionTests(TestCase):
|
|||||||
'project_id': 'test_project_id'})
|
'project_id': 'test_project_id'})
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
'parent_id': None,
|
||||||
'email': 'test@example.com',
|
'email': 'test@example.com',
|
||||||
'project_name': 'test_project',
|
'project_name': 'test_project',
|
||||||
}
|
}
|
||||||
|
|
||||||
action = NewProject(data, task=task, order=1)
|
action = NewProjectWithUser(data, task=task, order=1)
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
@ -227,7 +228,9 @@ class ActionTests(TestCase):
|
|||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
tests.temp_cache['projects']['test_project'].name,
|
tests.temp_cache['projects']['test_project'].name,
|
||||||
'test_project')
|
'test_project')
|
||||||
self.assertEquals(task.cache, {'project_id': "project_id_1"})
|
self.assertEquals(
|
||||||
|
task.cache,
|
||||||
|
{'project_id': 'project_id_1', 'user_id': 'user_id_1'})
|
||||||
|
|
||||||
token_data = {'password': '123456'}
|
token_data = {'password': '123456'}
|
||||||
action.submit(token_data)
|
action.submit(token_data)
|
||||||
@ -257,11 +260,12 @@ class ActionTests(TestCase):
|
|||||||
'project_id': 'test_project_id'})
|
'project_id': 'test_project_id'})
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
'parent_id': None,
|
||||||
'email': 'test@example.com',
|
'email': 'test@example.com',
|
||||||
'project_name': 'test_project',
|
'project_name': 'test_project',
|
||||||
}
|
}
|
||||||
|
|
||||||
action = NewProject(data, task=task, order=1)
|
action = NewProjectWithUser(data, task=task, order=1)
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
@ -271,14 +275,18 @@ class ActionTests(TestCase):
|
|||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
tests.temp_cache['projects']['test_project'].name,
|
tests.temp_cache['projects']['test_project'].name,
|
||||||
'test_project')
|
'test_project')
|
||||||
self.assertEquals(task.cache, {'project_id': "project_id_1"})
|
self.assertEquals(
|
||||||
|
task.cache,
|
||||||
|
{'project_id': 'project_id_1', 'user_id': 'user_id_1'})
|
||||||
|
|
||||||
action.post_approve()
|
action.post_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
tests.temp_cache['projects']['test_project'].name,
|
tests.temp_cache['projects']['test_project'].name,
|
||||||
'test_project')
|
'test_project')
|
||||||
self.assertEquals(task.cache, {'project_id': "project_id_1"})
|
self.assertEquals(
|
||||||
|
task.cache,
|
||||||
|
{'project_id': 'project_id_1', 'user_id': 'user_id_1'})
|
||||||
|
|
||||||
token_data = {'password': '123456'}
|
token_data = {'password': '123456'}
|
||||||
action.submit(token_data)
|
action.submit(token_data)
|
||||||
@ -301,7 +309,7 @@ class ActionTests(TestCase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
user = mock.Mock()
|
user = mock.Mock()
|
||||||
user.id = 'user_id'
|
user.id = 'user_id_1'
|
||||||
user.name = "test@example.com"
|
user.name = "test@example.com"
|
||||||
user.email = "test@example.com"
|
user.email = "test@example.com"
|
||||||
|
|
||||||
@ -313,11 +321,12 @@ class ActionTests(TestCase):
|
|||||||
'project_id': 'test_project_id'})
|
'project_id': 'test_project_id'})
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
'parent_id': None,
|
||||||
'email': 'test@example.com',
|
'email': 'test@example.com',
|
||||||
'project_name': 'test_project',
|
'project_name': 'test_project',
|
||||||
}
|
}
|
||||||
|
|
||||||
action = NewProject(data, task=task, order=1)
|
action = NewProjectWithUser(data, task=task, order=1)
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, True)
|
self.assertEquals(action.valid, True)
|
||||||
@ -327,7 +336,9 @@ class ActionTests(TestCase):
|
|||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
tests.temp_cache['projects']['test_project'].name,
|
tests.temp_cache['projects']['test_project'].name,
|
||||||
'test_project')
|
'test_project')
|
||||||
self.assertEquals(task.cache, {'project_id': "project_id_1"})
|
self.assertEquals(
|
||||||
|
task.cache,
|
||||||
|
{'project_id': 'project_id_1', 'user_id': 'user_id_1'})
|
||||||
|
|
||||||
token_data = {'password': '123456'}
|
token_data = {'password': '123456'}
|
||||||
action.submit(token_data)
|
action.submit(token_data)
|
||||||
@ -362,11 +373,12 @@ class ActionTests(TestCase):
|
|||||||
'project_id': 'test_project_id'})
|
'project_id': 'test_project_id'})
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
'parent_id': None,
|
||||||
'email': 'test@example.com',
|
'email': 'test@example.com',
|
||||||
'project_name': 'test_project',
|
'project_name': 'test_project',
|
||||||
}
|
}
|
||||||
|
|
||||||
action = NewProject(data, task=task, order=1)
|
action = NewProjectWithUser(data, task=task, order=1)
|
||||||
|
|
||||||
action.pre_approve()
|
action.pre_approve()
|
||||||
self.assertEquals(action.valid, False)
|
self.assertEquals(action.valid, False)
|
||||||
|
@ -152,7 +152,32 @@ class IdentityManager(object):
|
|||||||
project = None
|
project = None
|
||||||
return project
|
return project
|
||||||
|
|
||||||
def create_project(self, project_name, created_on):
|
def update_project(self, project, name=None, domain=None, description=None,
|
||||||
project = self.ks_client.projects.create(project_name,
|
enabled=None, **kwargs):
|
||||||
created_on=created_on)
|
try:
|
||||||
|
return self.ks_client.projects.update(
|
||||||
|
project=project, domain=domain, name=name,
|
||||||
|
description=description, enabled=enabled,
|
||||||
|
**kwargs)
|
||||||
|
except ks_exceptions.NotFound:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_project(
|
||||||
|
self, project_name, created_on, parent=None, domain="default"):
|
||||||
|
project = self.ks_client.projects.create(
|
||||||
|
project_name, domain, parent=parent, created_on=created_on)
|
||||||
return project
|
return project
|
||||||
|
|
||||||
|
def find_region(self, region_name):
|
||||||
|
try:
|
||||||
|
region = self.ks_client.regions.find(name=region_name)
|
||||||
|
except ks_exceptions.NotFound:
|
||||||
|
region = None
|
||||||
|
return region
|
||||||
|
|
||||||
|
def get_region(self, region_id):
|
||||||
|
try:
|
||||||
|
region = self.ks_client.regions.get(region_id)
|
||||||
|
except ks_exceptions.NotFound:
|
||||||
|
region = None
|
||||||
|
return region
|
||||||
|
20
stacktask/api/migrations/0003_task_approved_by.py
Normal file
20
stacktask/api/migrations/0003_task_approved_by.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import jsonfield.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0002_auto_20160815_2249'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='task',
|
||||||
|
name='approved_by',
|
||||||
|
field=jsonfield.fields.JSONField(default={}),
|
||||||
|
),
|
||||||
|
]
|
@ -37,6 +37,9 @@ class Task(models.Model):
|
|||||||
keystone_user = JSONField(default={})
|
keystone_user = JSONField(default={})
|
||||||
project_id = models.CharField(max_length=32, db_index=True, null=True)
|
project_id = models.CharField(max_length=32, db_index=True, null=True)
|
||||||
|
|
||||||
|
# keystone_user for the approver:
|
||||||
|
approved_by = JSONField(default={})
|
||||||
|
|
||||||
# type of the task, for easy grouping
|
# type of the task, for easy grouping
|
||||||
task_type = models.CharField(max_length=100, db_index=True)
|
task_type = models.CharField(max_length=100, db_index=True)
|
||||||
|
|
||||||
@ -81,6 +84,7 @@ class Task(models.Model):
|
|||||||
"uuid": self.uuid,
|
"uuid": self.uuid,
|
||||||
"ip_address": self.ip_address,
|
"ip_address": self.ip_address,
|
||||||
"keystone_user": self.keystone_user,
|
"keystone_user": self.keystone_user,
|
||||||
|
"approved_by": self.approved_by,
|
||||||
"project_id": self.project_id,
|
"project_id": self.project_id,
|
||||||
"actions": actions,
|
"actions": actions,
|
||||||
"task_type": self.task_type,
|
"task_type": self.task_type,
|
||||||
|
@ -194,18 +194,18 @@ class UserRoles(tasks.TaskView):
|
|||||||
request.data['user_id'] = user_id
|
request.data['user_id'] = user_id
|
||||||
|
|
||||||
self.logger.info("(%s) - New EditUserRoles request." % timezone.now())
|
self.logger.info("(%s) - New EditUserRoles request." % timezone.now())
|
||||||
processed = self.process_actions(request)
|
processed, status = self.process_actions(request)
|
||||||
|
|
||||||
errors = processed.get('errors', None)
|
errors = processed.get('errors', None)
|
||||||
if errors:
|
if errors:
|
||||||
self.logger.info("(%s) - Validation errors with registration." %
|
self.logger.info("(%s) - Validation errors with registration." %
|
||||||
timezone.now())
|
timezone.now())
|
||||||
return Response(errors, status=400)
|
return Response(errors, status=status)
|
||||||
|
|
||||||
task = processed['task']
|
task = processed['task']
|
||||||
self.logger.info("(%s) - AutoApproving EditUserRoles request."
|
self.logger.info("(%s) - AutoApproving EditUserRoles request."
|
||||||
% timezone.now())
|
% timezone.now())
|
||||||
response_dict, status = self.approve(task)
|
response_dict, status = self.approve(request, task)
|
||||||
|
|
||||||
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
||||||
|
|
||||||
@ -223,18 +223,18 @@ class UserRoles(tasks.TaskView):
|
|||||||
request.data['user_id'] = user_id
|
request.data['user_id'] = user_id
|
||||||
|
|
||||||
self.logger.info("(%s) - New EditUser request." % timezone.now())
|
self.logger.info("(%s) - New EditUser request." % timezone.now())
|
||||||
processed = self.process_actions(request)
|
processed, status = self.process_actions(request)
|
||||||
|
|
||||||
errors = processed.get('errors', None)
|
errors = processed.get('errors', None)
|
||||||
if errors:
|
if errors:
|
||||||
self.logger.info("(%s) - Validation errors with registration." %
|
self.logger.info("(%s) - Validation errors with registration." %
|
||||||
timezone.now())
|
timezone.now())
|
||||||
return Response(errors, status=400)
|
return Response(errors, status=status)
|
||||||
|
|
||||||
task = processed['task']
|
task = processed['task']
|
||||||
self.logger.info("(%s) - AutoApproving EditUser request."
|
self.logger.info("(%s) - AutoApproving EditUser request."
|
||||||
% timezone.now())
|
% timezone.now())
|
||||||
response_dict, status = self.approve(task)
|
response_dict, status = self.approve(request, task)
|
||||||
|
|
||||||
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ class TaskView(APIViewWithLogger):
|
|||||||
for action in action_list:
|
for action in action_list:
|
||||||
if action['serializer'] is not None:
|
if action['serializer'] is not None:
|
||||||
errors.update(action['serializer'].errors)
|
errors.update(action['serializer'].errors)
|
||||||
return {'errors': errors}
|
return {'errors': errors}, 400
|
||||||
|
|
||||||
hash_key = create_task_hash(self.task_type, action_list)
|
hash_key = create_task_hash(self.task_type, action_list)
|
||||||
duplicate_tasks = Task.objects.filter(
|
duplicate_tasks = Task.objects.filter(
|
||||||
@ -131,7 +131,9 @@ class TaskView(APIViewWithLogger):
|
|||||||
self.logger.info(
|
self.logger.info(
|
||||||
"(%s) - Task is a duplicate - Ignoring new task." %
|
"(%s) - Task is a duplicate - Ignoring new task." %
|
||||||
timezone.now())
|
timezone.now())
|
||||||
return {'errors': ['Task is a duplicate of an existing task']}
|
return (
|
||||||
|
{'errors': ['Task is a duplicate of an existing task']},
|
||||||
|
409)
|
||||||
|
|
||||||
ip_address = request.META['REMOTE_ADDR']
|
ip_address = request.META['REMOTE_ADDR']
|
||||||
keystone_user = request.keystone_user
|
keystone_user = request.keystone_user
|
||||||
@ -182,22 +184,27 @@ class TaskView(APIViewWithLogger):
|
|||||||
["Error: Something went wrong on the server. " +
|
["Error: Something went wrong on the server. " +
|
||||||
"It will be looked into shortly."]
|
"It will be looked into shortly."]
|
||||||
}
|
}
|
||||||
return response_dict
|
return response_dict, 200
|
||||||
|
|
||||||
# send initial conformation email:
|
# send initial conformation email:
|
||||||
email_conf = class_conf.get('emails', {}).get('initial', None)
|
email_conf = class_conf.get('emails', {}).get('initial', None)
|
||||||
send_email(task, email_conf)
|
send_email(task, email_conf)
|
||||||
|
|
||||||
return {'task': task}
|
return {'task': task}, 200
|
||||||
|
|
||||||
def approve(self, task):
|
def approve(self, request, task):
|
||||||
"""
|
"""
|
||||||
Approves the task and runs the post_approve steps.
|
Approves the task and runs the post_approve steps.
|
||||||
Will create a token if required, otherwise will run the
|
Will create a token if required, otherwise will run the
|
||||||
submit steps.
|
submit steps.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# We approve the task before running actions,
|
||||||
|
# that way if something goes wrong we know if it was approved,
|
||||||
|
# when it was approved, and who approved it.
|
||||||
task.approved = True
|
task.approved = True
|
||||||
task.approved_on = timezone.now()
|
task.approved_on = timezone.now()
|
||||||
|
task.approved_by = request.keystone_user
|
||||||
task.save()
|
task.save()
|
||||||
|
|
||||||
action_models = task.actions
|
action_models = task.actions
|
||||||
@ -320,11 +327,11 @@ class CreateProject(TaskView):
|
|||||||
|
|
||||||
task_type = "create_project"
|
task_type = "create_project"
|
||||||
|
|
||||||
default_actions = ["NewProject", ]
|
default_actions = ["NewProjectWithUser", ]
|
||||||
|
|
||||||
def post(self, request, format=None):
|
def post(self, request, format=None):
|
||||||
"""
|
"""
|
||||||
Unauthenticated endpoint bound primarily to NewProject.
|
Unauthenticated endpoint bound primarily to NewProjectWithUser.
|
||||||
|
|
||||||
This process requires approval, so this will validate
|
This process requires approval, so this will validate
|
||||||
incoming data and create a task to be approved
|
incoming data and create a task to be approved
|
||||||
@ -332,7 +339,16 @@ class CreateProject(TaskView):
|
|||||||
"""
|
"""
|
||||||
self.logger.info("(%s) - Starting new project task." %
|
self.logger.info("(%s) - Starting new project task." %
|
||||||
timezone.now())
|
timezone.now())
|
||||||
processed = self.process_actions(request)
|
|
||||||
|
class_conf = settings.TASK_SETTINGS.get(self.task_type, {})
|
||||||
|
|
||||||
|
# we need to set the region the resources will be created in:
|
||||||
|
request.data['region'] = class_conf.get('default_region')
|
||||||
|
|
||||||
|
# parent_id for new project, if null defaults to domain:
|
||||||
|
request.data['parent_id'] = class_conf.get('default_parent_id')
|
||||||
|
|
||||||
|
processed, status = self.process_actions(request)
|
||||||
|
|
||||||
errors = processed.get('errors', None)
|
errors = processed.get('errors', None)
|
||||||
if errors:
|
if errors:
|
||||||
@ -351,7 +367,7 @@ class CreateProject(TaskView):
|
|||||||
|
|
||||||
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
||||||
|
|
||||||
return Response(response_dict, status=200)
|
return Response(response_dict, status=status)
|
||||||
|
|
||||||
|
|
||||||
class InviteUser(TaskView):
|
class InviteUser(TaskView):
|
||||||
@ -383,19 +399,19 @@ class InviteUser(TaskView):
|
|||||||
# TODO: First check if the user already exists or is pending
|
# TODO: First check if the user already exists or is pending
|
||||||
# We should not allow duplicate invites.
|
# We should not allow duplicate invites.
|
||||||
|
|
||||||
processed = self.process_actions(request)
|
processed, status = self.process_actions(request)
|
||||||
|
|
||||||
errors = processed.get('errors', None)
|
errors = processed.get('errors', None)
|
||||||
if errors:
|
if errors:
|
||||||
self.logger.info("(%s) - Validation errors with task." %
|
self.logger.info("(%s) - Validation errors with task." %
|
||||||
timezone.now())
|
timezone.now())
|
||||||
return Response(errors, status=400)
|
return Response(errors, status=status)
|
||||||
|
|
||||||
task = processed['task']
|
task = processed['task']
|
||||||
self.logger.info("(%s) - AutoApproving AttachUser request."
|
self.logger.info("(%s) - AutoApproving AttachUser request."
|
||||||
% timezone.now())
|
% timezone.now())
|
||||||
|
|
||||||
response_dict, status = self.approve(task)
|
response_dict, status = self.approve(request, task)
|
||||||
|
|
||||||
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
||||||
|
|
||||||
@ -432,19 +448,19 @@ class ResetPassword(TaskView):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
self.logger.info("(%s) - New ResetUser request." % timezone.now())
|
self.logger.info("(%s) - New ResetUser request." % timezone.now())
|
||||||
processed = self.process_actions(request)
|
processed, status = self.process_actions(request)
|
||||||
|
|
||||||
errors = processed.get('errors', None)
|
errors = processed.get('errors', None)
|
||||||
if errors:
|
if errors:
|
||||||
self.logger.info("(%s) - Validation errors with task." %
|
self.logger.info("(%s) - Validation errors with task." %
|
||||||
timezone.now())
|
timezone.now())
|
||||||
return Response(errors, status=400)
|
return Response(errors, status=status)
|
||||||
|
|
||||||
task = processed['task']
|
task = processed['task']
|
||||||
self.logger.info("(%s) - AutoApproving Resetuser request."
|
self.logger.info("(%s) - AutoApproving Resetuser request."
|
||||||
% timezone.now())
|
% timezone.now())
|
||||||
|
|
||||||
self.approve(task)
|
self.approve(request, task)
|
||||||
response_dict = {'notes': [
|
response_dict = {'notes': [
|
||||||
"If user with email exists, reset token will be issued."]}
|
"If user with email exists, reset token will be issued."]}
|
||||||
|
|
||||||
@ -513,18 +529,18 @@ class EditUser(TaskView):
|
|||||||
post_approve validation, and creates a Token if valid.
|
post_approve validation, and creates a Token if valid.
|
||||||
"""
|
"""
|
||||||
self.logger.info("(%s) - New EditUser request." % timezone.now())
|
self.logger.info("(%s) - New EditUser request." % timezone.now())
|
||||||
processed = self.process_actions(request)
|
processed, status = self.process_actions(request)
|
||||||
|
|
||||||
errors = processed.get('errors', None)
|
errors = processed.get('errors', None)
|
||||||
if errors:
|
if errors:
|
||||||
self.logger.info("(%s) - Validation errors with task." %
|
self.logger.info("(%s) - Validation errors with task." %
|
||||||
timezone.now())
|
timezone.now())
|
||||||
return Response(errors, status=400)
|
return Response(errors, status=status)
|
||||||
|
|
||||||
task = processed['task']
|
task = processed['task']
|
||||||
self.logger.info("(%s) - AutoApproving EditUser request."
|
self.logger.info("(%s) - AutoApproving EditUser request."
|
||||||
% timezone.now())
|
% timezone.now())
|
||||||
response_dict, status = self.approve(task)
|
response_dict, status = self.approve(request, task)
|
||||||
|
|
||||||
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
add_task_id_for_roles(request, processed, response_dict, ['admin'])
|
||||||
|
|
||||||
|
@ -27,6 +27,10 @@ def setup_temp_cache(projects, users):
|
|||||||
|
|
||||||
users.update({admin_user.id: admin_user})
|
users.update({admin_user.id: admin_user})
|
||||||
|
|
||||||
|
region_one = mock.Mock()
|
||||||
|
region_one.id = 'region_id_0'
|
||||||
|
region_one.name = 'RegionOne'
|
||||||
|
|
||||||
global temp_cache
|
global temp_cache
|
||||||
|
|
||||||
temp_cache = {
|
temp_cache = {
|
||||||
@ -39,6 +43,9 @@ def setup_temp_cache(projects, users):
|
|||||||
'project_admin': 'project_admin',
|
'project_admin': 'project_admin',
|
||||||
'project_mod': 'project_mod',
|
'project_mod': 'project_mod',
|
||||||
'heat_stack_owner': 'heat_stack_owner'
|
'heat_stack_owner': 'heat_stack_owner'
|
||||||
|
},
|
||||||
|
'regions': {
|
||||||
|
'RegionOne': region_one,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +172,7 @@ class FakeManager(object):
|
|||||||
if project.id == project_id:
|
if project.id == project_id:
|
||||||
return FakeProject(project)
|
return FakeProject(project)
|
||||||
|
|
||||||
def create_project(self, project_name, created_on, p_id=None):
|
def create_project(self, project_name, created_on, parent=None, p_id=None):
|
||||||
global temp_cache
|
global temp_cache
|
||||||
project = mock.Mock()
|
project = mock.Mock()
|
||||||
if p_id:
|
if p_id:
|
||||||
@ -174,6 +181,18 @@ class FakeManager(object):
|
|||||||
temp_cache['i'] += 0.5
|
temp_cache['i'] += 0.5
|
||||||
project.id = "project_id_%s" % int(temp_cache['i'])
|
project.id = "project_id_%s" % int(temp_cache['i'])
|
||||||
project.name = project_name
|
project.name = project_name
|
||||||
|
# TODO(adriant): Do something better with the parent value.
|
||||||
|
project.parent = parent
|
||||||
project.roles = {}
|
project.roles = {}
|
||||||
temp_cache['projects'][project_name] = project
|
temp_cache['projects'][project_name] = project
|
||||||
return project
|
return project
|
||||||
|
|
||||||
|
def find_region(self, region_name):
|
||||||
|
global temp_cache
|
||||||
|
return temp_cache['regions'].get(region_name, None)
|
||||||
|
|
||||||
|
def get_region(self, region_id):
|
||||||
|
global temp_cache
|
||||||
|
for region in temp_cache['regions'].values():
|
||||||
|
if region.id == region_id:
|
||||||
|
return region
|
||||||
|
@ -59,6 +59,8 @@ class AdminAPITests(APITestCase):
|
|||||||
|
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'stacktask.actions.models.user_store.IdentityManager', FakeManager)
|
'stacktask.actions.models.user_store.IdentityManager', FakeManager)
|
||||||
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
|
FakeManager)
|
||||||
def test_task_get(self):
|
def test_task_get(self):
|
||||||
"""
|
"""
|
||||||
Test the basic task detail view.
|
Test the basic task detail view.
|
||||||
@ -260,7 +262,11 @@ class AdminAPITests(APITestCase):
|
|||||||
headers=headers)
|
headers=headers)
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
data = {'project_name': "test_project2", 'email': "test@example.com"}
|
data = {
|
||||||
|
'project_name': "test_project2",
|
||||||
|
'email': "test@example.com",
|
||||||
|
'region': 'RegionOne',
|
||||||
|
}
|
||||||
response = self.client.put(url, data, format='json',
|
response = self.client.put(url, data, format='json',
|
||||||
headers=headers)
|
headers=headers)
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
@ -844,6 +850,8 @@ class AdminAPITests(APITestCase):
|
|||||||
|
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'stacktask.actions.models.user_store.IdentityManager', FakeManager)
|
'stacktask.actions.models.user_store.IdentityManager', FakeManager)
|
||||||
|
@mock.patch('stacktask.actions.tenant_setup.models.IdentityManager',
|
||||||
|
FakeManager)
|
||||||
def test_task_list_filter(self):
|
def test_task_list_filter(self):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
|
@ -285,7 +285,10 @@ class TaskViewTests(APITestCase):
|
|||||||
response = self.client.post(url, {'approved': True}, format='json',
|
response = self.client.post(url, {'approved': True}, format='json',
|
||||||
headers=headers)
|
headers=headers)
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(response.data, {'errors': ['actions invalid']})
|
self.assertEqual(
|
||||||
|
response.data,
|
||||||
|
{'errors': ['Cannot approve an invalid task. ' +
|
||||||
|
'Update data and rerun pre_approve.']})
|
||||||
|
|
||||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||||
FakeManager)
|
FakeManager)
|
||||||
@ -522,7 +525,7 @@ class TaskViewTests(APITestCase):
|
|||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(response.data, {'notes': ['created token']})
|
self.assertEqual(response.data, {'notes': ['created token']})
|
||||||
response = self.client.post(url, data, format='json', headers=headers)
|
response = self.client.post(url, data, format='json', headers=headers)
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
|
||||||
|
|
||||||
data = {'email': "test2@example.com", 'roles': ["_member_"],
|
data = {'email': "test2@example.com", 'roles': ["_member_"],
|
||||||
'project_id': 'test_project_id'}
|
'project_id': 'test_project_id'}
|
||||||
@ -530,7 +533,7 @@ class TaskViewTests(APITestCase):
|
|||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(response.data, {'notes': ['created token']})
|
self.assertEqual(response.data, {'notes': ['created token']})
|
||||||
response = self.client.post(url, data, format='json', headers=headers)
|
response = self.client.post(url, data, format='json', headers=headers)
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
|
||||||
|
|
||||||
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
|
||||||
FakeManager)
|
FakeManager)
|
||||||
|
@ -215,8 +215,6 @@ class TaskDetail(APIViewWithLogger):
|
|||||||
"""
|
"""
|
||||||
Allows the updating of action data and retriggering
|
Allows the updating of action data and retriggering
|
||||||
of the pre_approve step.
|
of the pre_approve step.
|
||||||
|
|
||||||
Will undo task approval, and clear tokens for the task.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
task = Task.objects.get(uuid=uuid)
|
task = Task.objects.get(uuid=uuid)
|
||||||
@ -321,73 +319,121 @@ class TaskDetail(APIViewWithLogger):
|
|||||||
{'errors': ['No task with this id.']},
|
{'errors': ['No task with this id.']},
|
||||||
status=404)
|
status=404)
|
||||||
|
|
||||||
if request.data.get('approved', False) is True:
|
if request.data.get('approved') is not True:
|
||||||
|
return Response(
|
||||||
|
{'approved': ["this is a required boolean field."]},
|
||||||
|
status=400)
|
||||||
|
|
||||||
if task.completed:
|
if task.completed:
|
||||||
return Response(
|
return Response(
|
||||||
{'errors':
|
{'errors':
|
||||||
['This task has already been completed.']},
|
['This task has already been completed.']},
|
||||||
status=400)
|
status=400)
|
||||||
|
|
||||||
if task.cancelled:
|
if task.cancelled:
|
||||||
return Response(
|
return Response(
|
||||||
{'errors':
|
{'errors':
|
||||||
['This task has been cancelled.']},
|
['This task has been cancelled.']},
|
||||||
status=400)
|
status=400)
|
||||||
|
|
||||||
need_token = False
|
# we check that the task is valid before approving it:
|
||||||
valid = True
|
valid = True
|
||||||
|
for action in task.actions:
|
||||||
|
if not action.valid:
|
||||||
|
valid = False
|
||||||
|
|
||||||
actions = []
|
if not valid:
|
||||||
|
return Response(
|
||||||
|
{'errors':
|
||||||
|
['Cannot approve an invalid task. ' +
|
||||||
|
'Update data and rerun pre_approve.']},
|
||||||
|
status=400)
|
||||||
|
|
||||||
for action in task.actions:
|
# We approve the task before running actions,
|
||||||
act_model = action.get_action()
|
# that way if something goes wrong we know if it was approved,
|
||||||
actions.append(act_model)
|
# when it was approved, and who approved it last. Subsequent
|
||||||
|
# reapproval attempts overwrite previous approved_by/on.
|
||||||
|
task.approved = True
|
||||||
|
task.approved_by = request.keystone_user
|
||||||
|
task.approved_on = timezone.now()
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
need_token = False
|
||||||
|
valid = True
|
||||||
|
|
||||||
|
actions = []
|
||||||
|
|
||||||
|
for action in task.actions:
|
||||||
|
act_model = action.get_action()
|
||||||
|
actions.append(act_model)
|
||||||
|
try:
|
||||||
|
act_model.post_approve()
|
||||||
|
except Exception as e:
|
||||||
|
notes = {
|
||||||
|
'errors':
|
||||||
|
[("Error: '%s' while approving task. " +
|
||||||
|
"See task itself for details.") % e],
|
||||||
|
'task': task.uuid
|
||||||
|
}
|
||||||
|
create_notification(task, notes)
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
trace = traceback.format_exc()
|
||||||
|
self.logger.critical(("(%s) - Exception escaped! %s\n" +
|
||||||
|
"Trace: \n%s") %
|
||||||
|
(timezone.now(), e, trace))
|
||||||
|
|
||||||
|
return Response(notes, status=500)
|
||||||
|
|
||||||
|
if not action.valid:
|
||||||
|
valid = False
|
||||||
|
if action.need_token:
|
||||||
|
need_token = True
|
||||||
|
|
||||||
|
if valid:
|
||||||
|
if need_token:
|
||||||
|
token = create_token(task)
|
||||||
try:
|
try:
|
||||||
act_model.post_approve()
|
class_conf = settings.TASK_SETTINGS.get(
|
||||||
except Exception as e:
|
task.task_type, settings.DEFAULT_TASK_SETTINGS)
|
||||||
|
|
||||||
|
# will throw a key error if the token template has not
|
||||||
|
# been specified
|
||||||
|
email_conf = class_conf['emails']['token']
|
||||||
|
send_email(task, email_conf, token)
|
||||||
|
return Response({'notes': ['created token']},
|
||||||
|
status=200)
|
||||||
|
except KeyError as e:
|
||||||
notes = {
|
notes = {
|
||||||
'errors':
|
'errors':
|
||||||
[("Error: '%s' while approving task. " +
|
[("Error: '%s' while sending " +
|
||||||
"See task itself for details.") % e],
|
"token. See task " +
|
||||||
|
"itself for details.") % e],
|
||||||
'task': task.uuid
|
'task': task.uuid
|
||||||
}
|
}
|
||||||
create_notification(task, notes)
|
create_notification(task, notes)
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
trace = traceback.format_exc()
|
trace = traceback.format_exc()
|
||||||
self.logger.critical(("(%s) - Exception escaped! %s\n" +
|
self.logger.critical(("(%s) - Exception escaped!" +
|
||||||
"Trace: \n%s") %
|
" %s\n Trace: \n%s") %
|
||||||
(timezone.now(), e, trace))
|
(timezone.now(), e, trace))
|
||||||
|
|
||||||
return Response(notes, status=500)
|
response_dict = {
|
||||||
|
'errors':
|
||||||
if not action.valid:
|
["Error: Something went wrong on the " +
|
||||||
valid = False
|
"server. It will be looked into shortly."]
|
||||||
if action.need_token:
|
}
|
||||||
need_token = True
|
return Response(response_dict, status=500)
|
||||||
|
else:
|
||||||
if valid:
|
for action in actions:
|
||||||
task.approved = True
|
|
||||||
task.approved_on = timezone.now()
|
|
||||||
task.save()
|
|
||||||
if need_token:
|
|
||||||
token = create_token(task)
|
|
||||||
try:
|
try:
|
||||||
class_conf = settings.TASK_SETTINGS.get(
|
action.submit({})
|
||||||
task.task_type, settings.DEFAULT_TASK_SETTINGS)
|
except Exception as e:
|
||||||
|
|
||||||
# will throw a key error if the token template has not
|
|
||||||
# been specified
|
|
||||||
email_conf = class_conf['emails']['token']
|
|
||||||
send_email(task, email_conf, token)
|
|
||||||
return Response({'notes': ['created token']},
|
|
||||||
status=200)
|
|
||||||
except KeyError as e:
|
|
||||||
notes = {
|
notes = {
|
||||||
'errors':
|
'errors':
|
||||||
[("Error: '%s' while sending " +
|
[("Error: '%s' while submitting " +
|
||||||
"token. See task " +
|
"task. See task " +
|
||||||
"itself for details.") % e],
|
"itself for details.") % e],
|
||||||
'task': task.uuid
|
'task': task.uuid
|
||||||
}
|
}
|
||||||
@ -399,52 +445,23 @@ class TaskDetail(APIViewWithLogger):
|
|||||||
" %s\n Trace: \n%s") %
|
" %s\n Trace: \n%s") %
|
||||||
(timezone.now(), e, trace))
|
(timezone.now(), e, trace))
|
||||||
|
|
||||||
response_dict = {
|
return Response(notes, status=500)
|
||||||
'errors':
|
|
||||||
["Error: Something went wrong on the " +
|
|
||||||
"server. It will be looked into shortly."]
|
|
||||||
}
|
|
||||||
return Response(response_dict, status=500)
|
|
||||||
else:
|
|
||||||
for action in actions:
|
|
||||||
try:
|
|
||||||
action.submit({})
|
|
||||||
except Exception as e:
|
|
||||||
notes = {
|
|
||||||
'errors':
|
|
||||||
[("Error: '%s' while submitting " +
|
|
||||||
"task. See task " +
|
|
||||||
"itself for details.") % e],
|
|
||||||
'task': task.uuid
|
|
||||||
}
|
|
||||||
create_notification(task, notes)
|
|
||||||
|
|
||||||
import traceback
|
task.completed = True
|
||||||
trace = traceback.format_exc()
|
task.completed_on = timezone.now()
|
||||||
self.logger.critical(("(%s) - Exception escaped!" +
|
task.save()
|
||||||
" %s\n Trace: \n%s") %
|
|
||||||
(timezone.now(), e, trace))
|
|
||||||
|
|
||||||
return Response(notes, status=500)
|
# Sending confirmation email:
|
||||||
|
class_conf = settings.TASK_SETTINGS.get(
|
||||||
|
task.task_type, settings.DEFAULT_TASK_SETTINGS)
|
||||||
|
email_conf = class_conf.get(
|
||||||
|
'emails', {}).get('completed', None)
|
||||||
|
send_email(task, email_conf)
|
||||||
|
|
||||||
task.completed = True
|
return Response(
|
||||||
task.completed_on = timezone.now()
|
{'notes': "Task completed successfully."},
|
||||||
task.save()
|
status=200)
|
||||||
|
return Response({'errors': ['actions invalid']}, status=400)
|
||||||
# Sending confirmation email:
|
|
||||||
class_conf = settings.TASK_SETTINGS.get(
|
|
||||||
task.task_type, settings.DEFAULT_TASK_SETTINGS)
|
|
||||||
email_conf = class_conf.get(
|
|
||||||
'emails', {}).get('completed', None)
|
|
||||||
send_email(task, email_conf)
|
|
||||||
|
|
||||||
return Response(
|
|
||||||
{'notes': "Task completed successfully."},
|
|
||||||
status=200)
|
|
||||||
return Response({'errors': ['actions invalid']}, status=400)
|
|
||||||
else:
|
|
||||||
return Response({'approved': ["this field is required."]},
|
|
||||||
status=400)
|
|
||||||
|
|
||||||
@utils.mod_or_admin
|
@utils.mod_or_admin
|
||||||
def delete(self, request, uuid, format=None):
|
def delete(self, request, uuid, format=None):
|
||||||
|
@ -151,8 +151,6 @@ USERNAME_IS_EMAIL = CONFIG['USERNAME_IS_EMAIL']
|
|||||||
# Keystone admin credentials:
|
# Keystone admin credentials:
|
||||||
KEYSTONE = CONFIG['KEYSTONE']
|
KEYSTONE = CONFIG['KEYSTONE']
|
||||||
|
|
||||||
DEFAULT_REGION = CONFIG['DEFAULT_REGION']
|
|
||||||
|
|
||||||
TOKEN_SUBMISSION_URL = CONFIG['TOKEN_SUBMISSION_URL']
|
TOKEN_SUBMISSION_URL = CONFIG['TOKEN_SUBMISSION_URL']
|
||||||
|
|
||||||
TOKEN_EXPIRE_TIME = CONFIG['TOKEN_EXPIRE_TIME']
|
TOKEN_EXPIRE_TIME = CONFIG['TOKEN_EXPIRE_TIME']
|
||||||
|
@ -63,11 +63,9 @@ KEYSTONE = {
|
|||||||
'username': 'admin',
|
'username': 'admin',
|
||||||
'password': 'openstack',
|
'password': 'openstack',
|
||||||
'project_name': 'admin',
|
'project_name': 'admin',
|
||||||
'auth_url': "http://localhost:5000/v3"
|
'auth_url': "http://localhost:5000/v3",
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_REGION = 'RegionOne'
|
|
||||||
|
|
||||||
TOKEN_SUBMISSION_URL = 'http://localhost:8080/token/'
|
TOKEN_SUBMISSION_URL = 'http://localhost:8080/token/'
|
||||||
|
|
||||||
TOKEN_EXPIRE_TIME = 24
|
TOKEN_EXPIRE_TIME = 24
|
||||||
@ -120,10 +118,12 @@ TASK_SETTINGS = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'create_project': {
|
'create_project': {
|
||||||
'actions': [
|
'additional_actions': [
|
||||||
'AddAdminToProject',
|
'AddDefaultUsersToProject',
|
||||||
'DefaultProjectResources'
|
'NewProjectDefaultNetwork'
|
||||||
]
|
],
|
||||||
|
'default_region': 'RegionOne',
|
||||||
|
'default_parent_id': None,
|
||||||
},
|
},
|
||||||
'reset_password': {
|
'reset_password': {
|
||||||
'handle_duplicates': 'cancel',
|
'handle_duplicates': 'cancel',
|
||||||
@ -154,13 +154,18 @@ TASK_SETTINGS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ACTION_SETTINGS = {
|
ACTION_SETTINGS = {
|
||||||
|
'NewProject': {
|
||||||
|
'default_roles': {
|
||||||
|
"project_admin", "project_mod", "_member_", "heat_stack_owner"
|
||||||
|
},
|
||||||
|
},
|
||||||
'NewUser': {
|
'NewUser': {
|
||||||
'allowed_roles': ['project_mod', 'project_admin', "_member_"]
|
'allowed_roles': ['project_mod', 'project_admin', "_member_"]
|
||||||
},
|
},
|
||||||
'ResetUser': {
|
'ResetUser': {
|
||||||
'blacklisted_roles': ['admin']
|
'blacklisted_roles': ['admin']
|
||||||
},
|
},
|
||||||
'DefaultProjectResources': {
|
'NewDefaultNetwork': {
|
||||||
'RegionOne': {
|
'RegionOne': {
|
||||||
'DNS_NAMESERVERS': ['193.168.1.2', '193.168.1.3'],
|
'DNS_NAMESERVERS': ['193.168.1.2', '193.168.1.3'],
|
||||||
'SUBNET_CIDR': '192.168.1.0/24',
|
'SUBNET_CIDR': '192.168.1.0/24',
|
||||||
@ -169,7 +174,16 @@ ACTION_SETTINGS = {
|
|||||||
'router_name': 'somerouter',
|
'router_name': 'somerouter',
|
||||||
'subnet_name': 'somesubnet'
|
'subnet_name': 'somesubnet'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'AddDefaultUsersToProject': {
|
||||||
|
'default_users': [
|
||||||
|
'admin',
|
||||||
|
],
|
||||||
|
'default_roles': [
|
||||||
|
'admin',
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ROLES_MAPPING = {
|
ROLES_MAPPING = {
|
||||||
@ -195,7 +209,6 @@ conf_dict = {
|
|||||||
"EMAIL_SETTINGS": EMAIL_SETTINGS,
|
"EMAIL_SETTINGS": EMAIL_SETTINGS,
|
||||||
"USERNAME_IS_EMAIL": USERNAME_IS_EMAIL,
|
"USERNAME_IS_EMAIL": USERNAME_IS_EMAIL,
|
||||||
"KEYSTONE": KEYSTONE,
|
"KEYSTONE": KEYSTONE,
|
||||||
"DEFAULT_REGION": DEFAULT_REGION,
|
|
||||||
"ACTIVE_TASKVIEWS": ACTIVE_TASKVIEWS,
|
"ACTIVE_TASKVIEWS": ACTIVE_TASKVIEWS,
|
||||||
"DEFAULT_TASK_SETTINGS": DEFAULT_TASK_SETTINGS,
|
"DEFAULT_TASK_SETTINGS": DEFAULT_TASK_SETTINGS,
|
||||||
"TASK_SETTINGS": TASK_SETTINGS,
|
"TASK_SETTINGS": TASK_SETTINGS,
|
||||||
|
@ -40,8 +40,8 @@ conf = {
|
|||||||
'username': settings.KEYSTONE['username'],
|
'username': settings.KEYSTONE['username'],
|
||||||
'password': settings.KEYSTONE['password'],
|
'password': settings.KEYSTONE['password'],
|
||||||
'project_name': settings.KEYSTONE['project_name'],
|
'project_name': settings.KEYSTONE['project_name'],
|
||||||
"project_domain_name": settings.KEYSTONE.get('domain_name', "default"),
|
"project_domain_id": settings.KEYSTONE.get('domain_id', "default"),
|
||||||
"user_domain_name": settings.KEYSTONE.get('domain_name', "default"),
|
"user_domain_id": settings.KEYSTONE.get('domain_id', "default"),
|
||||||
"auth_url": settings.KEYSTONE['auth_url'],
|
"auth_url": settings.KEYSTONE['auth_url'],
|
||||||
'delay_auth_decision': True,
|
'delay_auth_decision': True,
|
||||||
'include_service_catalog': False,
|
'include_service_catalog': False,
|
||||||
|
Loading…
Reference in New Issue
Block a user