diff --git a/adjutant/actions/migrations/0001_initial.py b/adjutant/actions/migrations/0001_initial.py index d737692..767e547 100644 --- a/adjutant/actions/migrations/0001_initial.py +++ b/adjutant/actions/migrations/0001_initial.py @@ -9,23 +9,36 @@ import jsonfield.fields class Migration(migrations.Migration): dependencies = [ - ('api', '0001_initial'), + ("api", "0001_initial"), ] operations = [ migrations.CreateModel( - name='Action', + name="Action", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('action_name', models.CharField(max_length=200)), - ('action_data', jsonfield.fields.JSONField(default={})), - ('cache', jsonfield.fields.JSONField(default={})), - ('state', models.CharField(default=b'default', max_length=200)), - ('valid', models.BooleanField(default=False)), - ('need_token', models.BooleanField(default=False)), - ('order', models.IntegerField()), - ('created', models.DateTimeField(default=django.utils.timezone.now)), - ('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Task')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("action_name", models.CharField(max_length=200)), + ("action_data", jsonfield.fields.JSONField(default={})), + ("cache", jsonfield.fields.JSONField(default={})), + ("state", models.CharField(default=b"default", max_length=200)), + ("valid", models.BooleanField(default=False)), + ("need_token", models.BooleanField(default=False)), + ("order", models.IntegerField()), + ("created", models.DateTimeField(default=django.utils.timezone.now)), + ( + "task", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.Task" + ), + ), ], ), ] diff --git a/adjutant/actions/migrations/0002_action_auto_approve.py b/adjutant/actions/migrations/0002_action_auto_approve.py index e920ea2..b254172 100644 --- a/adjutant/actions/migrations/0002_action_auto_approve.py +++ b/adjutant/actions/migrations/0002_action_auto_approve.py @@ -7,13 +7,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('actions', '0001_initial'), + ("actions", "0001_initial"), ] operations = [ migrations.AddField( - model_name='action', - name='auto_approve', + model_name="action", + name="auto_approve", field=models.NullBooleanField(default=None), ), ] diff --git a/adjutant/actions/migrations/0003_auto_20190610_0205.py b/adjutant/actions/migrations/0003_auto_20190610_0205.py index ab4e0d2..12c7989 100644 --- a/adjutant/actions/migrations/0003_auto_20190610_0205.py +++ b/adjutant/actions/migrations/0003_auto_20190610_0205.py @@ -8,13 +8,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('actions', '0002_action_auto_approve'), + ("actions", "0002_action_auto_approve"), ] operations = [ migrations.AlterField( - model_name='action', - name='state', - field=models.CharField(default='default', max_length=200), + model_name="action", + name="state", + field=models.CharField(default="default", max_length=200), ), ] diff --git a/adjutant/actions/migrations/0004_auto_20190610_0209.py b/adjutant/actions/migrations/0004_auto_20190610_0209.py index eb02256..602bc27 100644 --- a/adjutant/actions/migrations/0004_auto_20190610_0209.py +++ b/adjutant/actions/migrations/0004_auto_20190610_0209.py @@ -9,14 +9,16 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('tasks', '0001_initial'), - ('actions', '0003_auto_20190610_0205'), + ("tasks", "0001_initial"), + ("actions", "0003_auto_20190610_0205"), ] operations = [ migrations.AlterField( - model_name='action', - name='task', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tasks.Task'), + model_name="action", + name="task", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="tasks.Task" + ), ), ] diff --git a/adjutant/actions/models.py b/adjutant/actions/models.py index 036b601..f260f67 100644 --- a/adjutant/actions/models.py +++ b/adjutant/actions/models.py @@ -24,13 +24,14 @@ class Action(models.Model): """ Database model representation of an action. """ + action_name = models.CharField(max_length=200) action_data = JSONField(default={}) cache = JSONField(default={}) state = models.CharField(max_length=200, default="default") valid = models.BooleanField(default=False) need_token = models.BooleanField(default=False) - task = models.ForeignKey('tasks.Task', on_delete=models.CASCADE) + task = models.ForeignKey("tasks.Task", on_delete=models.CASCADE) # NOTE(amelia): Auto approve is technically a ternary operator # If all in a task are None it will not auto approve # However if at least one action has it set to True it @@ -46,5 +47,4 @@ class Action(models.Model): def get_action(self): """Returns self as the appropriate action wrapper type.""" data = self.action_data - return actions.ACTION_CLASSES[self.action_name]( - data=data, action_model=self) + return actions.ACTION_CLASSES[self.action_name](data=data, action_model=self) diff --git a/adjutant/actions/utils.py b/adjutant/actions/utils.py index 5870618..da900e2 100644 --- a/adjutant/actions/utils.py +++ b/adjutant/actions/utils.py @@ -30,7 +30,7 @@ def send_email(to_addresses, context, conf, task): Function for sending emails from actions """ - if not conf.get('template'): + if not conf.get("template"): return if not to_addresses: @@ -40,66 +40,58 @@ def send_email(to_addresses, context, conf, task): elif isinstance(to_addresses, set): to_addresses = list(to_addresses) - text_template = loader.get_template( - conf['template'], - using='include_etc_templates') + text_template = loader.get_template(conf["template"], using="include_etc_templates") - html_template = conf.get('html_template') + html_template = conf.get("html_template") if html_template: html_template = loader.get_template( - html_template, - using='include_etc_templates') + html_template, using="include_etc_templates" + ) try: message = text_template.render(context) # from_email is the return-path and is distinct from the # message headers - from_email = conf.get('from') + from_email = conf.get("from") if not from_email: - from_email = conf.get('reply') + from_email = conf.get("reply") if not from_email: return elif "%(task_uuid)s" in from_email: - from_email = from_email % {'task_uuid': task.uuid} + from_email = from_email % {"task_uuid": task.uuid} - reply_email = conf['reply'] + reply_email = conf["reply"] # these are the message headers which will be visible to # the email client. headers = { - 'X-Adjutant-Task-UUID': task.uuid, + "X-Adjutant-Task-UUID": task.uuid, # From needs to be set to be distinct from return-path - 'From': reply_email, - 'Reply-To': reply_email, + "From": reply_email, + "Reply-To": reply_email, } email = EmailMultiAlternatives( - conf['subject'], - message, - from_email, - to_addresses, - headers=headers, + conf["subject"], message, from_email, to_addresses, headers=headers, ) if html_template: - email.attach_alternative( - html_template.render(context), "text/html") + email.attach_alternative(html_template.render(context), "text/html") email.send(fail_silently=False) return True except Exception as e: notes = { - 'errors': - ("Error: '%s' while sending additional email for task: %s" % - (e, task.uuid)) + "errors": ( + "Error: '%s' while sending additional email for task: %s" + % (e, task.uuid) + ) } notif_conf = task.config.notifications if e.__class__.__name__ in notif_conf.safe_errors: - notification = create_notification( - task, notes, error=True, - handlers=False) + notification = create_notification(task, notes, error=True, handlers=False) notification.acknowledged = True notification.save() else: diff --git a/adjutant/actions/v1/base.py b/adjutant/actions/v1/base.py index a720d56..b69ae9f 100644 --- a/adjutant/actions/v1/base.py +++ b/adjutant/actions/v1/base.py @@ -64,15 +64,14 @@ class BaseAction(object): config_group = None - def __init__(self, data, action_model=None, task=None, - order=None): + def __init__(self, data, action_model=None, task=None, order=None): """ Build itself around an existing database model, or build itself and creates a new database model. Sets up required data as fields. """ - self.logger = getLogger('adjutant') + self.logger = getLogger("adjutant") for field in self.required: field_data = data[field] @@ -86,7 +85,7 @@ class BaseAction(object): action_name=self.__class__.__name__, action_data=data, task=task, - order=order + order=order, ) action.save() self.action = action @@ -141,8 +140,7 @@ class BaseAction(object): now = timezone.now() self.logger.info("(%s) - %s" % (now, note)) note = "%s - (%s)" % (note, now) - self.action.task.add_action_note( - str(self), note) + self.action.task.add_action_note(str(self), note) @property def config(self): @@ -154,8 +152,7 @@ class BaseAction(object): return self._config try: - action_defaults = CONF.workflow.action_defaults.get( - self.__class__.__name__) + action_defaults = CONF.workflow.action_defaults.get(self.__class__.__name__) except KeyError: self._config = {} return self._config @@ -163,7 +160,8 @@ class BaseAction(object): try: task_conf = CONF.workflow.tasks[self.action.task.task_type] self._config = action_defaults.overlay( - task_conf.actions[self.__class__.__name__]) + task_conf.actions[self.__class__.__name__] + ) except KeyError: self._config = action_defaults return self._config @@ -174,7 +172,8 @@ class BaseAction(object): except NotImplementedError: self.logger.warning( "DEPRECATED: Action '_pre_approve' stage has been renamed " - "to 'prepare'.") + "to 'prepare'." + ) return self._pre_approve() def approve(self): @@ -183,7 +182,8 @@ class BaseAction(object): except NotImplementedError: self.logger.warning( "DEPRECATED: Action '_post_approve' stage has been renamed " - "to 'prepare'.") + "to 'prepare'." + ) return self._post_approve() def submit(self, token_data): @@ -208,16 +208,16 @@ class ResourceMixin(object): def _validate_keystone_user_project_id(self): keystone_user = self.action.task.keystone_user - if keystone_user['project_id'] != self.project_id: - self.add_note('Project id does not match keystone user project.') + if keystone_user["project_id"] != self.project_id: + self.add_note("Project id does not match keystone user project.") return False return True def _validate_keystone_user_domain_id(self): keystone_user = self.action.task.keystone_user - if keystone_user['project_domain_id'] != self.domain_id: - self.add_note('Domain id does not match keystone user domain.') + if keystone_user["project_domain_id"] != self.domain_id: + self.add_note("Domain id does not match keystone user domain.") return False return True @@ -225,7 +225,7 @@ class ResourceMixin(object): id_manager = user_store.IdentityManager() domain = id_manager.get_domain(self.domain_id) if not domain: - self.add_note('Domain does not exist.') + self.add_note("Domain does not exist.") return False return True @@ -234,24 +234,23 @@ class ResourceMixin(object): # Handle an edge_case where some actions set their # own project_id value. if not self.project_id: - self.add_note('No project_id given.') + self.add_note("No project_id given.") return False # Now actually check the project exists. id_manager = user_store.IdentityManager() project = id_manager.get_project(self.project_id) if not project: - self.add_note('Project with id %s does not exist.' % - self.project_id) + self.add_note("Project with id %s does not exist." % self.project_id) return False - self.add_note('Project with id %s exists.' % self.project_id) + self.add_note("Project with id %s exists." % self.project_id) return True def _validate_domain_name(self): id_manager = user_store.IdentityManager() self.domain = id_manager.find_domain(self.domain_name) if not self.domain: - self.add_note('Domain does not exist.') + self.add_note("Domain does not exist.") return False # also store the domain_id separately for later use self.domain_id = self.domain.id @@ -262,9 +261,9 @@ class ResourceMixin(object): id_manager = user_store.IdentityManager() v_region = id_manager.get_region(region) if not v_region: - self.add_note('ERROR: Region: %s does not exist.' % region) + self.add_note("ERROR: Region: %s does not exist." % region) return False - self.add_note('Region: %s exists.' % region) + self.add_note("Region: %s exists." % region) return True @@ -278,16 +277,17 @@ class UserMixin(ResourceMixin): self.user = id_manager.find_user(self.username, self.domain.id) if not self.user: - self.add_note('No user present with username') + self.add_note("No user present with username") return False return True def _validate_role_permissions(self): keystone_user = self.action.task.keystone_user # Role permissions check - if not self.are_roles_manageable(user_roles=keystone_user['roles'], - requested_roles=self.roles): - self.add_note('User does not have permission to edit role(s).') + if not self.are_roles_manageable( + user_roles=keystone_user["roles"], requested_roles=self.roles + ): + self.add_note("User does not have permission to edit role(s).") return False return True @@ -299,7 +299,7 @@ class UserMixin(ResourceMixin): requested_roles = set(requested_roles) # blacklist checks - blacklisted_roles = set(['admin']) + blacklisted_roles = set(["admin"]) if len(blacklisted_roles & requested_roles) > 0: return False @@ -317,15 +317,16 @@ class UserMixin(ResourceMixin): # Mutators def grant_roles(self, user, roles, project_id, inherited=False): return self._user_roles_edit( - user, roles, project_id, remove=False, inherited=inherited) + user, roles, project_id, remove=False, inherited=inherited + ) def remove_roles(self, user, roles, project_id, inherited=False): return self._user_roles_edit( - user, roles, project_id, remove=True, inherited=inherited) + user, roles, project_id, remove=True, inherited=inherited + ) # Helper function to add or remove roles - def _user_roles_edit(self, user, roles, project_id, remove=False, - inherited=False): + def _user_roles_edit(self, user, roles, project_id, remove=False, inherited=False): id_manager = user_store.IdentityManager() if not remove: action_fn = id_manager.add_user_role @@ -346,8 +347,9 @@ class UserMixin(ResourceMixin): action_fn(user, role, project_id, inherited=inherited) except Exception as e: self.add_note( - "Error: '%s' while %s the roles: %s on user: %s " % - (e, action_string, roles, user)) + "Error: '%s' while %s the roles: %s on user: %s " + % (e, action_string, roles, user) + ) raise def enable_user(self, user=None): @@ -358,22 +360,27 @@ class UserMixin(ResourceMixin): id_manager.enable_user(user) except Exception as e: self.add_note( - "Error: '%s' while re-enabling user: %s with roles: %s" % - (e, self.username, self.roles)) + "Error: '%s' while re-enabling user: %s with roles: %s" + % (e, self.username, self.roles) + ) raise def create_user(self, password): id_manager = user_store.IdentityManager() try: user = id_manager.create_user( - name=self.username, password=password, - email=self.email, domain=self.domain_id, - created_on=str_datetime(timezone.now())) + name=self.username, + password=password, + email=self.email, + domain=self.domain_id, + created_on=str_datetime(timezone.now()), + ) except Exception as e: # TODO: Narrow the Exceptions caught to a relevant set. self.add_note( - "Error: '%s' while creating user: %s with roles: %s" % - (e, self.username, self.roles)) + "Error: '%s' while creating user: %s with roles: %s" + % (e, self.username, self.roles) + ) raise return user @@ -385,8 +392,8 @@ class UserMixin(ResourceMixin): id_manager.update_user_password(user, password) except Exception as e: self.add_note( - "Error: '%s' while changing password for user: %s" % - (e, self.username)) + "Error: '%s' while changing password for user: %s" % (e, self.username) + ) raise def update_email(self, email, user=None): @@ -397,8 +404,8 @@ class UserMixin(ResourceMixin): id_manager.update_user_email(user, email) except Exception as e: self.add_note( - "Error: '%s' while changing email for user: %s" % - (e, self.username)) + "Error: '%s' while changing email for user: %s" % (e, self.username) + ) raise def update_user_name(self, username, user=None): @@ -409,8 +416,8 @@ class UserMixin(ResourceMixin): id_manager.update_user_name(user, username) except Exception as e: self.add_note( - "Error: '%s' while changing username for user: %s" % - (e, self.username)) + "Error: '%s' while changing username for user: %s" % (e, self.username) + ) raise @@ -424,22 +431,18 @@ class ProjectMixin(ResourceMixin): if self.parent_id: parent = id_manager.get_project(self.parent_id) if not parent: - self.add_note("Parent id: '%s' does not exist." % - self.parent_id) + self.add_note("Parent id: '%s' does not exist." % self.parent_id) return False return True def _validate_project_absent(self): id_manager = user_store.IdentityManager() - project = id_manager.find_project( - self.project_name, self.domain_id) + project = id_manager.find_project(self.project_name, self.domain_id) if project: - self.add_note("Existing project with name '%s'." % - self.project_name) + self.add_note("Existing project with name '%s'." % self.project_name) return False - self.add_note("No existing project with name '%s'." % - self.project_name) + self.add_note("No existing project with name '%s'." % self.project_name) return True def _create_project(self): @@ -447,17 +450,20 @@ class ProjectMixin(ResourceMixin): description = getattr(self, "description", "") try: project = id_manager.create_project( - self.project_name, created_on=str_datetime(timezone.now()), - parent=self.parent_id, domain=self.domain_id, - description=description) + self.project_name, + created_on=str_datetime(timezone.now()), + parent=self.parent_id, + domain=self.domain_id, + description=description, + ) except Exception as e: self.add_note( - "Error: '%s' while creating project: %s" % - (e, self.project_name)) + "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.action.task.cache["project_id"] = project.id + self.set_cache("project_id", project.id) self.add_note("New project '%s' created." % project.name) @@ -477,7 +483,8 @@ class QuotaMixin(ResourceMixin): def _usage_greater_than_quota(self, regions): quota_manager = QuotaManager( self.project_id, - size_difference_threshold=self.config.size_difference_threshold) + size_difference_threshold=self.config.size_difference_threshold, + ) quota = CONF.quota.sizes.get(self.size, {}) for region in regions: current_usage = quota_manager.get_current_usage(region) @@ -500,7 +507,6 @@ class QuotaMixin(ResourceMixin): class UserIdAction(BaseAction): - def _get_target_user(self): """ Gets the target user by id @@ -522,7 +528,7 @@ class UserNameAction(BaseAction): # NOTE(amelia): Make a copy to avoid editing it globally. self.required = list(self.required) try: - self.required.remove('username') + self.required.remove("username") except ValueError: pass # nothing to remove diff --git a/adjutant/actions/v1/misc.py b/adjutant/actions/v1/misc.py index bbee82b..b634cba 100644 --- a/adjutant/actions/v1/misc.py +++ b/adjutant/actions/v1/misc.py @@ -32,35 +32,40 @@ def _build_default_email_group(group_name): fields.StrConfig( "subject", help_text="Email subject for this stage.", - default="Openstack Email Notification") + default="Openstack Email Notification", + ) ) email_group.register_child_config( fields.StrConfig( "from", help_text="From email for this stage.", regex=constants.EMAIL_WITH_TEMPLATE_REGEX, - default="bounce+%(task_uuid)s@example.com") + default="bounce+%(task_uuid)s@example.com", + ) ) email_group.register_child_config( fields.StrConfig( "reply", help_text="Reply-to email for this stage.", regex=constants.EMAIL_WITH_TEMPLATE_REGEX, - default="no-reply@example.com") + default="no-reply@example.com", + ) ) email_group.register_child_config( fields.StrConfig( "template", help_text="Email template for this stage. " - "No template will cause the email not to send.", - default=None) + "No template will cause the email not to send.", + default=None, + ) ) email_group.register_child_config( fields.StrConfig( "html_template", help_text="Email html template for this stage. " - "No template will cause the email not to send.", - default=None) + "No template will cause the email not to send.", + default=None, + ) ) email_group.register_child_config( fields.BoolConfig( @@ -108,24 +113,27 @@ class SendAdditionalEmailAction(BaseAction): def set_email(self, conf): self.emails = set() - if conf.get('email_current_user'): + if conf.get("email_current_user"): self.add_note("Adding the current user's email address") if CONF.identity.username_is_email: - self.emails.add(self.action.task.keystone_user['username']) + self.emails.add(self.action.task.keystone_user["username"]) else: try: id_manager = user_store.IdentityManager() email = id_manager.get_user( - self.action.task.keystone_user['user_id']).email + self.action.task.keystone_user["user_id"] + ).email self.emails.add(email) except AttributeError: self.add_note("Could not add current user email address") - if conf.get('email_roles'): - roles = set(conf.get('email_roles')) - project_id = self.action.task.keystone_user['project_id'] - self.add_note('Adding email addresses for roles %s in project %s' - % (roles, project_id)) + if conf.get("email_roles"): + roles = set(conf.get("email_roles")) + project_id = self.action.task.keystone_user["project_id"] + self.add_note( + "Adding email addresses for roles %s in project %s" + % (roles, project_id) + ) id_manager = user_store.IdentityManager() users = id_manager.list_users(project_id) @@ -137,14 +145,14 @@ class SendAdditionalEmailAction(BaseAction): else: self.emails.add(user.email) - if conf.get('email_task_cache'): - task_emails = self.action.task.cache.get('additional_emails', []) + if conf.get("email_task_cache"): + task_emails = self.action.task.cache.get("additional_emails", []) if isinstance(task_emails, six.string_types): task_emails = [task_emails] for email in task_emails: self.emails.add(email) - for email in conf.get('email_additional_addresses'): + for email in conf.get("email_additional_addresses"): self.emails.add(email) def _validate(self): @@ -152,13 +160,13 @@ class SendAdditionalEmailAction(BaseAction): self.action.save() def _prepare(self): - self.perform_action('prepare') + self.perform_action("prepare") def _approve(self): - self.perform_action('approve') + self.perform_action("approve") def _submit(self, data): - self.perform_action('submit') + self.perform_action("submit") def perform_action(self, stage): self._validate() @@ -171,7 +179,7 @@ class SendAdditionalEmailAction(BaseAction): email_conf = self.config.get(stage) # If either of these are false we won't be sending anything. - if not email_conf or not email_conf.get('template'): + if not email_conf or not email_conf.get("template"): return self.set_email(email_conf) @@ -188,10 +196,7 @@ class SendAdditionalEmailAction(BaseAction): act = action.get_action() actions[str(act)] = act - context = { - 'task': task, - 'actions': actions - } + context = {"task": task, "actions": actions} result = send_email(self.emails, context, email_conf, task) diff --git a/adjutant/actions/v1/projects.py b/adjutant/actions/v1/projects.py index 283f141..2e2faf1 100644 --- a/adjutant/actions/v1/projects.py +++ b/adjutant/actions/v1/projects.py @@ -23,8 +23,7 @@ from adjutant.config import CONF from adjutant.common import user_store from adjutant.common.utils import str_datetime from adjutant.actions.utils import validate_steps -from adjutant.actions.v1.base import ( - BaseAction, UserNameAction, UserMixin, ProjectMixin) +from adjutant.actions.v1.base import BaseAction, UserNameAction, UserMixin, ProjectMixin from adjutant.actions.v1 import serializers @@ -36,10 +35,10 @@ class NewProjectAction(BaseAction, ProjectMixin, UserMixin): """ required = [ - 'domain_id', - 'parent_id', - 'project_name', - 'description', + "domain_id", + "parent_id", + "project_name", + "description", ] serializer = serializers.NewProjectSerializer @@ -50,7 +49,7 @@ class NewProjectAction(BaseAction, ProjectMixin, UserMixin): "default_roles", help_text="Roles to be given on project to the creating user.", default=[], - sample_default=["member", "project_admin"] + sample_default=["member", "project_admin"], ), ], ) @@ -59,18 +58,20 @@ class NewProjectAction(BaseAction, ProjectMixin, UserMixin): super(NewProjectAction, self).__init__(*args, **kwargs) def _validate(self): - self.action.valid = validate_steps([ - self._validate_domain_id, - self._validate_keystone_user_parent_project, - self._validate_project_absent, - ]) + self.action.valid = validate_steps( + [ + self._validate_domain_id, + self._validate_keystone_user_parent_project, + self._validate_project_absent, + ] + ) self.action.save() def _validate_domain_id(self): keystone_user = self.action.task.keystone_user - if keystone_user['project_domain_id'] != self.domain_id: - self.add_note('Domain id does not match keystone user domain.') + if keystone_user["project_domain_id"] != self.domain_id: + self.add_note("Domain id does not match keystone user domain.") return False return super(NewProjectAction, self)._validate_domain_id() @@ -79,9 +80,8 @@ class NewProjectAction(BaseAction, ProjectMixin, UserMixin): 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.') + if self.parent_id != keystone_user["project_id"]: + self.add_note("Parent id does not match keystone user project.") return False return self._validate_parent_project() return True @@ -90,9 +90,9 @@ class NewProjectAction(BaseAction, ProjectMixin, UserMixin): self._validate() def _approve(self): - project_id = self.get_cache('project_id') + project_id = self.get_cache("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.") else: self._validate() @@ -102,34 +102,38 @@ class NewProjectAction(BaseAction, ProjectMixin, UserMixin): self._create_project() - user_id = self.get_cache('user_id') + user_id = self.get_cache("user_id") if user_id: - self.action.task.cache['user_id'] = user_id + self.action.task.cache["user_id"] = user_id self.add_note("User already given roles.") else: default_roles = self.config.default_roles - project_id = self.get_cache('project_id') + project_id = self.get_cache("project_id") keystone_user = self.action.task.keystone_user try: id_manager = user_store.IdentityManager() - user = id_manager.get_user(keystone_user['user_id']) + user = 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, default_roles, user.name, project_id)) + ( + "Error: '%s' while adding roles %s " + "to user '%s' on project '%s'" + ) + % (e, default_roles, user.name, 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.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" - % (user.name, project_id, default_roles)) + % (user.name, project_id, default_roles) + ) def _submit(self, token_data): """ @@ -144,13 +148,7 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): doesn't exists. """ - required = [ - 'domain_id', - 'parent_id', - 'project_name', - 'username', - 'email' - ] + required = ["domain_id", "parent_id", "project_name", "username", "email"] serializer = serializers.NewProjectWithUserSerializer @@ -160,7 +158,7 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): "default_roles", help_text="Roles to be given on project for the user.", default=[], - sample_default=["member", "project_admin"] + sample_default=["member", "project_admin"], ), ], ) @@ -169,12 +167,14 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): super(NewProjectWithUserAction, self).__init__(*args, **kwargs) def _validate(self): - self.action.valid = validate_steps([ - self._validate_domain_id, - self._validate_parent_project, - self._validate_project_absent, - self._validate_user, - ]) + self.action.valid = validate_steps( + [ + self._validate_domain_id, + self._validate_parent_project, + self._validate_project_absent, + self._validate_user, + ] + ) self.action.save() def _validate_user(self): @@ -184,36 +184,40 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): if not user: self.add_note( "No user present with username '%s'. " - "Need to create new user." % self.username) + "Need to create new user." % self.username + ) if not id_manager.can_edit_users: self.add_note( - 'Identity backend does not support user editing, ' - 'cannot create new user.') + "Identity backend does not support user editing, " + "cannot create new user." + ) return False # add to cache to use in template - self.action.task.cache['user_state'] = "default" + self.action.task.cache["user_state"] = "default" self.action.need_token = True self.set_token_fields(["password"]) return True - if (not CONF.identity.username_is_email - and getattr(user, 'email', None) != self.email): - self.add_note("Existing user '%s' with non-matching email." % - self.username) + if ( + not CONF.identity.username_is_email + and getattr(user, "email", None) != self.email + ): + self.add_note("Existing user '%s' with non-matching email." % self.username) return False if not user.enabled: self.add_note( - "Existing disabled user '%s' with matching email." % - self.email) + "Existing disabled user '%s' with matching email." % self.email + ) if not id_manager.can_edit_users: self.add_note( - 'Identity backend does not support user editing, ' - 'cannot renable user.') + "Identity backend does not support user editing, " + "cannot renable user." + ) return False self.action.state = "disabled" # add to cache to use in template - self.action.task.cache['user_state'] = "disabled" + self.action.task.cache["user_state"] = "disabled" self.action.need_token = True # as they are disabled we'll reset their password self.set_token_fields(["password"]) @@ -221,15 +225,14 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): else: self.action.state = "existing" # add to cache to use in template - self.action.task.cache['user_state'] = "existing" + self.action.task.cache["user_state"] = "existing" self.action.need_token = False - self.add_note("Existing user '%s' with matching email." % - self.email) + self.add_note("Existing user '%s' with matching email." % self.email) return True def _validate_user_submit(self): - user_id = self.get_cache('user_id') - project_id = self.get_cache('project_id') + user_id = self.get_cache("user_id") + project_id = self.get_cache("project_id") id_manager = user_store.IdentityManager() @@ -241,7 +244,7 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): else: self.action.valid = False - self.action.task.cache['user_state'] = self.action.state + self.action.task.cache["user_state"] = self.action.state self.action.save() @@ -257,15 +260,16 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): if not self.valid: return - project_id = self.get_cache('project_id') + project_id = self.get_cache("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.") else: self.action.valid = ( self._validate_domain_id() and self._validate_parent_project() - and self._validate_project_absent()) + and self._validate_project_absent() + ) self.action.save() if not self.valid: @@ -274,11 +278,11 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): self._create_project() # User validation and checks - user_id = self.get_cache('user_id') - roles_granted = self.get_cache('roles_granted') + user_id = self.get_cache("user_id") + roles_granted = self.get_cache("roles_granted") if user_id and roles_granted: - self.action.task.cache['user_id'] = user_id - self.action.task.cache['user_state'] = self.action.state + self.action.task.cache["user_id"] = user_id + self.action.task.cache["user_state"] = self.action.state self.add_note("User already setup.") elif not user_id: self.action.valid = self._validate_user() @@ -295,60 +299,66 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): id_manager = user_store.IdentityManager() default_roles = self.config.default_roles - project_id = self.get_cache('project_id') + project_id = self.get_cache("project_id") if self.action.state == "default": try: # Generate a temporary password: password = uuid4().hex + uuid4().hex - user_id = self.get_cache('user_id') + user_id = self.get_cache("user_id") if not user_id: user = id_manager.create_user( - name=self.username, password=password, - email=self.email, domain=self.domain_id, - created_on=str_datetime(timezone.now())) - self.set_cache('user_id', user.id) + name=self.username, + password=password, + email=self.email, + domain=self.domain_id, + created_on=str_datetime(timezone.now()), + ) + self.set_cache("user_id", user.id) else: user = id_manager.get_user(user_id) # put user_id into action cache: - self.action.task.cache['user_id'] = user.id + self.action.task.cache["user_id"] = user.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)) + "Error: '%s' while creating user: %s with roles: %s" + % (e, self.username, default_roles) + ) raise - self.set_cache('roles_granted', True) + self.set_cache("roles_granted", True) self.add_note( - "New user '%s' created for project %s with roles: %s" % - (self.username, project_id, default_roles)) + "New user '%s' created for project %s with roles: %s" + % (self.username, project_id, default_roles) + ) elif self.action.state == "existing": try: - user_id = self.get_cache('user_id') + user_id = self.get_cache("user_id") if not user_id: - user = id_manager.find_user( - self.username, self.domain_id) - self.set_cache('user_id', user.id) + user = id_manager.find_user(self.username, self.domain_id) + self.set_cache("user_id", user.id) else: user = id_manager.get_user(user_id) - self.action.task.cache['user_id'] = user.id + self.action.task.cache["user_id"] = user.id self.grant_roles(user, default_roles, project_id) except Exception as e: self.add_note( - "Error: '%s' while granting roles: %s to user: %s" % - (e, default_roles, self.username)) + "Error: '%s' while granting roles: %s to user: %s" + % (e, default_roles, self.username) + ) raise - self.set_cache('roles_granted', True) + self.set_cache("roles_granted", True) self.add_note( "Existing user '%s' setup on project %s with roles: %s" - % (self.username, project_id, default_roles)) + % (self.username, project_id, default_roles) + ) elif self.action.state == "disabled": - user_id = self.get_cache('user_id') + user_id = self.get_cache("user_id") if not user_id: # first re-enable user try: @@ -356,8 +366,8 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): id_manager.enable_user(user) except Exception as e: self.add_note( - "Error: '%s' while re-enabling user: %s" % - (e, self.username)) + "Error: '%s' while re-enabling user: %s" % (e, self.username) + ) raise # and now update their password @@ -367,32 +377,34 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): id_manager.update_user_password(user, password) except Exception as e: self.add_note( - "Error: '%s' while changing password for user: %s" % - (e, self.username)) + "Error: '%s' while changing password for user: %s" + % (e, self.username) + ) raise - self.add_note( - 'User %s password has been changed.' % self.username) + self.add_note("User %s password has been changed." % self.username) - self.set_cache('user_id', user.id) + self.set_cache("user_id", user.id) else: user = id_manager.get_user(user_id) - self.action.task.cache['user_id'] = user.id + self.action.task.cache["user_id"] = user.id # now add their roles - roles_granted = self.get_cache('roles_granted') + roles_granted = self.get_cache("roles_granted") if not roles_granted: try: self.grant_roles(user, default_roles, project_id) except Exception as e: self.add_note( - "Error: '%s' while granting user: %s roles: %s" % - (e, self.username, default_roles)) + "Error: '%s' while granting user: %s roles: %s" + % (e, self.username, default_roles) + ) raise - self.set_cache('roles_granted', True) + self.set_cache("roles_granted", True) self.add_note( "Existing user '%s' setup on project %s with roles: %s" - % (self.username, project_id, default_roles)) + % (self.username, project_id, default_roles) + ) def _submit(self, token_data): """ @@ -407,29 +419,30 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): if not self.valid: return - project_id = self.get_cache('project_id') - self.action.task.cache['project_id'] = project_id - user_id = self.get_cache('user_id') - self.action.task.cache['user_id'] = user_id + project_id = self.get_cache("project_id") + self.action.task.cache["project_id"] = project_id + user_id = self.get_cache("user_id") + self.action.task.cache["user_id"] = user_id id_manager = user_store.IdentityManager() if self.action.state in ["default", "disabled"]: user = id_manager.get_user(user_id) try: - id_manager.update_user_password( - user, token_data['password']) + id_manager.update_user_password(user, token_data["password"]) except Exception as e: self.add_note( - "Error: '%s' while changing password for user: %s" % - (e, self.username)) + "Error: '%s' while changing password for user: %s" + % (e, self.username) + ) raise - self.add_note('User %s password has been changed.' % self.username) + self.add_note("User %s password has been changed." % self.username) elif self.action.state == "existing": # do nothing, everything is already done. self.add_note( - "Existing user '%s' already attached to project %s" % ( - user_id, project_id)) + "Existing user '%s' already attached to project %s" + % (user_id, project_id) + ) class AddDefaultUsersToProjectAction(BaseAction, ProjectMixin, UserMixin): @@ -441,7 +454,7 @@ class AddDefaultUsersToProjectAction(BaseAction, ProjectMixin, UserMixin): """ required = [ - 'domain_id', + "domain_id", ] serializer = serializers.AddDefaultUsersToProjectSerializer @@ -472,24 +485,21 @@ class AddDefaultUsersToProjectAction(BaseAction, ProjectMixin, UserMixin): for user in self.users: ks_user = id_manager.find_user(user, self.domain_id) if ks_user: - self.add_note('User: %s exists.' % user) + self.add_note("User: %s exists." % user) else: - self.add_note('ERROR: User: %s does not exist.' % user) + self.add_note("ERROR: User: %s does not exist." % user) all_found = False return all_found def _pre_validate(self): - self.action.valid = validate_steps([ - self._validate_users, - ]) + self.action.valid = validate_steps([self._validate_users,]) self.action.save() def _validate(self): - self.action.valid = validate_steps([ - self._validate_users, - self._validate_project_id, - ]) + self.action.valid = validate_steps( + [self._validate_users, self._validate_project_id,] + ) self.action.save() def _prepare(self): @@ -497,7 +507,7 @@ class AddDefaultUsersToProjectAction(BaseAction, ProjectMixin, UserMixin): def _approve(self): id_manager = user_store.IdentityManager() - self.project_id = self.action.task.cache.get('project_id', None) + self.project_id = self.action.task.cache.get("project_id", None) self._validate() if self.valid and not self.action.state == "completed": @@ -507,12 +517,14 @@ class AddDefaultUsersToProjectAction(BaseAction, ProjectMixin, UserMixin): self.grant_roles(ks_user, self.roles, self.project_id) self.add_note( - 'User: "%s" given roles: %s on project: %s.' % - (ks_user.name, self.roles, self.project_id)) + 'User: "%s" given roles: %s on project: %s.' + % (ks_user.name, self.roles, self.project_id) + ) except Exception as e: self.add_note( - "Error: '%s' while adding users to project: %s" % - (e, self.project_id)) + "Error: '%s' while adding users to project: %s" + % (e, self.project_id) + ) raise self.action.state = "completed" self.action.save() diff --git a/adjutant/actions/v1/resources.py b/adjutant/actions/v1/resources.py index af609e8..0a91774 100644 --- a/adjutant/actions/v1/resources.py +++ b/adjutant/actions/v1/resources.py @@ -36,9 +36,9 @@ class NewDefaultNetworkAction(BaseAction, ProjectMixin): """ required = [ - 'setup_network', - 'project_id', - 'region', + "setup_network", + "project_id", + "region", ] serializer = serializers.NewDefaultNetworkSerializer @@ -64,23 +64,20 @@ class NewDefaultNetworkAction(BaseAction, ProjectMixin): default="default_router", ), fields.StrConfig( - "public_network", - help_text="ID of the public network.", + "public_network", help_text="ID of the public network.", ), fields.StrConfig( - "subnet_cidr", - help_text="CIDR for the default subnet.", + "subnet_cidr", help_text="CIDR for the default subnet.", ), fields.ListConfig( - "dns_nameservers", - help_text="DNS nameservers for the subnet.", + "dns_nameservers", help_text="DNS nameservers for the subnet.", ), - ] + ], ), fields.DictConfig( "regions", help_text="Specific per region config for default network. " - "See 'region_defaults'.", + "See 'region_defaults'.", default={}, ), ] @@ -91,83 +88,86 @@ class NewDefaultNetworkAction(BaseAction, ProjectMixin): def _validate_region(self): if not self.region: - self.add_note('ERROR: No region given.') + self.add_note("ERROR: No region given.") return False id_manager = user_store.IdentityManager() region = id_manager.get_region(self.region) if not region: - self.add_note('ERROR: Region does not exist.') + self.add_note("ERROR: Region does not exist.") return False - self.add_note('Region: %s exists.' % self.region) + self.add_note("Region: %s exists." % self.region) return True def _validate(self): - self.action.valid = validate_steps([ - self._validate_region, - self._validate_project_id, - self._validate_keystone_user_project_id, - ]) + self.action.valid = validate_steps( + [ + self._validate_region, + self._validate_project_id, + self._validate_keystone_user_project_id, + ] + ) self.action.save() def _create_network(self): neutron = openstack_clients.get_neutronclient(region=self.region) try: region_config = self.config.regions[self.region] - network_config = self.config.region_defaults.overlay( - region_config) + network_config = self.config.region_defaults.overlay(region_config) except KeyError: network_config = self.config.region_defaults - if not self.get_cache('network_id'): + if not self.get_cache("network_id"): try: network_body = { "network": { "name": network_config.network_name, - 'tenant_id': self.project_id, - "admin_state_up": True + "tenant_id": self.project_id, + "admin_state_up": True, } } network = neutron.create_network(body=network_body) except Exception as e: self.add_note( - "Error: '%s' while creating network: %s" % - (e, network_config.network_name)) + "Error: '%s' while creating network: %s" + % (e, network_config.network_name) + ) raise - self.set_cache('network_id', network['network']['id']) - self.add_note("Network %s created for project %s" % - (network_config.network_name, - self.project_id)) + self.set_cache("network_id", network["network"]["id"]) + self.add_note( + "Network %s created for project %s" + % (network_config.network_name, self.project_id) + ) else: - self.add_note("Network %s already created for project %s" % - (network_config.network_name, - self.project_id)) + self.add_note( + "Network %s already created for project %s" + % (network_config.network_name, self.project_id) + ) - if not self.get_cache('subnet_id'): + if not self.get_cache("subnet_id"): try: subnet_body = { "subnet": { - "network_id": self.get_cache('network_id'), + "network_id": self.get_cache("network_id"), "ip_version": 4, - 'tenant_id': self.project_id, - 'dns_nameservers': network_config.dns_nameservers, - "cidr": network_config.subnet_cidr + "tenant_id": self.project_id, + "dns_nameservers": network_config.dns_nameservers, + "cidr": network_config.subnet_cidr, } } subnet = neutron.create_subnet(body=subnet_body) except Exception as e: - self.add_note( - "Error: '%s' while creating subnet" % e) + self.add_note("Error: '%s' while creating subnet" % e) raise - self.set_cache('subnet_id', subnet['subnet']['id']) - self.add_note("Subnet created for network %s" % - network_config.network_name) + self.set_cache("subnet_id", subnet["subnet"]["id"]) + self.add_note("Subnet created for network %s" % network_config.network_name) else: - self.add_note("Subnet already created for network %s" % - network_config.network_name) + self.add_note( + "Subnet already created for network %s" % network_config.network_name + ) - if not self.get_cache('router_id'): + if not self.get_cache("router_id"): try: router_body = { "router": { @@ -175,39 +175,35 @@ class NewDefaultNetworkAction(BaseAction, ProjectMixin): "external_gateway_info": { "network_id": network_config.public_network }, - 'tenant_id': self.project_id, - "admin_state_up": True + "tenant_id": self.project_id, + "admin_state_up": True, } } router = neutron.create_router(body=router_body) except Exception as e: self.add_note( - "Error: '%s' while creating router: %s" % - (e, network_config.router_name)) + "Error: '%s' while creating router: %s" + % (e, network_config.router_name) + ) raise - self.set_cache('router_id', router['router']['id']) - self.add_note("Router created for project %s" % - self.project_id) + self.set_cache("router_id", router["router"]["id"]) + self.add_note("Router created for project %s" % self.project_id) else: - self.add_note("Router already created for project %s" % - self.project_id) + self.add_note("Router already created for project %s" % self.project_id) - if not self.get_cache('port_id'): + if not self.get_cache("port_id"): try: - interface_body = { - "subnet_id": self.get_cache('subnet_id') - } + interface_body = {"subnet_id": self.get_cache("subnet_id")} interface = neutron.add_interface_router( - self.get_cache('router_id'), body=interface_body) + self.get_cache("router_id"), body=interface_body + ) except Exception as e: - self.add_note( - "Error: '%s' while attaching interface" % e) + self.add_note("Error: '%s' while attaching interface" % e) raise - self.set_cache('port_id', interface['port_id']) + self.set_cache("port_id", interface["port_id"]) self.add_note("Interface added to router for subnet") else: - self.add_note( - "Interface added to router for project %s" % self.project_id) + self.add_note("Interface added to router for project %s" % self.project_id) def _prepare(self): # Note: Do we need to get this from cache? it is a required setting @@ -231,31 +227,28 @@ class NewProjectDefaultNetworkAction(NewDefaultNetworkAction): """ required = [ - 'setup_network', - 'region', + "setup_network", + "region", ] serializer = serializers.NewProjectDefaultNetworkSerializer def _pre_validate(self): # Note: Don't check project here as it doesn't exist yet. - self.action.valid = validate_steps([ - self._validate_region, - ]) + self.action.valid = validate_steps([self._validate_region,]) self.action.save() def _validate(self): - self.action.valid = validate_steps([ - self._validate_region, - self._validate_project_id, - ]) + self.action.valid = validate_steps( + [self._validate_region, self._validate_project_id,] + ) self.action.save() def _prepare(self): self._pre_validate() def _approve(self): - self.project_id = self.action.task.cache.get('project_id', None) + self.project_id = self.action.task.cache.get("project_id", None) self._validate() if self.setup_network and self.valid: @@ -266,9 +259,9 @@ class UpdateProjectQuotasAction(BaseAction, QuotaMixin): """ Updates quota for a project to a given size in a list of regions """ required = [ - 'size', - 'project_id', - 'regions', + "size", + "project_id", + "regions", ] serializer = serializers.UpdateProjectQuotasSerializer @@ -293,10 +286,10 @@ class UpdateProjectQuotasAction(BaseAction, QuotaMixin): def _get_email(self): if CONF.identity.username_is_email: - return self.action.task.keystone_user['username'] + return self.action.task.keystone_user["username"] else: id_manager = user_store.IdentityManager() - user = id_manager.users.get(self.keystone_user['user_id']) + user = id_manager.users.get(self.keystone_user["user_id"]) email = user.email if email: return email @@ -316,17 +309,20 @@ class UpdateProjectQuotasAction(BaseAction, QuotaMixin): quota_config = CONF.quota.sizes.get(quota_size, {}) if not quota_config: self.add_note( - "Project quota not defined for size '%s' in region %s." % ( - quota_size, region_name)) + "Project quota not defined for size '%s' in region %s." + % (quota_size, region_name) + ) return quota_manager = QuotaManager( - self.project_id, self.config.size_difference_threshold) + self.project_id, self.config.size_difference_threshold + ) quota_manager.set_region_quota(region_name, quota_config) - self.add_note("Project quota for region %s set to %s" % ( - region_name, quota_size)) + self.add_note( + "Project quota for region %s set to %s" % (region_name, quota_size) + ) def _can_auto_approve(self): wait_days = self.config.days_between_autoapprove @@ -334,30 +330,34 @@ class UpdateProjectQuotasAction(BaseAction, QuotaMixin): completed_on__gte=timezone.now() - timedelta(days=wait_days), task_type__exact=self.action.task.task_type, cancelled__exact=False, - project_id__exact=self.project_id) + project_id__exact=self.project_id, + ) changed_in_period = False # Check to see if there have been any updates in the relavent regions # recently for task in task_list: for action in task.actions: - intersect = set(action.action_data[ - 'regions']).intersection(self.regions) + intersect = set(action.action_data["regions"]).intersection( + self.regions + ) if intersect: changed_in_period = True region_sizes = [] quota_manager = QuotaManager( - self.project_id, self.config.size_difference_threshold) + self.project_id, self.config.size_difference_threshold + ) for region in self.regions: current_size = quota_manager.get_region_quota_data( - region, include_usage=False)['current_quota_size'] + region, include_usage=False + )["current_quota_size"] region_sizes.append(current_size) self.add_note( - "Project has size '%s' in region: '%s'" % - (current_size, region)) + "Project has size '%s' in region: '%s'" % (current_size, region) + ) # Check for preapproved_quotas preapproved_quotas = [] @@ -365,42 +365,44 @@ class UpdateProjectQuotasAction(BaseAction, QuotaMixin): # If all region sizes are the same if region_sizes.count(region_sizes[0]) == len(region_sizes): - preapproved_quotas = quota_manager.get_quota_change_options( - region_sizes[0]) - smaller_quotas = quota_manager.get_smaller_quota_options( - region_sizes[0]) + preapproved_quotas = quota_manager.get_quota_change_options(region_sizes[0]) + smaller_quotas = quota_manager.get_smaller_quota_options(region_sizes[0]) if self.size in smaller_quotas: self.add_note( - "Quota size '%s' is in list of smaller quotas: %s" % - (self.size, smaller_quotas)) + "Quota size '%s' is in list of smaller quotas: %s" + % (self.size, smaller_quotas) + ) return True if changed_in_period: self.add_note( - "Quota has already been updated within the auto " - "approve time limit.") + "Quota has already been updated within the auto " "approve time limit." + ) return False if self.size not in preapproved_quotas: self.add_note( - "Quota size '%s' not in preapproved list: %s" % - (self.size, preapproved_quotas)) + "Quota size '%s' not in preapproved list: %s" + % (self.size, preapproved_quotas) + ) return False self.add_note( - "Quota size '%s' in preapproved list: %s" % - (self.size, preapproved_quotas)) + "Quota size '%s' in preapproved list: %s" % (self.size, preapproved_quotas) + ) return True def _validate(self): # Make sure the project id is valid and can be used - self.action.valid = validate_steps([ - self._validate_project_id, - self._validate_quota_size_exists, - self._validate_regions_exist, - self._validate_usage_lower_than_quota, - ]) + self.action.valid = validate_steps( + [ + self._validate_project_id, + self._validate_quota_size_exists, + self._validate_regions_exist, + self._validate_usage_lower_than_quota, + ] + ) self.action.save() def _prepare(self): @@ -420,8 +422,8 @@ class UpdateProjectQuotasAction(BaseAction, QuotaMixin): self._set_region_quota(region, self.size) self.action.state = "completed" - self.action.task.cache['project_id'] = self.project_id - self.action.task.cache['size'] = self.size + self.action.task.cache["project_id"] = self.project_id + self.action.task.cache["size"] = self.size self.action.save() @@ -434,6 +436,7 @@ class UpdateProjectQuotasAction(BaseAction, QuotaMixin): class SetProjectQuotaAction(UpdateProjectQuotasAction): """ Updates quota for a given project to a configured quota level """ + required = [] serializer = serializers.SetProjectQuotaSerializer @@ -454,9 +457,7 @@ class SetProjectQuotaAction(UpdateProjectQuotasAction): def _validate(self): # Make sure the project id is valid and can be used - self.action.valid = validate_steps([ - self._validate_project_id, - ]) + self.action.valid = validate_steps([self._validate_project_id,]) self.action.save() def _prepare(self): @@ -466,7 +467,7 @@ class SetProjectQuotaAction(UpdateProjectQuotasAction): def _approve(self): # Assumption: another action has placed the project_id into the cache. - self.project_id = self.action.task.cache.get('project_id', None) + self.project_id = self.action.task.cache.get("project_id", None) self._validate() if not self.valid or self.action.state == "completed": diff --git a/adjutant/actions/v1/serializers.py b/adjutant/actions/v1/serializers.py index 77e4ea8..1afce5d 100644 --- a/adjutant/actions/v1/serializers.py +++ b/adjutant/actions/v1/serializers.py @@ -32,7 +32,8 @@ class BaseUserNameSerializer(serializers.Serializer): """ A serializer where the user is identified by username/email. """ - domain_id = serializers.CharField(max_length=64, default='default') + + domain_id = serializers.CharField(max_length=64, default="default") username = serializers.CharField(max_length=255) email = serializers.EmailField() @@ -40,7 +41,7 @@ class BaseUserNameSerializer(serializers.Serializer): super(BaseUserNameSerializer, self).__init__(*args, **kwargs) if CONF.identity.username_is_email: - self.fields.pop('username') + self.fields.pop("username") class BaseUserIdSerializer(serializers.Serializer): @@ -53,35 +54,36 @@ class NewUserSerializer(BaseUserNameSerializer): def __init__(self, *args, **kwargs): super(NewUserSerializer, self).__init__(*args, **kwargs) # NOTE(adriant): This overide is mostly in use so that it can be tested - self.fields['roles'] = serializers.MultipleChoiceField( - choices=get_role_choices(), default=set) - self.fields['inherited_roles'] = serializers.MultipleChoiceField( - choices=get_role_choices(), default=set) + self.fields["roles"] = serializers.MultipleChoiceField( + choices=get_role_choices(), default=set + ) + self.fields["inherited_roles"] = serializers.MultipleChoiceField( + choices=get_role_choices(), default=set + ) def validate(self, data): - if not data['roles'] and not data['inherited_roles']: + if not data["roles"] and not data["inherited_roles"]: raise serializers.ValidationError( - "Must supply either 'roles' or 'inherited_roles', or both.") + "Must supply either 'roles' or 'inherited_roles', or both." + ) return data class NewProjectSerializer(serializers.Serializer): - parent_id = serializers.CharField( - max_length=64, default=None, allow_null=True) + parent_id = serializers.CharField(max_length=64, default=None, allow_null=True) project_name = serializers.CharField(max_length=64) - domain_id = serializers.CharField(max_length=64, default='default') + domain_id = serializers.CharField(max_length=64, default="default") description = serializers.CharField(default="", allow_blank=True) class NewProjectWithUserSerializer(BaseUserNameSerializer): - parent_id = serializers.CharField( - max_length=64, default=None, allow_null=True) + parent_id = serializers.CharField(max_length=64, default=None, allow_null=True) project_name = serializers.CharField(max_length=64) class ResetUserPasswordSerializer(BaseUserNameSerializer): - domain_name = serializers.CharField(max_length=64, default='Default') + domain_name = serializers.CharField(max_length=64, default="Default") # override domain_id so serializer doesn't set it up. domain_id = None @@ -93,15 +95,18 @@ class EditUserRolesSerializer(BaseUserIdSerializer): def __init__(self, *args, **kwargs): super(EditUserRolesSerializer, self).__init__(*args, **kwargs) # NOTE(adriant): This overide is mostly in use so that it can be tested - self.fields['roles'] = serializers.MultipleChoiceField( - choices=get_role_choices(), default=set) - self.fields['inherited_roles'] = serializers.MultipleChoiceField( - choices=get_role_choices(), default=set) + self.fields["roles"] = serializers.MultipleChoiceField( + choices=get_role_choices(), default=set + ) + self.fields["inherited_roles"] = serializers.MultipleChoiceField( + choices=get_role_choices(), default=set + ) def validate(self, data): - if not data['roles'] and not data['inherited_roles']: + if not data["roles"] and not data["inherited_roles"]: raise serializers.ValidationError( - "Must supply either 'roles' or 'inherited_roles', or both.") + "Must supply either 'roles' or 'inherited_roles', or both." + ) return data @@ -118,7 +123,7 @@ class NewProjectDefaultNetworkSerializer(serializers.Serializer): class AddDefaultUsersToProjectSerializer(serializers.Serializer): - domain_id = serializers.CharField(max_length=64, default='default') + domain_id = serializers.CharField(max_length=64, default="default") class SetProjectQuotaSerializer(serializers.Serializer): @@ -142,8 +147,9 @@ class UpdateProjectQuotasSerializer(serializers.Serializer): # NOTE(amelia): This overide is mostly in use so that it can be tested # However it does take into account the improbable edge case that the # regions have changed since the server was last started - self.fields['regions'] = serializers.MultipleChoiceField( - choices=get_region_choices()) + self.fields["regions"] = serializers.MultipleChoiceField( + choices=get_region_choices() + ) def validate_size(self, value): """ @@ -151,6 +157,5 @@ class UpdateProjectQuotasSerializer(serializers.Serializer): """ size_list = CONF.quota.sizes.keys() if value not in size_list: - raise serializers.ValidationError("Quota size: %s is not valid" - % value) + raise serializers.ValidationError("Quota size: %s is not valid" % value) return value diff --git a/adjutant/actions/v1/tests/test_misc_actions.py b/adjutant/actions/v1/tests/test_misc_actions.py index 3f4353f..adcbbb2 100644 --- a/adjutant/actions/v1/tests/test_misc_actions.py +++ b/adjutant/actions/v1/tests/test_misc_actions.py @@ -27,11 +27,11 @@ from adjutant.common.tests.utils import AdjutantTestCase from adjutant.config import CONF default_email_conf = { - 'from': "adjutant@example.com", - 'reply': 'adjutant@example.com', - 'template': 'initial.txt', - 'html_template': 'completed.txt', - 'subject': 'additional email' + "from": "adjutant@example.com", + "reply": "adjutant@example.com", + "template": "initial.txt", + "html_template": "completed.txt", + "subject": "additional email", } @@ -40,22 +40,15 @@ class FailEmail(mock.MagicMock): raise SMTPException -@mock.patch('adjutant.common.user_store.IdentityManager', - FakeManager) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) class MiscActionTests(AdjutantTestCase): - def test_send_email(self): # include html template to_address = "test@example.com" - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) - context = { - 'task': task, - 'actions': ["action_1", "action_2"] - } + context = {"task": task, "actions": ["action_1", "action_2"]} result = send_email(to_address, context, default_email_conf, task) @@ -67,58 +60,53 @@ class MiscActionTests(AdjutantTestCase): def test_send_email_no_addresses(self): to_address = [] - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) - context = { - 'task': task, - 'actions': ["action_1", "action_2"] - } + context = {"task": task, "actions": ["action_1", "action_2"]} result = send_email(to_address, context, default_email_conf, task) self.assertEqual(result, None) self.assertEqual(len(mail.outbox), 0) - @mock.patch('adjutant.actions.utils.EmailMultiAlternatives', - FailEmail) + @mock.patch("adjutant.actions.utils.EmailMultiAlternatives", FailEmail) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.action_defaults.SendAdditionalEmailAction.approve": [ - {'operation': 'overlay', 'value': { - 'email_task_cache': True, - 'subject': 'Email Subject', - 'template': 'token.txt' - }}, + { + "operation": "overlay", + "value": { + "email_task_cache": True, + "subject": "Email Subject", + "template": "token.txt", + }, + }, ], - }) + }, + ) def test_send_additional_email_fail(self): """ Tests that a failure to send an additional email doesn't cause it to become invalid or break. """ - task = Task.objects.create( - keystone_user={}, - task_type='edit_roles', - ) + task = Task.objects.create(keystone_user={}, task_type="edit_roles",) action = SendAdditionalEmailAction({}, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) - task.cache["additional_emails"] = ["thisguy@righthere.com", - "nope@example.com"] + task.cache["additional_emails"] = ["thisguy@righthere.com", "nope@example.com"] action.approve() self.assertEqual(action.valid, True) self.assertEqual(len(mail.outbox), 0) self.assertTrue( - "Unable to send additional email. Stage: approve" in - action.action.task.action_notes['SendAdditionalEmailAction'][1]) + "Unable to send additional email. Stage: approve" + in action.action.task.action_notes["SendAdditionalEmailAction"][1] + ) action.submit({}) self.assertEqual(action.valid, True) @@ -127,37 +115,39 @@ class MiscActionTests(AdjutantTestCase): CONF, operations={ "adjutant.workflow.action_defaults.SendAdditionalEmailAction.approve": [ - {'operation': 'overlay', 'value': { - 'email_task_cache': True, - 'subject': 'Email Subject', - 'template': 'token.txt' - }}, + { + "operation": "overlay", + "value": { + "email_task_cache": True, + "subject": "Email Subject", + "template": "token.txt", + }, + }, ], - }) + }, + ) def test_send_additional_email_task_cache(self): """ Tests sending an additional email with the address placed in the task cache. """ - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) action = SendAdditionalEmailAction({}, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) - task.cache["additional_emails"] = ["thisguy@righthere.com", - "nope@example.com"] + task.cache["additional_emails"] = ["thisguy@righthere.com", "nope@example.com"] action.approve() self.assertEqual(action.valid, True) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(set(mail.outbox[0].to), - set(["thisguy@righthere.com", "nope@example.com"])) + self.assertEqual( + set(mail.outbox[0].to), set(["thisguy@righthere.com", "nope@example.com"]) + ) action.submit({}) self.assertEqual(action.valid, True) @@ -167,22 +157,24 @@ class MiscActionTests(AdjutantTestCase): CONF, operations={ "adjutant.workflow.action_defaults.SendAdditionalEmailAction.approve": [ - {'operation': 'overlay', 'value': { - 'email_task_cache': True, - 'subject': 'Email Subject', - 'template': 'token.txt' - }}, + { + "operation": "overlay", + "value": { + "email_task_cache": True, + "subject": "Email Subject", + "template": "token.txt", + }, + }, ], - }) + }, + ) def test_send_additional_email_task_cache_none_set(self): """ Tests sending an additional email with 'email_task_cache' set but no address placed in the task cache. """ - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) action = SendAdditionalEmailAction({}, task=task, order=1) @@ -201,23 +193,24 @@ class MiscActionTests(AdjutantTestCase): CONF, operations={ "adjutant.workflow.action_defaults.SendAdditionalEmailAction.approve": [ - {'operation': 'overlay', 'value': { - 'email_additional_addresses': [ - 'anadminwhocares@example.com'], - 'subject': 'Email Subject', - 'template': 'token.txt' - }}, + { + "operation": "overlay", + "value": { + "email_additional_addresses": ["anadminwhocares@example.com"], + "subject": "Email Subject", + "template": "token.txt", + }, + }, ], - }) + }, + ) def test_send_additional_email_email_in_config(self): """ Tests sending an additional email with the address placed in the task cache. """ - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) action = SendAdditionalEmailAction({}, task=task, order=1) @@ -228,8 +221,7 @@ class MiscActionTests(AdjutantTestCase): self.assertEqual(action.valid, True) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].to, - ["anadminwhocares@example.com"]) + self.assertEqual(mail.outbox[0].to, ["anadminwhocares@example.com"]) action.submit({}) self.assertEqual(action.valid, True) diff --git a/adjutant/actions/v1/tests/test_project_actions.py b/adjutant/actions/v1/tests/test_project_actions.py index de17bb7..b27bccc 100644 --- a/adjutant/actions/v1/tests/test_project_actions.py +++ b/adjutant/actions/v1/tests/test_project_actions.py @@ -17,38 +17,42 @@ import mock from confspirator.tests import utils as conf_utils from adjutant.actions.v1.projects import ( - NewProjectWithUserAction, AddDefaultUsersToProjectAction, - NewProjectAction) + NewProjectWithUserAction, + AddDefaultUsersToProjectAction, + NewProjectAction, +) from adjutant.api.models import Task from adjutant.common.tests import fake_clients -from adjutant.common.tests.fake_clients import ( - FakeManager, setup_identity_cache) +from adjutant.common.tests.fake_clients import FakeManager, setup_identity_cache from adjutant.common.tests.utils import AdjutantTestCase from adjutant.config import CONF -@mock.patch('adjutant.common.user_store.IdentityManager', - FakeManager) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.action_defaults.NewProjectWithUserAction.default_roles": [ - {'operation': 'override', 'value': [ - 'member', 'heat_stack_owner', 'project_admin', 'project_mod']}, + { + "operation": "override", + "value": ["member", "heat_stack_owner", "project_admin", "project_mod"], + }, ], "adjutant.workflow.action_defaults.NewProjectAction.default_roles": [ - {'operation': 'override', 'value': [ - 'member', 'heat_stack_owner', 'project_admin', 'project_mod']}, + { + "operation": "override", + "value": ["member", "heat_stack_owner", "project_admin", "project_mod"], + }, ], "adjutant.workflow.action_defaults.AddDefaultUsersToProjectAction.default_users": [ - {'operation': 'override', 'value': ['admin']}, + {"operation": "override", "value": ["admin"]}, ], "adjutant.workflow.action_defaults.AddDefaultUsersToProjectAction.default_roles": [ - {'operation': 'override', 'value': ['admin']}, + {"operation": "override", "value": ["admin"]}, ], - }) + }, +) class ProjectActionTests(AdjutantTestCase): - def test_new_project(self): """ Base case, no project, no user. @@ -59,15 +63,13 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache() - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -78,30 +80,34 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") - new_user = fake_clients.identity_cache['new_users'][0] - self.assertEqual(new_user.name, 'test@example.com') - self.assertEqual(new_user.email, 'test@example.com') + new_user = fake_clients.identity_cache["new_users"][0] + self.assertEqual(new_user.name, "test@example.com") + self.assertEqual(new_user.email, "test@example.com") self.assertEqual( task.cache, - {'project_id': new_project.id, 'user_id': new_user.id, - 'user_state': 'default'}) + { + "project_id": new_project.id, + "user_id": new_user.id, + "user_state": "default", + }, + ) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) - self.assertEqual(new_user.password, '123456') + self.assertEqual(new_user.password, "123456") fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(new_user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) def test_new_project_reapprove(self): """ @@ -111,15 +117,13 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache() - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -130,41 +134,47 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") - new_user = fake_clients.identity_cache['new_users'][0] - self.assertEqual(new_user.name, 'test@example.com') - self.assertEqual(new_user.email, 'test@example.com') + new_user = fake_clients.identity_cache["new_users"][0] + self.assertEqual(new_user.name, "test@example.com") + self.assertEqual(new_user.email, "test@example.com") self.assertEqual( task.cache, - {'project_id': new_project.id, 'user_id': new_user.id, - 'user_state': 'default'}) + { + "project_id": new_project.id, + "user_id": new_user.id, + "user_state": "default", + }, + ) action.approve() self.assertEqual(action.valid, True) - self.assertEqual( - len(fake_clients.identity_cache['new_projects']), 1) - self.assertEqual( - len(fake_clients.identity_cache['new_users']), 1) + self.assertEqual(len(fake_clients.identity_cache["new_projects"]), 1) + self.assertEqual(len(fake_clients.identity_cache["new_users"]), 1) self.assertEqual( task.cache, - {'project_id': new_project.id, 'user_id': new_user.id, - 'user_state': 'default'}) + { + "project_id": new_project.id, + "user_id": new_user.id, + "user_state": "default", + }, + ) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) - self.assertEqual(new_user.password, '123456') + self.assertEqual(new_user.password, "123456") fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(new_user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) def test_new_project_reapprove_failure(self): """ @@ -175,15 +185,13 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache() - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -198,6 +206,7 @@ class ProjectActionTests(AdjutantTestCase): def fail_grant(user, default_roles, project_id): raise FakeException + # We swap out the old grant function and keep # it for later. old_grant_function = action.grant_roles @@ -209,14 +218,13 @@ class ProjectActionTests(AdjutantTestCase): # No roles_granted yet, but user created self.assertTrue("user_id" in action.action.cache) self.assertFalse("roles_granted" in action.action.cache) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") - new_user = fake_clients.identity_cache['new_users'][0] - self.assertEqual(new_user.name, 'test@example.com') - self.assertEqual(new_user.email, 'test@example.com') - self.assertEqual( - len(fake_clients.identity_cache['role_assignments']), 0) + new_user = fake_clients.identity_cache["new_users"][0] + self.assertEqual(new_user.name, "test@example.com") + self.assertEqual(new_user.email, "test@example.com") + self.assertEqual(len(fake_clients.identity_cache["role_assignments"]), 0) # And then swap back the correct function action.grant_roles = old_grant_function @@ -226,18 +234,18 @@ class ProjectActionTests(AdjutantTestCase): # roles_granted in cache self.assertTrue("roles_granted" in action.action.cache) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) - self.assertEqual(new_user.password, '123456') + self.assertEqual(new_user.password, "123456") fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(new_user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) def test_new_project_existing_user(self): """ @@ -245,19 +253,18 @@ class ProjectActionTests(AdjutantTestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -268,37 +275,41 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") - self.assertEqual( - len(fake_clients.identity_cache['new_users']), 0) + self.assertEqual(len(fake_clients.identity_cache["new_users"]), 0) self.assertEqual( task.cache, - {'project_id': new_project.id, 'user_id': user.id, - 'user_state': 'existing'}) + { + "project_id": new_project.id, + "user_id": user.id, + "user_state": "existing", + }, + ) # submit does nothing for existing action.submit({}) self.assertEqual(action.valid, True) - self.assertEqual(user.password, '123') + self.assertEqual(user.password, "123") fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_new_project_user_nonmatching_email(self): """ Attempts to create a new project and a new user, where there is @@ -306,20 +317,19 @@ class ProjectActionTests(AdjutantTestCase): """ user = fake_clients.FakeUser( - name="test_user", password="123", email="different@example.com") + name="test_user", password="123", email="different@example.com" + ) setup_identity_cache(users=[user]) - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'username': 'test_user', - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "username": "test_user", + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -331,10 +341,10 @@ class ProjectActionTests(AdjutantTestCase): self.assertEqual(action.valid, False) self.assertEqual( - fake_clients.identity_cache['projects'].get('test_project'), - None) + fake_clients.identity_cache["projects"].get("test_project"), None + ) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, False) @@ -345,15 +355,13 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache() - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -364,12 +372,12 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") - fake_clients.identity_cache['projects'] = {} + fake_clients.identity_cache["projects"] = {} - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, False) @@ -380,15 +388,13 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache() - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -399,13 +405,13 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_user = fake_clients.identity_cache['new_users'][0] - self.assertEqual(new_user.name, 'test@example.com') - self.assertEqual(new_user.email, 'test@example.com') + new_user = fake_clients.identity_cache["new_users"][0] + self.assertEqual(new_user.name, "test@example.com") + self.assertEqual(new_user.email, "test@example.com") - fake_clients.identity_cache['users'] = {} + fake_clients.identity_cache["users"] = {} - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, False) @@ -415,20 +421,21 @@ class ProjectActionTests(AdjutantTestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com", - enabled=False) + name="test@example.com", + password="123", + email="test@example.com", + enabled=False, + ) setup_identity_cache(users=[user]) - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } # Sign up, approve @@ -440,30 +447,30 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") - self.assertEqual( - len(fake_clients.identity_cache['new_users']), 0) + self.assertEqual(len(fake_clients.identity_cache["new_users"]), 0) self.assertEqual( task.cache, - {'user_id': user.id, - 'project_id': new_project.id, - 'user_state': 'disabled'}) - self.assertEqual( - action.action.cache["token_fields"], - ['password']) + { + "user_id": user.id, + "project_id": new_project.id, + "user_state": "disabled", + }, + ) + self.assertEqual(action.action.cache["token_fields"], ["password"]) # submit password reset - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) - self.assertEqual(user.password, '123456') + self.assertEqual(user.password, "123456") # check that user has been enabled correctly - self.assertEqual(user.email, 'test@example.com') + self.assertEqual(user.email, "test@example.com") self.assertEqual(user.enabled, True) # Check user has correct roles in new project @@ -471,8 +478,8 @@ class ProjectActionTests(AdjutantTestCase): roles = fake_client._get_roles_as_names(user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) def test_new_project_user_disabled_during_signup(self): """ @@ -486,15 +493,13 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache() # Sign up for the project+user, validate. - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } # Sign up @@ -506,11 +511,11 @@ class ProjectActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() user = fake_client.create_user( name="test@example.com", - password='origpass', + password="origpass", email="test@example.com", created_on=None, - domain='default', - default_project=None + domain="default", + default_project=None, ) fake_client.disable_user(user.id) @@ -518,36 +523,38 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") - self.assertEqual( - len(fake_clients.identity_cache['new_users']), 1) + self.assertEqual(len(fake_clients.identity_cache["new_users"]), 1) self.assertEqual( task.cache, - {'user_id': user.id, - 'project_id': new_project.id, - 'user_state': 'disabled'}) + { + "user_id": user.id, + "project_id": new_project.id, + "user_state": "disabled", + }, + ) # check that user has been re-enabled with a generated password. self.assertEqual(user.enabled, True) - self.assertNotEqual(user.password, 'origpass') + self.assertNotEqual(user.password, "origpass") # submit password reset - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) # Ensure user has new password: - self.assertEqual(user.password, '123456') + self.assertEqual(user.password, "123456") fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) def test_new_project_existing_project(self): """ @@ -560,16 +567,17 @@ class ProjectActionTests(AdjutantTestCase): task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -587,16 +595,17 @@ class ProjectActionTests(AdjutantTestCase): task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'not_default_id', - 'parent_id': None, - 'email': 'test@example.com', - 'project_name': 'test_project', + "domain_id": "not_default_id", + "parent_id": None, + "email": "test@example.com", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -611,9 +620,10 @@ class ProjectActionTests(AdjutantTestCase): CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_new_project_email_not_username(self): """ Base case, no project, no user. @@ -624,16 +634,14 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache() - task = Task.objects.create( - keystone_user={} - ) + task = Task.objects.create(keystone_user={}) data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'username': 'test_user', - 'project_name': 'test_project', + "domain_id": "default", + "parent_id": None, + "email": "test@example.com", + "username": "test_user", + "project_name": "test_project", } action = NewProjectWithUserAction(data, task=task, order=1) @@ -644,30 +652,34 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") - new_user = fake_clients.identity_cache['new_users'][0] - self.assertEqual(new_user.name, 'test_user') - self.assertEqual(new_user.email, 'test@example.com') + new_user = fake_clients.identity_cache["new_users"][0] + self.assertEqual(new_user.name, "test_user") + self.assertEqual(new_user.email, "test@example.com") self.assertEqual( task.cache, - {'project_id': new_project.id, 'user_id': new_user.id, - 'user_state': 'default'}) + { + "project_id": new_project.id, + "user_id": new_user.id, + "user_state": "default", + }, + ) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) - self.assertEqual(new_user.password, '123456') + self.assertEqual(new_user.password, "123456") fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(new_user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) def test_add_default_users(self): """ @@ -681,13 +693,13 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache(projects=[project]) - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) - task.cache = {'project_id': project.id} + task.cache = {"project_id": project.id} action = AddDefaultUsersToProjectAction( - {'domain_id': 'default'}, task=task, order=1) + {"domain_id": "default"}, task=task, order=1 + ) action.prepare() self.assertEqual(action.valid, True) @@ -696,9 +708,9 @@ class ProjectActionTests(AdjutantTestCase): self.assertEqual(action.valid, True) fake_client = fake_clients.FakeManager() - user = fake_client.find_user('admin', 'default') + user = fake_client.find_user("admin", "default") roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['admin']) + self.assertEqual(roles, ["admin"]) def test_add_default_users_invalid_project(self): """Add default users to a project that doesn't exist. @@ -708,13 +720,13 @@ class ProjectActionTests(AdjutantTestCase): """ setup_identity_cache() - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) - task.cache = {'project_id': "invalid_project_id"} + task.cache = {"project_id": "invalid_project_id"} action = AddDefaultUsersToProjectAction( - {'domain_id': 'default'}, task=task, order=1) + {"domain_id": "default"}, task=task, order=1 + ) action.prepare() # No need to test project yet - it's ok if it doesn't exist self.assertEqual(action.valid, True) @@ -731,13 +743,13 @@ class ProjectActionTests(AdjutantTestCase): setup_identity_cache(projects=[project]) - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) - task.cache = {'project_id': project.id} + task.cache = {"project_id": project.id} action = AddDefaultUsersToProjectAction( - {'domain_id': 'default'}, task=task, order=1) + {"domain_id": "default"}, task=task, order=1 + ) action.prepare() self.assertEqual(action.valid, True) @@ -746,15 +758,15 @@ class ProjectActionTests(AdjutantTestCase): self.assertEqual(action.valid, True) fake_client = fake_clients.FakeManager() - user = fake_client.find_user('admin', 'default') + user = fake_client.find_user("admin", "default") roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['admin']) + self.assertEqual(roles, ["admin"]) action.approve() self.assertEqual(action.valid, True) roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['admin']) + self.assertEqual(roles, ["admin"]) def test_new_project_action(self): """ @@ -764,20 +776,24 @@ class ProjectActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="parent_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( - keystone_user={"user_id": user.id, - "project_id": project.id, - "project_domain_id": 'default'}) + keystone_user={ + "user_id": user.id, + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'parent_id': project.id, - 'project_name': 'test_project', - 'description': '', + "domain_id": "default", + "parent_id": project.id, + "project_name": "test_project", + "description": "", } action = NewProjectAction(data, task=task, order=1) @@ -788,16 +804,16 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") self.assertEqual(new_project.parent_id, project.id) fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) action.submit({}) self.assertEqual(action.valid, True) @@ -811,20 +827,24 @@ class ProjectActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="parent_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( - keystone_user={"user_id": user.id, - "project_id": project.id, - "project_domain_id": 'default'}) + keystone_user={ + "user_id": user.id, + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'parent_id': project.id, - 'project_name': 'test_project', - 'description': '', + "domain_id": "default", + "parent_id": project.id, + "project_name": "test_project", + "description": "", } action = NewProjectAction(data, task=task, order=1) @@ -835,29 +855,29 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") self.assertEqual(new_project.parent_id, project.id) fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) action.approve() # Nothing should change self.assertEqual(action.valid, True) - self.assertEqual(new_project.name, 'test_project') + self.assertEqual(new_project.name, "test_project") self.assertEqual(new_project.parent_id, project.id) roles = fake_client._get_roles_as_names(user, new_project) self.assertEqual( sorted(roles), - sorted(['member', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + sorted(["member", "project_admin", "project_mod", "heat_stack_owner"]), + ) action.submit({}) self.assertEqual(action.valid, True) @@ -871,20 +891,24 @@ class ProjectActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="parent_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( - keystone_user={"user_id": user.id, - "project_id": project.id, - "project_domain_id": 'default'}) + keystone_user={ + "user_id": user.id, + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'parent_id': "not_parent_project_id", - 'project_name': 'test_project', - 'description': '', + "domain_id": "default", + "parent_id": "not_parent_project_id", + "project_name": "test_project", + "description": "", } action = NewProjectAction(data, task=task, order=1) @@ -907,20 +931,24 @@ class ProjectActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="parent_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( - keystone_user={"user_id": user.id, - "project_id": project.id, - "project_domain_id": 'default'}) + keystone_user={ + "user_id": user.id, + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'notdefault', - 'parent_id': project.id, - 'project_name': 'test_project', - 'description': '', + "domain_id": "notdefault", + "parent_id": project.id, + "project_name": "test_project", + "description": "", } action = NewProjectAction(data, task=task, order=1) @@ -942,20 +970,24 @@ class ProjectActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="parent_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( - keystone_user={"user_id": user.id, - "project_id": project.id, - "project_domain_id": 'default'}) + keystone_user={ + "user_id": user.id, + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'parent_id': None, - 'project_name': 'test_project', - 'description': '', + "domain_id": "default", + "parent_id": None, + "project_name": "test_project", + "description": "", } action = NewProjectAction(data, task=task, order=1) @@ -966,8 +998,8 @@ class ProjectActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") self.assertEqual(new_project.parent_id, None) action.submit({}) diff --git a/adjutant/actions/v1/tests/test_resource_actions.py b/adjutant/actions/v1/tests/test_resource_actions.py index 18d2411..63821be 100644 --- a/adjutant/actions/v1/tests/test_resource_actions.py +++ b/adjutant/actions/v1/tests/test_resource_actions.py @@ -17,31 +17,38 @@ import mock from confspirator.tests import utils as conf_utils from adjutant.actions.v1.resources import ( - NewDefaultNetworkAction, NewProjectDefaultNetworkAction, - SetProjectQuotaAction, UpdateProjectQuotasAction) + NewDefaultNetworkAction, + NewProjectDefaultNetworkAction, + SetProjectQuotaAction, + UpdateProjectQuotasAction, +) from adjutant.api.models import Task from adjutant.common.tests.fake_clients import ( - FakeManager, setup_identity_cache, get_fake_neutron, get_fake_novaclient, - get_fake_cinderclient, setup_neutron_cache, neutron_cache, cinder_cache, - nova_cache, setup_mock_caches, get_fake_octaviaclient, octavia_cache) + FakeManager, + setup_identity_cache, + get_fake_neutron, + get_fake_novaclient, + get_fake_cinderclient, + setup_neutron_cache, + neutron_cache, + cinder_cache, + nova_cache, + setup_mock_caches, + get_fake_octaviaclient, + octavia_cache, +) from adjutant.common.tests.utils import AdjutantTestCase from adjutant.config import CONF -@mock.patch('adjutant.common.user_store.IdentityManager', - FakeManager) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) @mock.patch( - 'adjutant.actions.v1.resources.openstack_clients.get_neutronclient', - get_fake_neutron) -@mock.patch( - 'adjutant.common.openstack_clients.get_neutronclient', - get_fake_neutron) -@mock.patch( - 'adjutant.common.openstack_clients.get_novaclient', - get_fake_novaclient) -@mock.patch( - 'adjutant.common.openstack_clients.get_cinderclient', - get_fake_cinderclient) + "adjutant.actions.v1.resources.openstack_clients.get_neutronclient", + get_fake_neutron, +) +@mock.patch("adjutant.common.openstack_clients.get_neutronclient", get_fake_neutron) +@mock.patch("adjutant.common.openstack_clients.get_novaclient", get_fake_novaclient) +@mock.patch("adjutant.common.openstack_clients.get_cinderclient", get_fake_cinderclient) @conf_utils.modify_conf( CONF, operations={ @@ -73,39 +80,36 @@ from adjutant.config import CONF "adjutant.workflow.action_defaults.SetProjectQuotaAction.region_sizes": [ { "operation": "override", - "value": {'RegionOne': 'small', 'RegionThree': 'large_cinder_only'} + "value": {"RegionOne": "small", "RegionThree": "large_cinder_only"}, }, ], }, ) class ProjectSetupActionTests(AdjutantTestCase): - def test_network_setup(self): """ Base case, setup a new network , no issues. """ - setup_neutron_cache('RegionOne', 'test_project_id') + setup_neutron_cache("RegionOne", "test_project_id") task = Task.objects.create( - keystone_user={ - 'roles': ['admin'], - 'project_id': 'test_project_id'}) + keystone_user={"roles": ["admin"], "project_id": "test_project_id"} + ) project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} setup_identity_cache(projects=[project]) data = { - 'setup_network': True, - 'region': 'RegionOne', - 'project_id': 'test_project_id', + "setup_network": True, + "region": "RegionOne", + "project_id": "test_project_id", } - action = NewDefaultNetworkAction( - data, task=task, order=1) + action = NewDefaultNetworkAction(data, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) @@ -115,46 +119,49 @@ class ProjectSetupActionTests(AdjutantTestCase): self.assertEqual( action.action.cache, - {'network_id': 'net_id_0', - 'port_id': 'port_id_3', - 'router_id': 'router_id_2', - 'subnet_id': 'subnet_id_1'} + { + "network_id": "net_id_0", + "port_id": "port_id_3", + "router_id": "router_id_2", + "subnet_id": "subnet_id_1", + }, ) global neutron_cache - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 1) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 1 + ) def test_network_setup_no_setup(self): """ Told not to setup, should do nothing. """ - setup_neutron_cache('RegionOne', 'test_project_id') + setup_neutron_cache("RegionOne", "test_project_id") task = Task.objects.create( - keystone_user={ - 'roles': ['admin'], - 'project_id': 'test_project_id'}) + keystone_user={"roles": ["admin"], "project_id": "test_project_id"} + ) project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} setup_identity_cache(projects=[project]) data = { - 'setup_network': False, - 'region': 'RegionOne', - 'project_id': 'test_project_id', + "setup_network": False, + "region": "RegionOne", + "project_id": "test_project_id", } - action = NewDefaultNetworkAction( - data, task=task, order=1) + action = NewDefaultNetworkAction(data, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) @@ -165,45 +172,46 @@ class ProjectSetupActionTests(AdjutantTestCase): self.assertEqual(action.action.cache, {}) global neutron_cache - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 0) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 0) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 0) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 0 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 0 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 0 + ) def test_network_setup_fail(self): """ Should fail, but on re_approve will continue where it left off. """ - setup_neutron_cache('RegionOne', 'test_project_id') + setup_neutron_cache("RegionOne", "test_project_id") global neutron_cache task = Task.objects.create( - keystone_user={ - 'roles': ['admin'], - 'project_id': 'test_project_id'}) + keystone_user={"roles": ["admin"], "project_id": "test_project_id"} + ) project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} setup_identity_cache(projects=[project]) data = { - 'setup_network': True, - 'region': 'RegionOne', - 'project_id': 'test_project_id', + "setup_network": True, + "region": "RegionOne", + "project_id": "test_project_id", } - action = NewDefaultNetworkAction( - data, task=task, order=1) + action = NewDefaultNetworkAction(data, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) - neutron_cache['RegionOne']['test_project_id']['routers'] = [] + neutron_cache["RegionOne"]["test_project_id"]["routers"] = [] try: action.approve() @@ -212,53 +220,57 @@ class ProjectSetupActionTests(AdjutantTestCase): pass self.assertEqual( - action.action.cache, - {'network_id': 'net_id_0', - 'subnet_id': 'subnet_id_1'} + action.action.cache, {"network_id": "net_id_0", "subnet_id": "subnet_id_1"} ) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 0) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 0 + ) - neutron_cache['RegionOne']['test_project_id']['routers'] = {} + neutron_cache["RegionOne"]["test_project_id"]["routers"] = {} action.approve() self.assertEqual( action.action.cache, - {'network_id': 'net_id_0', - 'port_id': 'port_id_3', - 'router_id': 'router_id_2', - 'subnet_id': 'subnet_id_1'} + { + "network_id": "net_id_0", + "port_id": "port_id_3", + "router_id": "router_id_2", + "subnet_id": "subnet_id_1", + }, ) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 1) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 1 + ) def test_new_project_network_setup(self): """ Base case, setup network after a new project, no issues. """ setup_identity_cache() - setup_neutron_cache('RegionOne', 'test_project_id') - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + setup_neutron_cache("RegionOne", "test_project_id") + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'setup_network': True, - 'region': 'RegionOne', + "setup_network": True, + "region": "RegionOne", } - action = NewProjectDefaultNetworkAction( - data, task=task, order=1) + action = NewProjectDefaultNetworkAction(data, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) @@ -266,50 +278,53 @@ class ProjectSetupActionTests(AdjutantTestCase): # 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.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} setup_identity_cache(projects=[project]) - task.cache = {'project_id': "test_project_id"} + task.cache = {"project_id": "test_project_id"} action.approve() self.assertEqual(action.valid, True) self.assertEqual( action.action.cache, - {'network_id': 'net_id_0', - 'port_id': 'port_id_3', - 'router_id': 'router_id_2', - 'subnet_id': 'subnet_id_1'} + { + "network_id": "net_id_0", + "port_id": "port_id_3", + "router_id": "router_id_2", + "subnet_id": "subnet_id_1", + }, ) global neutron_cache - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 1) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 1 + ) def test_new_project_network_setup_no_id(self): """ No project id given, should do nothing. """ setup_identity_cache() - setup_neutron_cache('RegionOne', 'test_project_id') - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + setup_neutron_cache("RegionOne", "test_project_id") + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'setup_network': True, - 'region': 'RegionOne', + "setup_network": True, + "region": "RegionOne", } - action = NewProjectDefaultNetworkAction( - data, task=task, order=1) + action = NewProjectDefaultNetworkAction(data, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) @@ -320,29 +335,30 @@ class ProjectSetupActionTests(AdjutantTestCase): self.assertEqual(action.action.cache, {}) global neutron_cache - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 0) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 0) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 0) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 0 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 0 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 0 + ) def test_new_project_network_setup_no_setup(self): """ Told not to setup, should do nothing. """ setup_identity_cache() - setup_neutron_cache('RegionOne', 'test_project_id') - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + setup_neutron_cache("RegionOne", "test_project_id") + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'setup_network': False, - 'region': 'RegionOne', + "setup_network": False, + "region": "RegionOne", } - action = NewProjectDefaultNetworkAction( - data, task=task, order=1) + action = NewProjectDefaultNetworkAction(data, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) @@ -350,14 +366,14 @@ class ProjectSetupActionTests(AdjutantTestCase): # 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.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} setup_identity_cache(projects=[project]) - task.cache = {'project_id': "test_project_id"} + task.cache = {"project_id": "test_project_id"} action.approve() self.assertEqual(action.valid, True) @@ -365,47 +381,48 @@ class ProjectSetupActionTests(AdjutantTestCase): self.assertEqual(action.action.cache, {}) global neutron_cache - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 0) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 0) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 0) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 0 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 0 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 0 + ) def test_new_project_network_setup_fail(self): """ Should fail, but on re_approve will continue where it left off. """ setup_identity_cache() - setup_neutron_cache('RegionOne', 'test_project_id') + setup_neutron_cache("RegionOne", "test_project_id") global neutron_cache - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'setup_network': True, - 'region': 'RegionOne', + "setup_network": True, + "region": "RegionOne", } - action = NewProjectDefaultNetworkAction( - data, task=task, order=1) + action = NewProjectDefaultNetworkAction(data, task=task, order=1) action.prepare() self.assertEqual(action.valid, True) - neutron_cache['RegionOne']['test_project_id']['routers'] = [] + neutron_cache["RegionOne"]["test_project_id"]["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.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} setup_identity_cache(projects=[project]) - task.cache = {'project_id': "test_project_id"} + task.cache = {"project_id": "test_project_id"} try: action.approve() @@ -414,54 +431,59 @@ class ProjectSetupActionTests(AdjutantTestCase): pass self.assertEqual( - action.action.cache, - {'network_id': 'net_id_0', - 'subnet_id': 'subnet_id_1'} + action.action.cache, {"network_id": "net_id_0", "subnet_id": "subnet_id_1"} ) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 0) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 0 + ) - neutron_cache['RegionOne']['test_project_id']['routers'] = {} + neutron_cache["RegionOne"]["test_project_id"]["routers"] = {} action.approve() self.assertEqual( action.action.cache, - {'network_id': 'net_id_0', - 'port_id': 'port_id_3', - 'router_id': 'router_id_2', - 'subnet_id': 'subnet_id_1'} + { + "network_id": "net_id_0", + "port_id": "port_id_3", + "router_id": "router_id_2", + "subnet_id": "subnet_id_1", + }, ) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['networks']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['routers']), 1) - self.assertEqual(len( - neutron_cache['RegionOne']['test_project_id']['subnets']), 1) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["networks"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["routers"]), 1 + ) + self.assertEqual( + len(neutron_cache["RegionOne"]["test_project_id"]["subnets"]), 1 + ) def test_set_quota(self): """ Base case, sets quota on all services of the cached project id. """ project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} setup_identity_cache(projects=[project]) - setup_mock_caches('RegionOne', 'test_project_id') + setup_mock_caches("RegionOne", "test_project_id") - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) - task.cache = {'project_id': "test_project_id"} + task.cache = {"project_id": "test_project_id"} action = SetProjectQuotaAction({}, task=task, order=1) @@ -472,67 +494,57 @@ class ProjectSetupActionTests(AdjutantTestCase): self.assertEqual(action.valid, True) # check the quotas were updated - cinderquota = cinder_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(cinderquota['gigabytes'], 5000) - novaquota = nova_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(novaquota['ram'], 65536) - neutronquota = neutron_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(neutronquota['network'], 3) + cinderquota = cinder_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(cinderquota["gigabytes"], 5000) + novaquota = nova_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(novaquota["ram"], 65536) + neutronquota = neutron_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(neutronquota["network"], 3) # RegionThree, cinder only - self.assertFalse('RegionThree' in nova_cache) - r2_cinderquota = cinder_cache['RegionThree']['test_project_id']['quota'] - self.assertEqual(r2_cinderquota['gigabytes'], 50001) - self.assertEqual(r2_cinderquota['snapshots'], 600) - self.assertEqual(r2_cinderquota['volumes'], 200) + self.assertFalse("RegionThree" in nova_cache) + r2_cinderquota = cinder_cache["RegionThree"]["test_project_id"]["quota"] + self.assertEqual(r2_cinderquota["gigabytes"], 50001) + self.assertEqual(r2_cinderquota["snapshots"], 600) + self.assertEqual(r2_cinderquota["volumes"], 200) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) +@mock.patch("adjutant.common.openstack_clients.get_neutronclient", get_fake_neutron) +@mock.patch("adjutant.common.openstack_clients.get_novaclient", get_fake_novaclient) +@mock.patch("adjutant.common.openstack_clients.get_cinderclient", get_fake_cinderclient) @mock.patch( - 'adjutant.common.user_store.IdentityManager', - FakeManager) -@mock.patch( - 'adjutant.common.openstack_clients.get_neutronclient', - get_fake_neutron) -@mock.patch( - 'adjutant.common.openstack_clients.get_novaclient', - get_fake_novaclient) -@mock.patch( - 'adjutant.common.openstack_clients.get_cinderclient', - get_fake_cinderclient) -@mock.patch( - 'adjutant.common.openstack_clients.get_octaviaclient', - get_fake_octaviaclient) + "adjutant.common.openstack_clients.get_octaviaclient", get_fake_octaviaclient +) class QuotaActionTests(AdjutantTestCase): - def test_update_quota(self): """ Sets a new quota on all services of a project in a single region """ project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} user = mock.Mock() - user.id = 'user_id' + user.id = "user_id" user.name = "test@example.com" user.email = "test@example.com" - user.domain = 'default' + user.domain = "default" user.password = "test_password" setup_identity_cache(projects=[project], users=[user]) - setup_mock_caches('RegionOne', 'test_project_id') + setup_mock_caches("RegionOne", "test_project_id") # Test sending to only a single region - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'project_id': 'test_project_id', - 'size': 'medium', - 'regions': ['RegionOne'], - 'user_id': user.id + "project_id": "test_project_id", + "size": "medium", + "regions": ["RegionOne"], + "user_id": user.id, } action = UpdateProjectQuotasAction(data, task=task, order=1) @@ -544,43 +556,42 @@ class QuotaActionTests(AdjutantTestCase): self.assertEqual(action.valid, True) # check the quotas were updated - cinderquota = cinder_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(cinderquota['gigabytes'], 10000) - novaquota = nova_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(novaquota['ram'], 327680) - neutronquota = neutron_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(neutronquota['network'], 5) + cinderquota = cinder_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(cinderquota["gigabytes"], 10000) + novaquota = nova_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(novaquota["ram"], 327680) + neutronquota = neutron_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(neutronquota["network"], 5) def test_update_quota_multi_region(self): """ Sets a new quota on all services of a project in multiple regions """ project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} user = mock.Mock() - user.id = 'user_id' + user.id = "user_id" user.name = "test@example.com" user.email = "test@example.com" - user.domain = 'default' + user.domain = "default" user.password = "test_password" setup_identity_cache(projects=[project], users=[user]) - setup_mock_caches('RegionOne', project.id) - setup_mock_caches('RegionTwo', project.id) + setup_mock_caches("RegionOne", project.id) + setup_mock_caches("RegionTwo", project.id) - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'project_id': 'test_project_id', - 'size': 'large', - 'domain_id': 'default', - 'regions': ['RegionOne', 'RegionTwo'], - 'user_id': 'user_id' + "project_id": "test_project_id", + "size": "large", + "domain_id": "default", + "regions": ["RegionOne", "RegionTwo"], + "user_id": "user_id", } action = UpdateProjectQuotasAction(data, task=task, order=1) @@ -592,27 +603,26 @@ class QuotaActionTests(AdjutantTestCase): self.assertEqual(action.valid, True) # check the quotas were updated - cinderquota = cinder_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(cinderquota['gigabytes'], 50000) - novaquota = nova_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(novaquota['ram'], 655360) - neutronquota = neutron_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(neutronquota['network'], 10) + cinderquota = cinder_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(cinderquota["gigabytes"], 50000) + novaquota = nova_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(novaquota["ram"], 655360) + neutronquota = neutron_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(neutronquota["network"], 10) - cinderquota = cinder_cache['RegionTwo']['test_project_id']['quota'] - self.assertEqual(cinderquota['gigabytes'], 50000) - novaquota = nova_cache['RegionTwo']['test_project_id']['quota'] - self.assertEqual(novaquota['ram'], 655360) - neutronquota = neutron_cache['RegionTwo']['test_project_id']['quota'] - self.assertEqual(neutronquota['network'], 10) + cinderquota = cinder_cache["RegionTwo"]["test_project_id"]["quota"] + self.assertEqual(cinderquota["gigabytes"], 50000) + novaquota = nova_cache["RegionTwo"]["test_project_id"]["quota"] + self.assertEqual(novaquota["ram"], 655360) + neutronquota = neutron_cache["RegionTwo"]["test_project_id"]["quota"] + self.assertEqual(neutronquota["network"], 10) @conf_utils.modify_conf( CONF, operations={ - "adjutant.quota.sizes_ascending": [ - {'operation': 'override', 'value': []}, - ], - }) + "adjutant.quota.sizes_ascending": [{"operation": "override", "value": []},], + }, + ) def test_update_quota_not_in_sizes_asc(self): """ Tests that the quota will still update to a size even if it is not @@ -620,30 +630,29 @@ class QuotaActionTests(AdjutantTestCase): """ project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} user = mock.Mock() - user.id = 'user_id' + user.id = "user_id" user.name = "test@example.com" user.email = "test@example.com" - user.domain = 'default' + user.domain = "default" user.password = "test_password" setup_identity_cache(projects=[project], users=[user]) - setup_mock_caches('RegionOne', project.id) - setup_mock_caches('RegionTwo', project.id) + setup_mock_caches("RegionOne", project.id) + setup_mock_caches("RegionTwo", project.id) - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'project_id': 'test_project_id', - 'size': 'large', - 'domain_id': 'default', - 'regions': ['RegionOne', 'RegionTwo'], + "project_id": "test_project_id", + "size": "large", + "domain_id": "default", + "regions": ["RegionOne", "RegionTwo"], } action = UpdateProjectQuotasAction(data, task=task, order=1) @@ -655,19 +664,19 @@ class QuotaActionTests(AdjutantTestCase): self.assertEqual(action.valid, True) # check the quotas were updated - cinderquota = cinder_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(cinderquota['gigabytes'], 50000) - novaquota = nova_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(novaquota['ram'], 655360) - neutronquota = neutron_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(neutronquota['network'], 10) + cinderquota = cinder_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(cinderquota["gigabytes"], 50000) + novaquota = nova_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(novaquota["ram"], 655360) + neutronquota = neutron_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(neutronquota["network"], 10) - cinderquota = cinder_cache['RegionTwo']['test_project_id']['quota'] - self.assertEqual(cinderquota['gigabytes'], 50000) - novaquota = nova_cache['RegionTwo']['test_project_id']['quota'] - self.assertEqual(novaquota['ram'], 655360) - neutronquota = neutron_cache['RegionTwo']['test_project_id']['quota'] - self.assertEqual(neutronquota['network'], 10) + cinderquota = cinder_cache["RegionTwo"]["test_project_id"]["quota"] + self.assertEqual(cinderquota["gigabytes"], 50000) + novaquota = nova_cache["RegionTwo"]["test_project_id"]["quota"] + self.assertEqual(novaquota["ram"], 655360) + neutronquota = neutron_cache["RegionTwo"]["test_project_id"]["quota"] + self.assertEqual(neutronquota["network"], 10) @conf_utils.modify_conf( CONF, @@ -683,29 +692,28 @@ class QuotaActionTests(AdjutantTestCase): def test_update_quota_octavia(self): """Tests the quota update of the octavia service""" project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} user = mock.Mock() - user.id = 'user_id' + user.id = "user_id" user.name = "test@example.com" user.email = "test@example.com" - user.domain = 'default' + user.domain = "default" user.password = "test_password" setup_identity_cache(projects=[project], users=[user]) - setup_mock_caches('RegionOne', project.id) + setup_mock_caches("RegionOne", project.id) - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'project_id': 'test_project_id', - 'size': 'large', - 'domain_id': 'default', - 'regions': ['RegionOne'], + "project_id": "test_project_id", + "size": "large", + "domain_id": "default", + "regions": ["RegionOne"], } action = UpdateProjectQuotasAction(data, task=task, order=1) @@ -717,14 +725,14 @@ class QuotaActionTests(AdjutantTestCase): self.assertEqual(action.valid, True) # check the quotas were updated - cinderquota = cinder_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(cinderquota['gigabytes'], 50000) - novaquota = nova_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(novaquota['ram'], 655360) - neutronquota = neutron_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(neutronquota['network'], 10) - octaviaquota = octavia_cache['RegionOne']['test_project_id']['quota'] - self.assertEqual(octaviaquota['load_balancer'], 10) + cinderquota = cinder_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(cinderquota["gigabytes"], 50000) + novaquota = nova_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(novaquota["ram"], 655360) + neutronquota = neutron_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(neutronquota["network"], 10) + octaviaquota = octavia_cache["RegionOne"]["test_project_id"]["quota"] + self.assertEqual(octaviaquota["load_balancer"], 10) @conf_utils.modify_conf( CONF, @@ -740,35 +748,35 @@ class QuotaActionTests(AdjutantTestCase): def test_update_quota_octavia_over_usage(self): """When octavia usage is higher than new quota it won't be changed""" project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} user = mock.Mock() - user.id = 'user_id' + user.id = "user_id" user.name = "test@example.com" user.email = "test@example.com" - user.domain = 'default' + user.domain = "default" user.password = "test_password" setup_identity_cache(projects=[project], users=[user]) - setup_mock_caches('RegionOne', project.id) + setup_mock_caches("RegionOne", project.id) - task = Task.objects.create( - keystone_user={'roles': ['admin']}) + task = Task.objects.create(keystone_user={"roles": ["admin"]}) data = { - 'project_id': 'test_project_id', - 'size': 'small', - 'domain_id': 'default', - 'regions': ['RegionOne'], + "project_id": "test_project_id", + "size": "small", + "domain_id": "default", + "regions": ["RegionOne"], } # setup 2 load balancers - octavia_cache['RegionOne'][project.id]['load_balancer'] = [ - {'id': 'fake_id'}, - {'id': 'fake_id2'}] + octavia_cache["RegionOne"][project.id]["load_balancer"] = [ + {"id": "fake_id"}, + {"id": "fake_id2"}, + ] action = UpdateProjectQuotasAction(data, task=task, order=1) @@ -779,6 +787,6 @@ class QuotaActionTests(AdjutantTestCase): self.assertEqual(action.valid, False) # check the quotas were updated - octaviaquota = octavia_cache['RegionOne']['test_project_id']['quota'] + octaviaquota = octavia_cache["RegionOne"]["test_project_id"]["quota"] # Still set to default - self.assertEqual(octaviaquota['load_balancer'], 1) + self.assertEqual(octaviaquota["load_balancer"], 1) diff --git a/adjutant/actions/v1/tests/test_user_actions.py b/adjutant/actions/v1/tests/test_user_actions.py index 6670827..f590555 100644 --- a/adjutant/actions/v1/tests/test_user_actions.py +++ b/adjutant/actions/v1/tests/test_user_actions.py @@ -17,8 +17,11 @@ import mock from confspirator.tests import utils as conf_utils from adjutant.actions.v1.users import ( - EditUserRolesAction, NewUserAction, ResetUserPasswordAction, - UpdateUserEmailAction) + EditUserRolesAction, + NewUserAction, + ResetUserPasswordAction, + UpdateUserEmailAction, +) from adjutant.api.models import Task from adjutant.common.tests import fake_clients from adjutant.common.tests.fake_clients import setup_identity_cache @@ -26,27 +29,33 @@ from adjutant.common.tests.utils import AdjutantTestCase from adjutant.config import CONF -@mock.patch('adjutant.common.user_store.IdentityManager', - fake_clients.FakeManager) +@mock.patch("adjutant.common.user_store.IdentityManager", fake_clients.FakeManager) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.role_mapping": [ - {'operation': 'override', 'value': { - 'admin': [ - 'project_admin', 'project_mod', 'member', 'heat_stack_owner' - ], - 'project_admin': [ - 'project_mod', 'member', 'heat_stack_owner', 'project_admin', - ], - 'project_mod': [ - 'member', 'heat_stack_owner', 'project_mod', - ], - }}, + { + "operation": "override", + "value": { + "admin": [ + "project_admin", + "project_mod", + "member", + "heat_stack_owner", + ], + "project_admin": [ + "project_mod", + "member", + "heat_stack_owner", + "project_admin", + ], + "project_mod": ["member", "heat_stack_owner", "project_mod",], + }, + }, ], - }) + }, +) class UserActionTests(AdjutantTestCase): - def test_new_user(self): """ Test the default case, all valid. @@ -58,17 +67,18 @@ class UserActionTests(AdjutantTestCase): task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'email': 'test@example.com', - 'project_id': project.id, - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'default', + "email": "test@example.com", + "project_id": project.id, + "roles": ["member"], + "inherited_roles": [], + "domain_id": "default", } action = NewUserAction(data, task=task, order=1) @@ -79,21 +89,20 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) - self.assertEqual( - len(fake_clients.identity_cache['new_users']), 1) + self.assertEqual(len(fake_clients.identity_cache["new_users"]), 1) fake_client = fake_clients.FakeManager() user = fake_client.find_user(name="test@example.com", domain="default") - self.assertEqual(user.email, 'test@example.com') - self.assertEqual(user.password, '123456') + self.assertEqual(user.email, "test@example.com") + self.assertEqual(user.password, "123456") roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member']) + self.assertEqual(roles, ["member"]) def test_new_user_existing(self): """ @@ -102,23 +111,25 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'email': 'test@example.com', - 'project_id': project.id, - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'default', + "email": "test@example.com", + "project_id": project.id, + "roles": ["member"], + "inherited_roles": [], + "domain_id": "default", } action = NewUserAction(data, task=task, order=1) @@ -136,7 +147,7 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member']) + self.assertEqual(roles, ["member"]) def test_new_user_disabled(self): """ @@ -146,24 +157,28 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com", - enabled=False) + name="test@example.com", + password="123", + email="test@example.com", + enabled=False, + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'email': 'test@example.com', - 'project_id': project.id, - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'default', + "email": "test@example.com", + "project_id": project.id, + "roles": ["member"], + "inherited_roles": [], + "domain_id": "default", } action = NewUserAction(data, task=task, order=1) @@ -174,21 +189,21 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) - self.assertEqual(len(fake_clients.identity_cache['users']), 2) + self.assertEqual(len(fake_clients.identity_cache["users"]), 2) fake_client = fake_clients.FakeManager() user = fake_client.find_user(name="test@example.com", domain="default") - self.assertEqual(user.email, 'test@example.com') - self.assertEqual(user.password, '123456') + self.assertEqual(user.email, "test@example.com") + self.assertEqual(user.password, "123456") self.assertTrue(user.enabled) roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member']) + self.assertEqual(roles, ["member"]) def test_new_user_existing_role(self): """ @@ -201,30 +216,33 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'email': 'test@example.com', - 'project_id': project.id, - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'default', + "email": "test@example.com", + "project_id": project.id, + "roles": ["member"], + "inherited_roles": [], + "domain_id": "default", } action = NewUserAction(data, task=task, order=1) @@ -234,7 +252,7 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - self.assertEqual(action.action.state, 'complete') + self.assertEqual(action.action.state, "complete") token_data = {} action.submit(token_data) @@ -243,7 +261,7 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member']) + self.assertEqual(roles, ["member"]) def test_new_user_no_tenant(self): """ @@ -254,17 +272,18 @@ class UserActionTests(AdjutantTestCase): task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'email': 'test@example.com', - 'project_id': 'test_project_id', - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'default', + "email": "test@example.com", + "project_id": "test_project_id", + "roles": ["member"], + "inherited_roles": [], + "domain_id": "default", } action = NewUserAction(data, task=task, order=1) @@ -289,23 +308,25 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'email': 'test@example.com', - 'project_id': 'test_project_id_1', - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'default', + "email": "test@example.com", + "project_id": "test_project_id_1", + "roles": ["member"], + "inherited_roles": [], + "domain_id": "default", } action = NewUserAction(data, task=task, order=1) @@ -323,23 +344,25 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['member'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["member"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'email': 'test@example.com', - 'project_id': project.id, - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'default', + "email": "test@example.com", + "project_id": project.id, + "roles": ["member"], + "inherited_roles": [], + "domain_id": "default", } action = NewUserAction(data, task=task, order=1) @@ -357,30 +380,33 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) task = Task.objects.create( keystone_user={ - 'roles': ['project_admin'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["project_admin"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'email': 'test@example.com', - 'project_id': project.id, - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'not_default', + "email": "test@example.com", + "project_id": project.id, + "roles": ["member"], + "inherited_roles": [], + "domain_id": "not_default", } action = NewUserAction(data, task=task, order=1) @@ -394,21 +420,22 @@ class UserActionTests(AdjutantTestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="gibberish", - email="test@example.com") + name="test@example.com", password="gibberish", email="test@example.com" + ) setup_identity_cache(users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'domain_name': 'Default', - 'email': 'test@example.com', + "domain_name": "Default", + "email": "test@example.com", } action = ResetUserPasswordAction(data, task=task, order=1) @@ -419,13 +446,13 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) self.assertEqual( - fake_clients.identity_cache['users'][user.id].password, - '123456') + fake_clients.identity_cache["users"][user.id].password, "123456" + ) def test_reset_user_password_case_insensitive(self): """ @@ -435,21 +462,22 @@ class UserActionTests(AdjutantTestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="gibberish", - email="test@example.com") + name="test@example.com", password="gibberish", email="test@example.com" + ) setup_identity_cache(users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'domain_name': 'Default', - 'email': 'TEST@example.com', + "domain_name": "Default", + "email": "TEST@example.com", } action = ResetUserPasswordAction(data, task=task, order=1) @@ -460,13 +488,13 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) self.assertEqual( - fake_clients.identity_cache['users'][user.id].password, - '123456') + fake_clients.identity_cache["users"][user.id].password, "123456" + ) def test_reset_user_password_no_user(self): """ @@ -477,14 +505,15 @@ class UserActionTests(AdjutantTestCase): task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'domain_name': 'Default', - 'email': 'test@example.com', + "domain_name": "Default", + "email": "test@example.com", } action = ResetUserPasswordAction(data, task=task, order=1) @@ -506,25 +535,26 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) - setup_identity_cache( - projects=[project], users=[user]) + setup_identity_cache(projects=[project], users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'user_id': user.id, - 'project_id': project.id, - 'roles': ['member', 'project_mod'], - 'inherited_roles': [], - 'remove': False + "domain_id": "default", + "user_id": user.id, + "project_id": project.id, + "roles": ["member", "project_mod"], + "inherited_roles": [], + "remove": False, } action = EditUserRolesAction(data, task=task, order=1) @@ -542,7 +572,7 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(sorted(roles), sorted(['member', 'project_mod'])) + self.assertEqual(sorted(roles), sorted(["member", "project_mod"])) def test_edit_user_roles_add_complete(self): """ @@ -551,38 +581,41 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignments = [ fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_mod", - user={'id': user.id} + user={"id": user.id}, ), ] setup_identity_cache( - projects=[project], users=[user], role_assignments=assignments) + projects=[project], users=[user], role_assignments=assignments + ) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'user_id': user.id, - 'project_id': project.id, - 'roles': ['member', 'project_mod'], - 'inherited_roles': [], - 'remove': False + "domain_id": "default", + "user_id": user.id, + "project_id": project.id, + "roles": ["member", "project_mod"], + "inherited_roles": [], + "remove": False, } action = EditUserRolesAction(data, task=task, order=1) @@ -601,7 +634,7 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member', 'project_mod']) + self.assertEqual(roles, ["member", "project_mod"]) def test_edit_user_roles_remove(self): """ @@ -611,38 +644,41 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignments = [ fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_mod", - user={'id': user.id} + user={"id": user.id}, ), ] setup_identity_cache( - projects=[project], users=[user], role_assignments=assignments) + projects=[project], users=[user], role_assignments=assignments + ) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'user_id': user.id, - 'project_id': project.id, - 'roles': ['project_mod'], - 'inherited_roles': [], - 'remove': True + "domain_id": "default", + "user_id": user.id, + "project_id": project.id, + "roles": ["project_mod"], + "inherited_roles": [], + "remove": True, } action = EditUserRolesAction(data, task=task, order=1) @@ -660,7 +696,7 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member']) + self.assertEqual(roles, ["member"]) def test_edit_user_roles_remove_complete(self): """ @@ -670,31 +706,34 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'user_id': user.id, - 'project_id': project.id, - 'roles': ['project_mod'], - 'inherited_roles': [], - 'remove': True + "domain_id": "default", + "user_id": user.id, + "project_id": project.id, + "roles": ["project_mod"], + "inherited_roles": [], + "remove": True, } action = EditUserRolesAction(data, task=task, order=1) @@ -713,7 +752,7 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member']) + self.assertEqual(roles, ["member"]) def test_edit_user_roles_can_manage_all(self): """ @@ -723,38 +762,41 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignments = [ fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_admin", - user={'id': user.id} + user={"id": user.id}, ), ] setup_identity_cache( - projects=[project], users=[user], role_assignments=assignments) + projects=[project], users=[user], role_assignments=assignments + ) task = Task.objects.create( keystone_user={ - 'roles': ['project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'user_id': user.id, - 'project_id': project.id, - 'roles': ['project_mod'], - 'inherited_roles': [], - 'remove': False + "domain_id": "default", + "user_id": user.id, + "project_id": project.id, + "roles": ["project_mod"], + "inherited_roles": [], + "remove": False, } action = EditUserRolesAction(data, task=task, order=1) @@ -765,7 +807,7 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member', 'project_admin']) + self.assertEqual(roles, ["member", "project_admin"]) def test_edit_user_roles_modified_config(self): """ @@ -775,31 +817,34 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_mod", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) task = Task.objects.create( keystone_user={ - 'roles': ['project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'user_id': user.id, - 'project_id': project.id, - 'roles': ['heat_stack_owner'], - 'inherited_roles': [], - 'remove': False + "domain_id": "default", + "user_id": user.id, + "project_id": project.id, + "roles": ["heat_stack_owner"], + "inherited_roles": [], + "remove": False, } action = EditUserRolesAction(data, task=task, order=1) @@ -809,16 +854,16 @@ class UserActionTests(AdjutantTestCase): # Change config with conf_utils.modify_conf( - CONF, - operations={ - "adjutant.identity.role_mapping": [ - {'operation': 'update', 'value': { - 'project_mod': [ - 'member', 'project_mod', - ], - }}, - ], - }): + CONF, + operations={ + "adjutant.identity.role_mapping": [ + { + "operation": "update", + "value": {"project_mod": ["member", "project_mod",],}, + }, + ], + }, + ): action.approve() self.assertEqual(action.valid, False) @@ -837,19 +882,26 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['project_mod', 'heat_stack_owner']) + self.assertEqual(roles, ["project_mod", "heat_stack_owner"]) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.role_mapping": [ - {'operation': 'update', 'value': { - 'project_mod': [ - 'member', 'heat_stack_owner', 'project_mod', 'new_role', - ], - }}, + { + "operation": "update", + "value": { + "project_mod": [ + "member", + "heat_stack_owner", + "project_mod", + "new_role", + ], + }, + }, ], - }) + }, + ) def test_edit_user_roles_modified_config_add(self): """ Tests that the role mappings do come from config and a new role @@ -858,35 +910,38 @@ class UserActionTests(AdjutantTestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_mod", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) new_role = fake_clients.FakeRole("new_role") - fake_clients.identity_cache['roles'][new_role.id] = new_role + fake_clients.identity_cache["roles"][new_role.id] = new_role task = Task.objects.create( keystone_user={ - 'roles': ['project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'domain_id': 'default', - 'user_id': user.id, - 'project_id': project.id, - 'roles': ['new_role'], - 'inherited_roles': [], - 'remove': False + "domain_id": "default", + "user_id": user.id, + "project_id": project.id, + "roles": ["new_role"], + "inherited_roles": [], + "remove": False, } action = EditUserRolesAction(data, task=task, order=1) @@ -904,16 +959,17 @@ class UserActionTests(AdjutantTestCase): fake_client = fake_clients.FakeManager() roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['project_mod', 'new_role']) + self.assertEqual(roles, ["project_mod", "new_role"]) # Simple positive tests for when USERNAME_IS_EMAIL=False @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_create_user_email_not_username(self): """ Test the default case, all valid. @@ -926,18 +982,19 @@ class UserActionTests(AdjutantTestCase): task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': project.id, - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": project.id, + "project_domain_id": "default", + } + ) data = { - 'username': 'test_user', - 'email': 'test@example.com', - 'project_id': project.id, - 'roles': ['member'], - 'inherited_roles': [], - 'domain_id': 'default', + "username": "test_user", + "email": "test@example.com", + "project_id": project.id, + "roles": ["member"], + "inherited_roles": [], + "domain_id": "default", } action = NewUserAction(data, task=task, order=1) @@ -948,51 +1005,53 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) - self.assertEqual(len(fake_clients.identity_cache['users']), 2) + self.assertEqual(len(fake_clients.identity_cache["users"]), 2) fake_client = fake_clients.FakeManager() user = fake_client.find_user(name="test_user", domain="default") - self.assertEqual(user.email, 'test@example.com') - self.assertEqual(user.password, '123456') + self.assertEqual(user.email, "test@example.com") + self.assertEqual(user.password, "123456") self.assertTrue(user.enabled) roles = fake_client._get_roles_as_names(user, project) - self.assertEqual(roles, ['member']) + self.assertEqual(roles, ["member"]) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_reset_user_email_not_username(self): """ Base case, existing user. Username not email address """ user = fake_clients.FakeUser( - name="test_user", password="gibberish", - email="test@example.com") + name="test_user", password="gibberish", email="test@example.com" + ) setup_identity_cache(users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'username': "test_user", - 'domain_name': 'Default', - 'email': 'test@example.com', + "username": "test_user", + "domain_name": "Default", + "email": "test@example.com", } action = ResetUserPasswordAction(data, task=task, order=1) @@ -1003,7 +1062,7 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) @@ -1011,16 +1070,17 @@ class UserActionTests(AdjutantTestCase): user = fake_client.find_user(name="test_user", domain="default") - self.assertEqual(user.email, 'test@example.com') - self.assertEqual(user.password, '123456') + self.assertEqual(user.email, "test@example.com") + self.assertEqual(user.password, "123456") @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_reset_user_password_case_insensitive_not_username(self): """ Existing user, ensure action is case insensitive. @@ -1028,22 +1088,23 @@ class UserActionTests(AdjutantTestCase): USERNAME_IS_EMAIL=False """ user = fake_clients.FakeUser( - name="test_USER", password="gibberish", - email="test@example.com") + name="test_USER", password="gibberish", email="test@example.com" + ) setup_identity_cache(users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['admin', 'project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["admin", "project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'domain_name': 'Default', - 'username': 'test_USER', - 'email': 'TEST@example.com', + "domain_name": "Default", + "username": "test_USER", + "email": "TEST@example.com", } action = ResetUserPasswordAction(data, task=task, order=1) @@ -1054,34 +1115,35 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - token_data = {'password': '123456'} + token_data = {"password": "123456"} action.submit(token_data) self.assertEqual(action.valid, True) self.assertEqual( - fake_clients.identity_cache['users'][user.id].password, - '123456') + fake_clients.identity_cache["users"][user.id].password, "123456" + ) def test_update_email(self): """ Base test case for user updating email address. """ user = fake_clients.FakeUser( - name="test@example.com", password="gibberish", - email="test@example.com") + name="test@example.com", password="gibberish", email="test@example.com" + ) setup_identity_cache(users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'new_email': 'new_test@example.com', - 'user_id': user.id, + "new_email": "new_test@example.com", + "user_id": user.id, } action = UpdateUserEmailAction(data, task=task, order=1) @@ -1092,18 +1154,18 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - token_data = {'confirm': True} + token_data = {"confirm": True} action.submit(token_data) self.assertEqual(action.valid, True) self.assertEqual( - fake_clients.identity_cache['users'][user.id].email, - 'new_test@example.com') + fake_clients.identity_cache["users"][user.id].email, "new_test@example.com" + ) self.assertEqual( - fake_clients.identity_cache['users'][user.id].name, - 'new_test@example.com') + fake_clients.identity_cache["users"][user.id].name, "new_test@example.com" + ) def test_update_email_invalid_user(self): """ @@ -1113,14 +1175,15 @@ class UserActionTests(AdjutantTestCase): task = Task.objects.create( keystone_user={ - 'roles': ['project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'new_email': 'new_test@example.com', - 'user_id': "non_user_id", + "new_email": "new_test@example.com", + "user_id": "non_user_id", } action = UpdateUserEmailAction(data, task=task, order=1) @@ -1131,7 +1194,7 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, False) - token_data = {'confirm': True} + token_data = {"confirm": True} action.submit(token_data) self.assertEqual(action.valid, False) @@ -1140,29 +1203,31 @@ class UserActionTests(AdjutantTestCase): CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_update_email_username_not_email(self): """ Test case for a user attempting to update with an invalid email. """ user = fake_clients.FakeUser( - name="test_user", password="gibberish", - email="test@example.com") + name="test_user", password="gibberish", email="test@example.com" + ) setup_identity_cache(users=[user]) task = Task.objects.create( keystone_user={ - 'roles': ['project_mod'], - 'project_id': 'test_project_id', - 'project_domain_id': 'default', - }) + "roles": ["project_mod"], + "project_id": "test_project_id", + "project_domain_id": "default", + } + ) data = { - 'new_email': 'new_testexample.com', - 'user_id': user.id, + "new_email": "new_testexample.com", + "user_id": user.id, } action = UpdateUserEmailAction(data, task=task, order=1) @@ -1173,13 +1238,13 @@ class UserActionTests(AdjutantTestCase): action.approve() self.assertEqual(action.valid, True) - action.submit({'confirm': True}) + action.submit({"confirm": True}) self.assertEqual(action.valid, True) self.assertEqual( - fake_clients.identity_cache['users'][user.id].email, - 'new_testexample.com') + fake_clients.identity_cache["users"][user.id].email, "new_testexample.com" + ) self.assertEqual( - fake_clients.identity_cache['users'][user.id].name, - 'test_user') + fake_clients.identity_cache["users"][user.id].name, "test_user" + ) diff --git a/adjutant/actions/v1/users.py b/adjutant/actions/v1/users.py index defe50b..5c79799 100644 --- a/adjutant/actions/v1/users.py +++ b/adjutant/actions/v1/users.py @@ -18,7 +18,11 @@ from confspirator import fields from adjutant.config import CONF from adjutant.common import user_store from adjutant.actions.v1.base import ( - UserNameAction, UserIdAction, UserMixin, ProjectMixin) + UserNameAction, + UserIdAction, + UserMixin, + ProjectMixin, +) from adjutant.actions.v1 import serializers from adjutant.actions.utils import validate_steps @@ -32,12 +36,12 @@ class NewUserAction(UserNameAction, ProjectMixin, UserMixin): """ required = [ - 'username', - 'email', - 'project_id', - 'roles', - 'inherited_roles', - 'domain_id', + "username", + "email", + "project_id", + "roles", + "inherited_roles", + "domain_id", ] serializer = serializers.NewUserSerializer @@ -51,37 +55,43 @@ class NewUserAction(UserNameAction, ProjectMixin, UserMixin): if not user: self.add_note( "No user present with username '%s'. " - "Need to create new user." % self.username) + "Need to create new user." % self.username + ) if not id_manager.can_edit_users: self.add_note( - 'Identity backend does not support user editing, ' - 'cannot create new user.') + "Identity backend does not support user editing, " + "cannot create new user." + ) return False self.action.need_token = True # add to cache to use in template - self.action.task.cache['user_state'] = "default" + self.action.task.cache["user_state"] = "default" self.set_token_fields(["password"]) return True - if (not CONF.identity.username_is_email - and getattr(user, 'email', None) != self.email): + if ( + not CONF.identity.username_is_email + and getattr(user, "email", None) != self.email + ): self.add_note( - 'Found matching username, but email did not match. ' - 'Reporting as invalid.') + "Found matching username, but email did not match. " + "Reporting as invalid." + ) return False if not user.enabled: self.add_note( - "Existing disabled user '%s' with matching email." % - self.email) + "Existing disabled user '%s' with matching email." % self.email + ) if not id_manager.can_edit_users: self.add_note( - 'Identity backend does not support user editing, ' - 'cannot renable user.') + "Identity backend does not support user editing, " + "cannot renable user." + ) return False self.action.need_token = True self.action.state = "disabled" # add to cache to use in template - self.action.task.cache['user_state'] = "disabled" + self.action.task.cache["user_state"] = "disabled" # as they are disabled we'll reset their password self.set_token_fields(["password"]) return True @@ -93,30 +103,29 @@ class NewUserAction(UserNameAction, ProjectMixin, UserMixin): if not missing: self.action.need_token = False self.action.state = "complete" - self.add_note( - 'Existing user already has roles.' - ) + self.add_note("Existing user already has roles.") else: self.roles = list(missing) self.action.need_token = True self.set_token_fields(["confirm"]) self.action.state = "existing" # add to cache to use in template - self.action.task.cache['user_state'] = "existing" - self.add_note( - 'Existing user with matching email missing roles.') + self.action.task.cache["user_state"] = "existing" + self.add_note("Existing user with matching email missing roles.") return True def _validate(self): - self.action.valid = validate_steps([ - self._validate_role_permissions, - self._validate_keystone_user_domain_id, - self._validate_keystone_user_project_id, - self._validate_domain_id, - self._validate_project_id, - self._validate_target_user, - ]) + self.action.valid = validate_steps( + [ + self._validate_role_permissions, + self._validate_keystone_user_domain_id, + self._validate_keystone_user_project_id, + self._validate_domain_id, + self._validate_project_id, + self._validate_target_user, + ] + ) self.action.save() def _prepare(self): @@ -134,13 +143,14 @@ class NewUserAction(UserNameAction, ProjectMixin, UserMixin): if self.action.state == "default": # default action: Create a new user in the tenant and add roles - user = self.create_user(token_data['password']) + user = self.create_user(token_data["password"]) self.grant_roles(user, self.roles, self.project_id) self.grant_roles(user, self.inherited_roles, self.project_id, True) self.add_note( - 'User %s has been created, with roles %s in project %s.' - % (self.username, self.roles, self.project_id)) + "User %s has been created, with roles %s in project %s." + % (self.username, self.roles, self.project_id) + ) elif self.action.state == "disabled": # first re-enable user @@ -148,14 +158,14 @@ class NewUserAction(UserNameAction, ProjectMixin, UserMixin): self.enable_user(user) self.grant_roles(user, self.roles, self.project_id) self.grant_roles(user, self.inherited_roles, self.project_id, True) - self.update_password(token_data['password']) + self.update_password(token_data["password"]) - self.add_note('User %s password has been changed.' % self.username) + self.add_note("User %s password has been changed." % self.username) self.add_note( - 'Existing user %s has been re-enabled and given roles %s' - ' in project %s.' - % (self.username, self.roles, self.project_id)) + "Existing user %s has been re-enabled and given roles %s" + " in project %s." % (self.username, self.roles, self.project_id) + ) elif self.action.state == "existing": # Existing action: only add roles. @@ -164,13 +174,15 @@ class NewUserAction(UserNameAction, ProjectMixin, UserMixin): self.grant_roles(user, self.inherited_roles, self.project_id, True) self.add_note( - 'Existing user %s has been given roles %s in project %s.' - % (self.username, self.roles, self.project_id)) + "Existing user %s has been given roles %s in project %s." + % (self.username, self.roles, self.project_id) + ) elif self.action.state == "complete": # complete action: nothing to do. self.add_note( - 'Existing user %s already had roles %s in project %s.' - % (self.username, self.roles, self.project_id)) + "Existing user %s already had roles %s in project %s." + % (self.username, self.roles, self.project_id) + ) class ResetUserPasswordAction(UserNameAction, UserMixin): @@ -178,11 +190,7 @@ class ResetUserPasswordAction(UserNameAction, UserMixin): Simple action to reset a password for a given user. """ - required = [ - 'domain_name', - 'username', - 'email' - ] + required = ["domain_name", "username", "email"] serializer = serializers.ResetUserPasswordSerializer @@ -192,7 +200,7 @@ class ResetUserPasswordAction(UserNameAction, UserMixin): "blacklisted_roles", help_text="Users with these roles cannot reset their passwords.", default=[], - sample_default=['admin'], + sample_default=["admin"], ), ], ) @@ -210,7 +218,7 @@ class ResetUserPasswordAction(UserNameAction, UserMixin): user_roles.extend(role.name for role in roles) if set(self.config.blacklisted_roles) & set(user_roles): - self.add_note('Cannot reset users with blacklisted roles.') + self.add_note("Cannot reset users with blacklisted roles.") return False return True @@ -219,26 +227,28 @@ class ResetUserPasswordAction(UserNameAction, UserMixin): # NOTE(adriant): We only need to check the USERNAME_IS_EMAIL=False # case since '_validate_username_exists' will ensure the True case if not CONF.identity.username_is_email: - if (self.user and ( - getattr(self.user, 'email', None).lower() - != self.email.lower())): - self.add_note('Existing user with non-matching email.') + if self.user and ( + getattr(self.user, "email", None).lower() != self.email.lower() + ): + self.add_note("Existing user with non-matching email.") return False self.action.need_token = True self.set_token_fields(["password"]) - self.add_note('Existing user with matching email.') + self.add_note("Existing user with matching email.") return True def _validate(self): # Here, the order of validation matters # as each one adds new class variables - self.action.valid = validate_steps([ - self._validate_domain_name, - self._validate_username_exists, - self._validate_user_roles, - self._validate_user_email, - ]) + self.action.valid = validate_steps( + [ + self._validate_domain_name, + self._validate_username_exists, + self._validate_user_roles, + self._validate_user_email, + ] + ) self.action.save() def _prepare(self): @@ -254,8 +264,8 @@ class ResetUserPasswordAction(UserNameAction, UserMixin): if not self.valid: return - self.update_password(token_data['password']) - self.add_note('User %s password has been changed.' % self.username) + self.update_password(token_data["password"]) + self.add_note("User %s password has been changed." % self.username) class EditUserRolesAction(UserIdAction, ProjectMixin, UserMixin): @@ -264,13 +274,7 @@ class EditUserRolesAction(UserIdAction, ProjectMixin, UserMixin): on a user for the given project. """ - required = [ - 'project_id', - 'user_id', - 'roles', - 'inherited_roles', - 'remove' - ] + required = ["project_id", "user_id", "roles", "inherited_roles", "remove"] serializer = serializers.EditUserRolesSerializer @@ -278,7 +282,7 @@ class EditUserRolesAction(UserIdAction, ProjectMixin, UserMixin): # Get target user user = self._get_target_user() if not user: - self.add_note('No user present with user_id') + self.add_note("No user present with user_id") return False return True @@ -288,37 +292,31 @@ class EditUserRolesAction(UserIdAction, ProjectMixin, UserMixin): project = id_manager.get_project(self.project_id) # user roles current_roles = id_manager.get_roles(user, project) - current_inherited_roles = id_manager.get_roles( - user, project, inherited=True) + current_inherited_roles = id_manager.get_roles(user, project, inherited=True) current_roles = {role.name for role in current_roles} - current_inherited_roles = { - role.name for role in current_inherited_roles} + current_inherited_roles = {role.name for role in current_inherited_roles} if self.remove: remaining = set(current_roles) & set(self.roles) - remaining_inherited = ( - set(current_inherited_roles) & set(self.inherited_roles)) + remaining_inherited = set(current_inherited_roles) & set( + self.inherited_roles + ) if not remaining and not remaining_inherited: self.action.state = "complete" - self.add_note( - "User doesn't have roles to remove.") + self.add_note("User doesn't have roles to remove.") else: self.roles = list(remaining) self.inherited_roles = list(remaining_inherited) - self.add_note( - 'User has roles to remove.') + self.add_note("User has roles to remove.") else: missing = set(self.roles) - set(current_roles) - missing_inherited = ( - set(self.inherited_roles) - set(current_inherited_roles)) + missing_inherited = set(self.inherited_roles) - set(current_inherited_roles) if not missing and not missing_inherited: self.action.state = "complete" - self.add_note( - 'User already has roles.') + self.add_note("User already has roles.") else: self.roles = list(missing) self.inherited_roles = list(missing_inherited) - self.add_note( - 'User missing roles.') + self.add_note("User missing roles.") # All paths are valid here # We've just set state and roles that need to be changed. return True @@ -327,18 +325,21 @@ class EditUserRolesAction(UserIdAction, ProjectMixin, UserMixin): id_manager = user_store.IdentityManager() - current_user_roles = id_manager.get_roles(project=self.project_id, - user=self.user_id) + current_user_roles = id_manager.get_roles( + project=self.project_id, user=self.user_id + ) current_user_roles = [role.name for role in current_user_roles] current_roles_manageable = self.are_roles_manageable( - self.action.task.keystone_user['roles'], current_user_roles) + self.action.task.keystone_user["roles"], current_user_roles + ) all_roles = set() all_roles.update(self.roles) all_roles.update(self.inherited_roles) new_roles_manageable = self.are_roles_manageable( - self.action.task.keystone_user['roles'], all_roles) + self.action.task.keystone_user["roles"], all_roles + ) if new_roles_manageable and current_roles_manageable: self.add_note("All user roles are manageable.") @@ -347,13 +348,15 @@ class EditUserRolesAction(UserIdAction, ProjectMixin, UserMixin): return False def _validate(self): - self.action.valid = validate_steps([ - self._validate_keystone_user_project_id, - self._validate_role_permissions, - self._validate_project_id, - self._validate_target_user, - self._validate_user_roles, - ]) + self.action.valid = validate_steps( + [ + self._validate_keystone_user_project_id, + self._validate_role_permissions, + self._validate_project_id, + self._validate_target_user, + self._validate_user_roles, + ] + ) self.action.save() def _prepare(self): @@ -371,37 +374,47 @@ class EditUserRolesAction(UserIdAction, ProjectMixin, UserMixin): if self.action.state == "default": user = self._get_target_user() - self._user_roles_edit(user, self.roles, self.project_id, - remove=self.remove) - self._user_roles_edit(user, self.inherited_roles, self.project_id, - remove=self.remove, inherited=True) + self._user_roles_edit(user, self.roles, self.project_id, remove=self.remove) + self._user_roles_edit( + user, + self.inherited_roles, + self.project_id, + remove=self.remove, + inherited=True, + ) if self.remove and self.roles: self.add_note( - 'User %s has had roles %s removed from project %s.' - % (self.user_id, self.roles, self.project_id)) + "User %s has had roles %s removed from project %s." + % (self.user_id, self.roles, self.project_id) + ) if self.remove and self.inherited_roles: self.add_note( - 'User %s has had inherited roles %s ' - 'removed from project %s.' - % (self.user_id, self.inherited_roles, self.project_id)) + "User %s has had inherited roles %s " + "removed from project %s." + % (self.user_id, self.inherited_roles, self.project_id) + ) if self.roles: self.add_note( - 'User %s has been given roles %s in project %s.' - % (self.user_id, self.roles, self.project_id)) + "User %s has been given roles %s in project %s." + % (self.user_id, self.roles, self.project_id) + ) if self.inherited_roles: self.add_note( - 'User %s has been given inherited roles %s in project %s.' - % (self.user_id, self.inherited_roles, self.project_id)) + "User %s has been given inherited roles %s in project %s." + % (self.user_id, self.inherited_roles, self.project_id) + ) elif self.action.state == "complete": if self.remove: self.add_note( "User %s didn't have roles %s in project %s." - % (self.user_id, self.roles, self.project_id)) + % (self.user_id, self.roles, self.project_id) + ) else: self.add_note( - 'User %s already had roles %s in project %s.' - % (self.user_id, self.roles, self.project_id)) + "User %s already had roles %s in project %s." + % (self.user_id, self.roles, self.project_id) + ) class UpdateUserEmailAction(UserIdAction, UserMixin): @@ -410,8 +423,8 @@ class UpdateUserEmailAction(UserIdAction, UserMixin): """ required = [ - 'user_id', - 'new_email', + "user_id", + "new_email", ] serializer = serializers.UpdateUserEmailSerializer @@ -421,10 +434,9 @@ class UpdateUserEmailAction(UserIdAction, UserMixin): return self.new_email def _validate(self): - self.action.valid = validate_steps([ - self._validate_user, - self._validate_email_not_in_use, - ]) + self.action.valid = validate_steps( + [self._validate_user, self._validate_email_not_in_use,] + ) self.action.save() def _validate_user(self): @@ -436,8 +448,7 @@ class UpdateUserEmailAction(UserIdAction, UserMixin): def _validate_email_not_in_use(self): if CONF.identity.username_is_email: - self.domain_id = self.action.task.keystone_user[ - 'project_domain_id'] + self.domain_id = self.action.task.keystone_user["project_domain_id"] id_manager = user_store.IdentityManager() @@ -469,5 +480,7 @@ class UpdateUserEmailAction(UserIdAction, UserMixin): if CONF.identity.username_is_email: self.update_user_name(self.new_email, user=self.user) - self.add_note('The email for user %s has been changed to %s.' - % (self.old_username, self.new_email)) + self.add_note( + "The email for user %s has been changed to %s." + % (self.old_username, self.new_email) + ) diff --git a/adjutant/api/exception_handler.py b/adjutant/api/exception_handler.py index 3ab576e..0b2fb40 100644 --- a/adjutant/api/exception_handler.py +++ b/adjutant/api/exception_handler.py @@ -23,7 +23,7 @@ from adjutant import exceptions from adjutant.notifications.utils import create_notification -LOG = getLogger('adjutant') +LOG = getLogger("adjutant") def exception_handler(exc, context): @@ -38,17 +38,17 @@ def exception_handler(exc, context): if isinstance(exc, exceptions.BaseAPIException): if isinstance(exc.message, (list, dict)): - data = {'errors': exc.message} + data = {"errors": exc.message} else: - data = {'errors': [exc.message]} + data = {"errors": [exc.message]} note_data = data if isinstance(exc, exceptions.TaskActionsFailed): if exc.internal_message: if isinstance(exc.internal_message, (list, dict)): - note_data = {'errors': exc.internal_message} + note_data = {"errors": exc.internal_message} else: - note_data = {'errors': [exc.internal_message]} + note_data = {"errors": [exc.internal_message]} create_notification(exc.task, note_data, error=True) LOG.info("(%s) - %s" % (now, exc)) diff --git a/adjutant/api/migrations/0001_initial.py b/adjutant/api/migrations/0001_initial.py index 9b5679d..de8d531 100644 --- a/adjutant/api/migrations/0001_initial.py +++ b/adjutant/api/migrations/0001_initial.py @@ -9,50 +9,78 @@ import adjutant.api.models class Migration(migrations.Migration): - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Notification', + name="Notification", fields=[ - ('uuid', models.CharField(default=adjutant.api.models.hex_uuid, max_length=32, serialize=False, primary_key=True)), - ('notes', jsonfield.fields.JSONField(default={})), - ('error', models.BooleanField(default=False, db_index=True)), - ('created_on', models.DateTimeField(default=django.utils.timezone.now)), - ('acknowledged', models.BooleanField(default=False, db_index=True)), + ( + "uuid", + models.CharField( + default=adjutant.api.models.hex_uuid, + max_length=32, + serialize=False, + primary_key=True, + ), + ), + ("notes", jsonfield.fields.JSONField(default={})), + ("error", models.BooleanField(default=False, db_index=True)), + ("created_on", models.DateTimeField(default=django.utils.timezone.now)), + ("acknowledged", models.BooleanField(default=False, db_index=True)), ], ), migrations.CreateModel( - name='Task', + name="Task", fields=[ - ('uuid', models.CharField(default=adjutant.api.models.hex_uuid, max_length=32, serialize=False, primary_key=True)), - ('hash_key', models.CharField(max_length=32, db_index=True)), - ('ip_address', models.GenericIPAddressField()), - ('keystone_user', jsonfield.fields.JSONField(default={})), - ('project_id', models.CharField(max_length=32, null=True, db_index=True)), - ('task_type', models.CharField(max_length=100, db_index=True)), - ('action_notes', jsonfield.fields.JSONField(default={})), - ('cancelled', models.BooleanField(default=False, db_index=True)), - ('approved', models.BooleanField(default=False, db_index=True)), - ('completed', models.BooleanField(default=False, db_index=True)), - ('created_on', models.DateTimeField(default=django.utils.timezone.now)), - ('approved_on', models.DateTimeField(null=True)), - ('completed_on', models.DateTimeField(null=True)), + ( + "uuid", + models.CharField( + default=adjutant.api.models.hex_uuid, + max_length=32, + serialize=False, + primary_key=True, + ), + ), + ("hash_key", models.CharField(max_length=32, db_index=True)), + ("ip_address", models.GenericIPAddressField()), + ("keystone_user", jsonfield.fields.JSONField(default={})), + ( + "project_id", + models.CharField(max_length=32, null=True, db_index=True), + ), + ("task_type", models.CharField(max_length=100, db_index=True)), + ("action_notes", jsonfield.fields.JSONField(default={})), + ("cancelled", models.BooleanField(default=False, db_index=True)), + ("approved", models.BooleanField(default=False, db_index=True)), + ("completed", models.BooleanField(default=False, db_index=True)), + ("created_on", models.DateTimeField(default=django.utils.timezone.now)), + ("approved_on", models.DateTimeField(null=True)), + ("completed_on", models.DateTimeField(null=True)), ], ), migrations.CreateModel( - name='Token', + name="Token", fields=[ - ('token', models.CharField(max_length=32, serialize=False, primary_key=True)), - ('created_on', models.DateTimeField(default=django.utils.timezone.now)), - ('expires', models.DateTimeField(db_index=True)), - ('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Task')), + ( + "token", + models.CharField(max_length=32, serialize=False, primary_key=True), + ), + ("created_on", models.DateTimeField(default=django.utils.timezone.now)), + ("expires", models.DateTimeField(db_index=True)), + ( + "task", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.Task" + ), + ), ], ), migrations.AddField( - model_name='notification', - name='task', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Task'), + model_name="notification", + name="task", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.Task" + ), ), ] diff --git a/adjutant/api/migrations/0002_auto_20160815_2249.py b/adjutant/api/migrations/0002_auto_20160815_2249.py index 3cd1ba2..6c3aa63 100644 --- a/adjutant/api/migrations/0002_auto_20160815_2249.py +++ b/adjutant/api/migrations/0002_auto_20160815_2249.py @@ -7,13 +7,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('api', '0001_initial'), + ("api", "0001_initial"), ] operations = [ migrations.AlterField( - model_name='task', - name='hash_key', + model_name="task", + name="hash_key", field=models.CharField(max_length=64, db_index=True), ), ] diff --git a/adjutant/api/migrations/0003_task_approved_by.py b/adjutant/api/migrations/0003_task_approved_by.py index 7c83800..4680640 100644 --- a/adjutant/api/migrations/0003_task_approved_by.py +++ b/adjutant/api/migrations/0003_task_approved_by.py @@ -8,13 +8,13 @@ import jsonfield.fields class Migration(migrations.Migration): dependencies = [ - ('api', '0002_auto_20160815_2249'), + ("api", "0002_auto_20160815_2249"), ] operations = [ migrations.AddField( - model_name='task', - name='approved_by', + model_name="task", + name="approved_by", field=jsonfield.fields.JSONField(default={}), ), ] diff --git a/adjutant/api/migrations/0004_auto_20160929_0317.py b/adjutant/api/migrations/0004_auto_20160929_0317.py index 9188ace..628340a 100644 --- a/adjutant/api/migrations/0004_auto_20160929_0317.py +++ b/adjutant/api/migrations/0004_auto_20160929_0317.py @@ -7,13 +7,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('api', '0003_task_approved_by'), + ("api", "0003_task_approved_by"), ] operations = [ migrations.AlterField( - model_name='task', - name='project_id', + model_name="task", + name="project_id", field=models.CharField(max_length=64, null=True, db_index=True), ), ] diff --git a/adjutant/api/migrations/0005_auto_20190610_0209.py b/adjutant/api/migrations/0005_auto_20190610_0209.py index 9f7568b..79b69bd 100644 --- a/adjutant/api/migrations/0005_auto_20190610_0209.py +++ b/adjutant/api/migrations/0005_auto_20190610_0209.py @@ -10,16 +10,13 @@ class Migration(migrations.Migration): atomic = False dependencies = [ - ('api', '0004_auto_20160929_0317'), + ("api", "0004_auto_20160929_0317"), ] operations = [ migrations.SeparateDatabaseAndState( database_operations=[ - migrations.AlterModelTable( - name='task', - table='tasks_task', - ), + migrations.AlterModelTable(name="task", table="tasks_task",), ], ), ] diff --git a/adjutant/api/migrations/0006_auto_20190610_0209.py b/adjutant/api/migrations/0006_auto_20190610_0209.py index 7608e63..9338814 100644 --- a/adjutant/api/migrations/0006_auto_20190610_0209.py +++ b/adjutant/api/migrations/0006_auto_20190610_0209.py @@ -9,14 +9,16 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('tasks', '0001_initial'), - ('actions', '0003_auto_20190610_0205'), + ("tasks", "0001_initial"), + ("actions", "0003_auto_20190610_0205"), ] operations = [ migrations.AlterField( - model_name='token', - name='task', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tasks.Task'), + model_name="token", + name="task", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="tasks.Task" + ), ), ] diff --git a/adjutant/api/migrations/0007_auto_20190610_0209.py b/adjutant/api/migrations/0007_auto_20190610_0209.py index f348f74..5b59647 100644 --- a/adjutant/api/migrations/0007_auto_20190610_0209.py +++ b/adjutant/api/migrations/0007_auto_20190610_0209.py @@ -9,14 +9,16 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('tasks', '0001_initial'), - ('actions', '0003_auto_20190610_0205'), + ("tasks", "0001_initial"), + ("actions", "0003_auto_20190610_0205"), ] operations = [ migrations.AlterField( - model_name='notification', - name='task', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tasks.Task'), + model_name="notification", + name="task", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="tasks.Task" + ), ), ] diff --git a/adjutant/api/migrations/0008_auto_20190610_0209.py b/adjutant/api/migrations/0008_auto_20190610_0209.py index e15b5aa..05d8d8c 100644 --- a/adjutant/api/migrations/0008_auto_20190610_0209.py +++ b/adjutant/api/migrations/0008_auto_20190610_0209.py @@ -8,19 +8,15 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('api', '0005_auto_20190610_0209'), - ('tasks', '0001_initial'), - ('actions', '0004_auto_20190610_0209'), - ('api', '0006_auto_20190610_0209'), - ('api', '0007_auto_20190610_0209'), + ("api", "0005_auto_20190610_0209"), + ("tasks", "0001_initial"), + ("actions", "0004_auto_20190610_0209"), + ("api", "0006_auto_20190610_0209"), + ("api", "0007_auto_20190610_0209"), ] operations = [ migrations.SeparateDatabaseAndState( - state_operations=[ - migrations.DeleteModel( - name='Task', - ), - ], + state_operations=[migrations.DeleteModel(name="Task",),], ), ] diff --git a/adjutant/api/models.py b/adjutant/api/models.py index db79e95..803535d 100644 --- a/adjutant/api/models.py +++ b/adjutant/api/models.py @@ -40,7 +40,7 @@ class Token(models.Model): "task_type": self.task.task_type, "token": self.token, "created_on": self.created_on, - "expires": self.expires + "expires": self.expires, } @property @@ -53,8 +53,7 @@ class Notification(models.Model): Notification linked to a task with some notes. """ - uuid = models.CharField(max_length=32, default=hex_uuid, - primary_key=True) + uuid = models.CharField(max_length=32, default=hex_uuid, primary_key=True) notes = JSONField(default={}) task = models.ForeignKey(Task, on_delete=models.CASCADE) error = models.BooleanField(default=False, db_index=True) @@ -68,5 +67,5 @@ class Notification(models.Model): "task": self.task.uuid, "error": self.error, "acknowledged": self.acknowledged, - "created_on": self.created_on + "created_on": self.created_on, } diff --git a/adjutant/api/urls.py b/adjutant/api/urls.py index 303cadd..16f060b 100644 --- a/adjutant/api/urls.py +++ b/adjutant/api/urls.py @@ -22,15 +22,15 @@ from adjutant.api.views import build_version_details from adjutant.api.v1 import views as views_v1 urlpatterns = [ - url(r'^$', views.VersionView.as_view()), + url(r"^$", views.VersionView.as_view()), ] # NOTE(adriant): make this conditional once we have a v2. -build_version_details('1.0', 'CURRENT', relative_endpoint='v1/') -urlpatterns.append(url(r'^v1/?$', views_v1.V1VersionEndpoint.as_view())) -urlpatterns.append(url(r'^v1/', include('adjutant.api.v1.urls'))) +build_version_details("1.0", "CURRENT", relative_endpoint="v1/") +urlpatterns.append(url(r"^v1/?$", views_v1.V1VersionEndpoint.as_view())) +urlpatterns.append(url(r"^v1/", include("adjutant.api.v1.urls"))) if settings.DEBUG: - schema_view = get_swagger_view(title='Adjutant API') - urlpatterns.append(url(r'^docs/', schema_view)) + schema_view = get_swagger_view(title="Adjutant API") + urlpatterns.append(url(r"^docs/", schema_view)) diff --git a/adjutant/api/utils.py b/adjutant/api/utils.py index 096d6bd..d7ec3a2 100644 --- a/adjutant/api/utils.py +++ b/adjutant/api/utils.py @@ -27,17 +27,17 @@ def require_roles(roles, func, *args, **kwargs): """ request = args[1] req_roles = set(roles) - if not request.keystone_user.get('authenticated', False): - return Response({'errors': ["Credentials incorrect or none given."]}, - 401) + if not request.keystone_user.get("authenticated", False): + return Response({"errors": ["Credentials incorrect or none given."]}, 401) - roles = set(request.keystone_user.get('roles', [])) + roles = set(request.keystone_user.get("roles", [])) if roles & req_roles: return func(*args, **kwargs) - return Response({'errors': ["Must have one of the following roles: %s" % - list(req_roles)]}, 403) + return Response( + {"errors": ["Must have one of the following roles: %s" % list(req_roles)]}, 403 + ) @decorator @@ -47,7 +47,8 @@ def mod_or_admin(func, *args, **kwargs): Admin is allowed everything, so is also included. """ return require_roles( - {'project_admin', 'project_mod', 'admin'}, func, *args, **kwargs) + {"project_admin", "project_mod", "admin"}, func, *args, **kwargs + ) @decorator @@ -55,8 +56,7 @@ def project_admin(func, *args, **kwargs): """ endpoints setup with this decorator require the admin/project admin role. """ - return require_roles( - {'project_admin', 'admin'}, func, *args, **kwargs) + return require_roles({"project_admin", "admin"}, func, *args, **kwargs) @decorator @@ -64,8 +64,7 @@ def admin(func, *args, **kwargs): """ endpoints setup with this decorator require the admin role. """ - return require_roles( - {'admin'}, func, *args, **kwargs) + return require_roles({"admin"}, func, *args, **kwargs) @decorator @@ -74,9 +73,8 @@ def authenticated(func, *args, **kwargs): endpoints setup with this decorator require the user to be signed in """ request = args[1] - if not request.keystone_user.get('authenticated', False): - return Response({'errors': ["Credentials incorrect or none given."]}, - 401) + if not request.keystone_user.get("authenticated", False): + return Response({"errors": ["Credentials incorrect or none given."]}, 401) return func(*args, **kwargs) @@ -87,7 +85,7 @@ def minimal_duration(func, min_time=1, *args, **kwargs): Make a function (or API call) take at least some time. """ # doesn't apply during tests - if 'test' in sys.argv: + if "test" in sys.argv: return func(*args, **kwargs) start = datetime.utcnow() diff --git a/adjutant/api/v1/openstack.py b/adjutant/api/v1/openstack.py index f30c760..ecb88f4 100644 --- a/adjutant/api/v1/openstack.py +++ b/adjutant/api/v1/openstack.py @@ -30,15 +30,15 @@ from adjutant.config import CONF class UserList(tasks.InviteUser): - url = r'^openstack/users/?$' + url = r"^openstack/users/?$" config_group = groups.DynamicNameConfigGroup( children=[ fields.ListConfig( - 'blacklisted_roles', + "blacklisted_roles", help_text="Users with any of these roles will be hidden from the user list.", default=[], - sample_default=['admin'] + sample_default=["admin"], ), ] ) @@ -51,11 +51,12 @@ class UserList(tasks.InviteUser): user_list = [] id_manager = user_store.IdentityManager() - project_id = request.keystone_user['project_id'] + project_id = request.keystone_user["project_id"] project = id_manager.get_project(project_id) can_manage_roles = id_manager.get_manageable_roles( - request.keystone_user['roles']) + request.keystone_user["roles"] + ) active_emails = set() for user in id_manager.list_users(project): @@ -77,20 +78,22 @@ class UserList(tasks.InviteUser): if skip: continue - email = getattr(user, 'email', '') + email = getattr(user, "email", "") enabled = user.enabled - user_status = 'Active' if enabled else 'Account Disabled' + user_status = "Active" if enabled else "Account Disabled" active_emails.add(email) - user_list.append({ - 'id': user.id, - 'name': user.name, - 'email': email, - 'roles': roles, - 'inherited_roles': inherited_roles, - 'cohort': 'Member', - 'status': user_status, - 'manageable': set(can_manage_roles).issuperset(roles), - }) + user_list.append( + { + "id": user.id, + "name": user.name, + "email": email, + "roles": roles, + "inherited_roles": inherited_roles, + "cohort": "Member", + "status": user_status, + "manageable": set(can_manage_roles).issuperset(roles), + } + ) for user in id_manager.list_inherited_users(project): skip = False @@ -103,25 +106,29 @@ class UserList(tasks.InviteUser): if skip: continue - email = getattr(user, 'email', '') + email = getattr(user, "email", "") enabled = user.enabled - user_status = 'Active' if enabled else 'Account Disabled' - user_list.append({'id': user.id, - 'name': user.name, - 'email': email, - 'roles': roles, - 'inherited_roles': [], - 'cohort': 'Inherited', - 'status': user_status, - 'manageable': False, - }) + user_status = "Active" if enabled else "Account Disabled" + user_list.append( + { + "id": user.id, + "name": user.name, + "email": email, + "roles": roles, + "inherited_roles": [], + "cohort": "Inherited", + "status": user_status, + "manageable": False, + } + ) # Get my active tasks for this project: project_tasks = models.Task.objects.filter( project_id=project_id, task_type="invite_user_to_project", completed=0, - cancelled=0) + cancelled=0, + ) registrations = [] for task in project_tasks: @@ -143,7 +150,8 @@ class UserList(tasks.InviteUser): task_data.update(action.action_data) registrations.append( - {'uuid': task.uuid, 'task_data': task_data, 'status': status}) + {"uuid": task.uuid, "task_data": task_data, "status": status} + ) for task in registrations: # NOTE(adriant): commenting out for now as it causes more confusion @@ -151,34 +159,33 @@ class UserList(tasks.InviteUser): # measures are in place. # if task['task_data']['email'] not in active_emails: user = { - 'id': task['uuid'], - 'name': task['task_data']['email'], - 'email': task['task_data']['email'], - 'roles': task['task_data']['roles'], - 'inherited_roles': - task['task_data']['inherited_roles'], - 'cohort': 'Invited', - 'status': task['status'] + "id": task["uuid"], + "name": task["task_data"]["email"], + "email": task["task_data"]["email"], + "roles": task["task_data"]["roles"], + "inherited_roles": task["task_data"]["inherited_roles"], + "cohort": "Invited", + "status": task["status"], } if not CONF.identity.username_is_email: - user['name'] = task['task_data']['username'] + user["name"] = task["task_data"]["username"] user_list.append(user) - return Response({'users': user_list}) + return Response({"users": user_list}) class UserDetail(BaseDelegateAPI): - url = r'^openstack/users/(?P\w+)/?$' + url = r"^openstack/users/(?P\w+)/?$" config_group = groups.DynamicNameConfigGroup( children=[ fields.ListConfig( - 'blacklisted_roles', + "blacklisted_roles", help_text="User with these roles will return not found.", default=[], - sample_default=['admin'] + sample_default=["admin"], ), ] ) @@ -193,30 +200,34 @@ class UserDetail(BaseDelegateAPI): id_manager = user_store.IdentityManager() user = id_manager.get_user(user_id) - no_user = {'errors': ['No user with this id.']} + no_user = {"errors": ["No user with this id."]} if not user: return Response(no_user, status=404) class_conf = self.config blacklisted_roles = class_conf.blacklisted_roles - project_id = request.keystone_user['project_id'] + project_id = request.keystone_user["project_id"] project = id_manager.get_project(project_id) roles = [role.name for role in id_manager.get_roles(user, project)] roles_blacklisted = set(blacklisted_roles) & set(roles) inherited_roles = [ - role.name for role in id_manager.get_roles(user, project, True)] - inherited_roles_blacklisted = ( - set(blacklisted_roles) & set(inherited_roles)) + role.name for role in id_manager.get_roles(user, project, True) + ] + inherited_roles_blacklisted = set(blacklisted_roles) & set(inherited_roles) if not roles or roles_blacklisted or inherited_roles_blacklisted: return Response(no_user, status=404) - return Response({'id': user.id, - "username": user.name, - "email": getattr(user, 'email', ''), - 'roles': roles, - 'inherited_roles': inherited_roles}) + return Response( + { + "id": user.id, + "username": user.name, + "email": getattr(user, "email", ""), + "roles": roles, + "inherited_roles": inherited_roles, + } + ) @utils.mod_or_admin def delete(self, request, user_id): @@ -226,37 +237,42 @@ class UserDetail(BaseDelegateAPI): """ id_manager = user_store.IdentityManager() user = id_manager.get_user(user_id) - project_id = request.keystone_user['project_id'] + project_id = request.keystone_user["project_id"] # NOTE(dale): For now, we only support cancelling pending invites. if user: return Response( - {'errors': [ - 'Revoking keystone users not implemented. ' - 'Try removing all roles instead.']}, - status=501) + { + "errors": [ + "Revoking keystone users not implemented. " + "Try removing all roles instead." + ] + }, + status=501, + ) project_tasks = models.Task.objects.filter( project_id=project_id, task_type="invite_user_to_project", completed=0, - cancelled=0) + cancelled=0, + ) for task in project_tasks: if task.uuid == user_id: self.task_manager.cancel(task) - return Response('Cancelled pending invite task!', status=200) - return Response('Not found.', status=404) + return Response("Cancelled pending invite task!", status=200) + return Response("Not found.", status=404) class UserRoles(BaseDelegateAPI): - url = r'^openstack/users/(?P\w+)/roles/?$' + url = r"^openstack/users/(?P\w+)/roles/?$" config_group = groups.DynamicNameConfigGroup( children=[ fields.ListConfig( - 'blacklisted_roles', + "blacklisted_roles", help_text="User with these roles will return not found.", default=[], - sample_default=['admin'] + sample_default=["admin"], ), ] ) @@ -269,11 +285,11 @@ class UserRoles(BaseDelegateAPI): id_manager = user_store.IdentityManager() user = id_manager.get_user(user_id) - no_user = {'errors': ['No user with this id.']} + no_user = {"errors": ["No user with this id."]} if not user: return Response(no_user, status=404) - project_id = request.keystone_user['project_id'] + project_id = request.keystone_user["project_id"] project = id_manager.get_project(project_id) class_conf = self.config @@ -282,19 +298,18 @@ class UserRoles(BaseDelegateAPI): roles = [role.name for role in id_manager.get_roles(user, project)] roles_blacklisted = set(blacklisted_roles) & set(roles) inherited_roles = [ - role.name for role in id_manager.get_roles(user, project, True)] - inherited_roles_blacklisted = ( - set(blacklisted_roles) & set(inherited_roles)) + role.name for role in id_manager.get_roles(user, project, True) + ] + inherited_roles_blacklisted = set(blacklisted_roles) & set(inherited_roles) if not roles or roles_blacklisted or inherited_roles_blacklisted: return Response(no_user, status=404) - return Response({'roles': roles, - 'inherited_roles': inherited_roles}) + return Response({"roles": roles, "inherited_roles": inherited_roles}) @utils.mod_or_admin def put(self, args, **kwargs): """ Add user roles to the current project. """ - kwargs['remove_role'] = False + kwargs["remove_role"] = False return self._edit_user(args, **kwargs) @utils.mod_or_admin @@ -303,34 +318,35 @@ class UserRoles(BaseDelegateAPI): This only supports Active users """ - kwargs['remove_role'] = True + kwargs["remove_role"] = True return self._edit_user(args, **kwargs) def _edit_user(self, request, user_id, remove_role=False, format=None): """ Helper function to add or remove roles from a user """ - request.data['remove'] = remove_role - if 'project_id' not in request.data: - request.data['project_id'] = request.keystone_user['project_id'] - request.data['user_id'] = user_id + request.data["remove"] = remove_role + if "project_id" not in request.data: + request.data["project_id"] = request.keystone_user["project_id"] + request.data["user_id"] = user_id - self.logger.info("(%s) - New EditUser %s request." % ( - timezone.now(), request.method)) + self.logger.info( + "(%s) - New EditUser %s request." % (timezone.now(), request.method) + ) self.task_manager.create_from_request(self.task_type, request) - return Response({'notes': ['task created']}, status=202) + return Response({"notes": ["task created"]}, status=202) class RoleList(BaseDelegateAPI): - url = r'^openstack/roles/?$' + url = r"^openstack/roles/?$" @utils.mod_or_admin def get(self, request): """Returns a list of roles that may be managed for this project""" # get roles for this user on the project - user_roles = request.keystone_user['roles'] + user_roles = request.keystone_user["roles"] id_manager = user_store.IdentityManager() manageable_role_names = id_manager.get_manageable_roles(user_roles) @@ -342,7 +358,7 @@ class RoleList(BaseDelegateAPI): if role: manageable_roles.append(role.to_dict()) - return Response({'roles': manageable_roles}) + return Response({"roles": manageable_roles}) class UserResetPassword(tasks.ResetPassword): @@ -351,7 +367,7 @@ class UserResetPassword(tasks.ResetPassword): --- """ - url = r'^openstack/users/password-reset/?$' + url = r"^openstack/users/password-reset/?$" pass @@ -362,7 +378,7 @@ class UserUpdateEmail(tasks.UpdateEmail): --- """ - url = r'^openstack/users/email-update/?$' + url = r"^openstack/users/email-update/?$" pass @@ -372,7 +388,7 @@ class SignUp(tasks.CreateProjectAndUser): The openstack endpoint for signups. """ - url = r'^openstack/sign-up/?$' + url = r"^openstack/sign-up/?$" pass @@ -383,7 +399,7 @@ class UpdateProjectQuotas(BaseDelegateAPI): one or more regions """ - url = r'^openstack/quotas/?$' + url = r"^openstack/quotas/?$" task_type = "update_quota" @@ -395,7 +411,7 @@ class UpdateProjectQuotas(BaseDelegateAPI): task_type__exact=self.task_type, project_id__exact=self.project_id, cancelled=0, - ).order_by('-created_on')[:self._number_of_returned_tasks] + ).order_by("-created_on")[: self._number_of_returned_tasks] response_tasks = [] @@ -409,13 +425,12 @@ class UpdateProjectQuotas(BaseDelegateAPI): task_data.update(action.action_data) new_dict = { "id": task.uuid, - "regions": task_data['regions'], - "size": task_data['size'], - "request_user": - task.keystone_user['username'], + "regions": task_data["regions"], + "size": task_data["size"], + "request_user": task.keystone_user["username"], "task_created": task.created_on, "valid": all([a.valid for a in task.actions]), - "status": status + "status": status, } response_tasks.append(new_dict) @@ -439,9 +454,9 @@ class UpdateProjectQuotas(BaseDelegateAPI): quota_sizes = CONF.quota.sizes size_order = CONF.quota.sizes_ascending - self.project_id = request.keystone_user['project_id'] - regions = request.query_params.get('regions', None) - include_usage = request.query_params.get('include_usage', True) + self.project_id = request.keystone_user["project_id"] + regions = request.query_params.get("regions", None) + include_usage = request.query_params.get("include_usage", True) if regions: regions = regions.split(",") @@ -456,35 +471,38 @@ class UpdateProjectQuotas(BaseDelegateAPI): quota_manager = QuotaManager(self.project_id) for region in regions: if self.check_region_exists(region): - region_quotas.append(quota_manager.get_region_quota_data( - region, include_usage)) + region_quotas.append( + quota_manager.get_region_quota_data(region, include_usage) + ) else: - return Response( - {"ERROR": ['Region: %s is not valid' % region]}, 400) + return Response({"ERROR": ["Region: %s is not valid" % region]}, 400) response_tasks = self.get_active_quota_tasks() - return Response({'regions': region_quotas, - "quota_sizes": quota_sizes, - "quota_size_order": size_order, - "active_quota_tasks": response_tasks}) + return Response( + { + "regions": region_quotas, + "quota_sizes": quota_sizes, + "quota_size_order": size_order, + "active_quota_tasks": response_tasks, + } + ) @utils.mod_or_admin def post(self, request): - request.data['project_id'] = request.keystone_user['project_id'] - self.project_id = request.keystone_user['project_id'] + request.data["project_id"] = request.keystone_user["project_id"] + self.project_id = request.keystone_user["project_id"] - regions = request.data.get('regions', None) + regions = request.data.get("regions", None) if not regions: id_manager = user_store.IdentityManager() regions = [region.id for region in id_manager.list_regions()] - request.data['regions'] = regions + request.data["regions"] = regions - self.logger.info("(%s) - New UpdateProjectQuotas request." - % timezone.now()) + self.logger.info("(%s) - New UpdateProjectQuotas request." % timezone.now()) self.task_manager.create_from_request(self.task_type, request) - return Response({'notes': ['task created']}, status=202) + return Response({"notes": ["task created"]}, status=202) diff --git a/adjutant/api/v1/tasks.py b/adjutant/api/v1/tasks.py index b069673..3dde816 100644 --- a/adjutant/api/v1/tasks.py +++ b/adjutant/api/v1/tasks.py @@ -27,14 +27,15 @@ from adjutant.api.v1.base import BaseDelegateAPI # NOTE(adriant): We should deprecate these Views properly and switch tests # to work against the openstack ones. + class CreateProjectAndUser(BaseDelegateAPI): - url = r'^actions/CreateProjectAndUser/?$' + url = r"^actions/CreateProjectAndUser/?$" config_group = groups.DynamicNameConfigGroup( children=[ fields.StrConfig( - 'default_region', + "default_region", help_text="Default region in which any potential resources may be created.", required=True, default="RegionOne", @@ -48,9 +49,9 @@ class CreateProjectAndUser(BaseDelegateAPI): fields.StrConfig( "default_parent_id", help_text="Parent id under which this project will be created. " - "Default is None, and will create under default domain.", + "Default is None, and will create under default domain.", default=None, - ) + ), ] ) @@ -64,28 +65,27 @@ class CreateProjectAndUser(BaseDelegateAPI): incoming data and create a task to be approved later. """ - self.logger.info( - "(%s) - Starting new project task." % timezone.now()) + self.logger.info("(%s) - Starting new project task." % timezone.now()) class_conf = self.config # we need to set the region the resources will be created in: - request.data['region'] = class_conf.default_region + request.data["region"] = class_conf.default_region # domain - request.data['domain_id'] = class_conf.default_domain_id + request.data["domain_id"] = class_conf.default_domain_id # parent_id for new project, if null defaults to domain: - request.data['parent_id'] = class_conf.default_parent_id + request.data["parent_id"] = class_conf.default_parent_id self.task_manager.create_from_request(self.task_type, request) - return Response({'notes': ['task created']}, status=202) + return Response({"notes": ["task created"]}, status=202) class InviteUser(BaseDelegateAPI): - url = r'^actions/InviteUser/?$' + url = r"^actions/InviteUser/?$" task_type = "invite_user_to_project" @@ -105,24 +105,21 @@ class InviteUser(BaseDelegateAPI): self.logger.info("(%s) - New AttachUser request." % timezone.now()) # Default project_id to the keystone user's project - if ('project_id' not in request.data - or request.data['project_id'] is None): - request.data['project_id'] = request.keystone_user['project_id'] + if "project_id" not in request.data or request.data["project_id"] is None: + request.data["project_id"] = request.keystone_user["project_id"] # Default domain_id to the keystone user's project - if ('domain_id' not in request.data - or request.data['domain_id'] is None): - request.data['domain_id'] = \ - request.keystone_user['project_domain_id'] + if "domain_id" not in request.data or request.data["domain_id"] is None: + request.data["domain_id"] = request.keystone_user["project_domain_id"] self.task_manager.create_from_request(self.task_type, request) - return Response({'notes': ['task created']}, status=202) + return Response({"notes": ["task created"]}, status=202) class ResetPassword(BaseDelegateAPI): - url = r'^actions/ResetPassword/?$' + url = r"^actions/ResetPassword/?$" task_type = "reset_user_password" @@ -156,17 +153,19 @@ class ResetPassword(BaseDelegateAPI): self.task_manager.create_from_request(self.task_type, request) except exceptions.BaseTaskException as e: self.logger.info( - "(%s) - ResetPassword raised error: %s" % (timezone.now(), e)) + "(%s) - ResetPassword raised error: %s" % (timezone.now(), e) + ) - response_dict = {'notes': [ - "If user with email exists, reset token will be issued."]} + response_dict = { + "notes": ["If user with email exists, reset token will be issued."] + } return Response(response_dict, status=202) class EditUser(BaseDelegateAPI): - url = r'^actions/EditUser/?$' + url = r"^actions/EditUser/?$" task_type = "edit_user_roles" @@ -183,12 +182,12 @@ class EditUser(BaseDelegateAPI): self.task_manager.create_from_request(self.task_type, request) - return Response({'notes': ['task created']}, status=202) + return Response({"notes": ["task created"]}, status=202) class UpdateEmail(BaseDelegateAPI): - url = r'^actions/UpdateEmail/?$' + url = r"^actions/UpdateEmail/?$" task_type = "update_user_email" @@ -199,8 +198,8 @@ class UpdateEmail(BaseDelegateAPI): This will submit and approve an update email action. """ - request.data['user_id'] = request.keystone_user['user_id'] + request.data["user_id"] = request.keystone_user["user_id"] self.task_manager.create_from_request(self.task_type, request) - return Response({'notes': ['task created']}, status=202) + return Response({"notes": ["task created"]}, status=202) diff --git a/adjutant/api/v1/tests/test_api_admin.py b/adjutant/api/v1/tests/test_api_admin.py index 7d089f7..0c197b8 100644 --- a/adjutant/api/v1/tests/test_api_admin.py +++ b/adjutant/api/v1/tests/test_api_admin.py @@ -27,13 +27,11 @@ from confspirator.tests import utils as conf_utils from adjutant.api.models import Task, Token, Notification from adjutant.common.tests import fake_clients -from adjutant.common.tests.fake_clients import ( - FakeManager, setup_identity_cache) +from adjutant.common.tests.fake_clients import FakeManager, setup_identity_cache from adjutant.config import CONF -@mock.patch('adjutant.common.user_store.IdentityManager', - FakeManager) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) class AdminAPITests(APITestCase): """ Tests to ensure the admin api endpoints work as expected within @@ -45,22 +43,22 @@ class AdminAPITests(APITestCase): Should be a 404. """ url = "/v1/tokens/e8b3f57f5da64bf3a6bf4f9bbd3a40b5" - response = self.client.get(url, format='json') + response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual( - response.json(), - {'errors': ['This token does not exist or has expired.']}) + response.json(), {"errors": ["This token does not exist or has expired."]} + ) def test_no_token_post(self): """ Should be a 404. """ url = "/v1/tokens/e8b3f57f5da64bf3a6bf4f9bbd3a40b5" - response = self.client.post(url, format='json') + response = self.client.post(url, format="json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual( - response.json(), - {'errors': ['This token does not exist or has expired.']}) + response.json(), {"errors": ["This token does not exist or has expired."]} + ) def test_task_get(self): """ @@ -69,21 +67,21 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.get(url, format='json', headers=headers) + response = self.client.get(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) def test_no_task_get(self): @@ -91,40 +89,43 @@ class AdminAPITests(APITestCase): Should be a 404. """ headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tasks/e8b3f57f5da64bf3a6bf4f9bbd3a40b5" - response = self.client.get(url, format='json', headers=headers) + response = self.client.get(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual( - response.json(), {'errors': ['No task with this id.']}) + self.assertEqual(response.json(), {"errors": ["No task with this id."]}) def test_no_task_post(self): """ Should be a 404. """ headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tasks/e8b3f57f5da64bf3a6bf4f9bbd3a40b5" response = self.client.post( - url, {'approved': True}, format='json', headers=headers) + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual( response.json(), - {'errors': [ - "Task not found with uuid of: " - "'e8b3f57f5da64bf3a6bf4f9bbd3a40b5'"]}) + { + "errors": [ + "Task not found with uuid of: " "'e8b3f57f5da64bf3a6bf4f9bbd3a40b5'" + ] + }, + ) def test_token_expired_post(self): """ @@ -132,28 +133,30 @@ class AdminAPITests(APITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) new_token = Token.objects.all()[0] new_token.expires = timezone.now() - timedelta(hours=24) new_token.save() url = "/v1/tokens/" + new_token.token - data = {'password': 'new_test_password'} - response = self.client.post(url, data, format='json') + data = {"password": "new_test_password"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual( - response.json(), - {'errors': ['This token does not exist or has expired.']}) + response.json(), {"errors": ["This token does not exist or has expired."]} + ) self.assertEqual(0, Token.objects.count()) def test_token_expired_get(self): @@ -162,17 +165,19 @@ class AdminAPITests(APITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) new_token = Token.objects.all()[0] new_token.expires = timezone.now() - timedelta(hours=24) @@ -181,8 +186,8 @@ class AdminAPITests(APITestCase): response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual( - response.json(), - {'errors': ['This token does not exist or has expired.']}) + response.json(), {"errors": ["This token does not exist or has expired."]} + ) self.assertEqual(0, Token.objects.count()) def test_token_get(self): @@ -191,17 +196,19 @@ class AdminAPITests(APITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token @@ -209,9 +216,12 @@ class AdminAPITests(APITestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( response.json(), - {u'actions': [u'ResetUserPasswordAction'], - u'required_fields': [u'password'], - u'task_type': 'reset_user_password'}) + { + "actions": ["ResetUserPasswordAction"], + "required_fields": ["password"], + "task_type": "reset_user_password", + }, + ) self.assertEqual(1, Token.objects.count()) def test_token_list_get(self): @@ -219,40 +229,41 @@ class AdminAPITests(APITestCase): Create two password resets, then confirm we can list tokens. """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) user2 = fake_clients.FakeUser( - name="test2@example.com", password="123", - email="test2@example.com") + name="test2@example.com", password="123", email="test2@example.com" + ) setup_identity_cache(users=[user, user2]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - data = {'email': "test2@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test2@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tokens/" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.json()['tokens']), 2) + self.assertEqual(len(response.json()["tokens"]), 2) task_ids = [t.uuid for t in Task.objects.all()] - token_task_ids = [t['task'] for t in response.json()['tokens']] + token_task_ids = [t["task"] for t in response.json()["tokens"]] self.assertEqual(sorted(task_ids), sorted(token_task_ids)) @@ -263,28 +274,29 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] new_task.completed = True new_task.save() url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.json(), - {'errors': ['This task has already been completed.']}) + response.json(), {"errors": ["This task has already been completed."]} + ) def test_status_page(self): """ @@ -295,42 +307,45 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/status/" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()['last_created_task'][ - 'actions'][0]['data']['email'], 'test@example.com') - self.assertEqual(response.json()['last_completed_task'], None) + self.assertEqual( + response.json()["last_created_task"]["actions"][0]["data"]["email"], + "test@example.com", + ) + self.assertEqual(response.json()["last_completed_task"], None) - self.assertEqual(response.json()['error_notifications'], []) + self.assertEqual(response.json()["error_notifications"], []) # Create a second task and ensure it is the new last_created_task url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project_2", - 'email': "test_2@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project_2", "email": "test_2@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) url = "/v1/status/" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()['last_created_task'][ - 'actions'][0]['data']['email'], 'test_2@example.com') - self.assertEqual(response.json()['last_completed_task'], None) + self.assertEqual( + response.json()["last_created_task"]["actions"][0]["data"]["email"], + "test_2@example.com", + ) + self.assertEqual(response.json()["last_completed_task"], None) - self.assertEqual(response.json()['error_notifications'], []) + self.assertEqual(response.json()["error_notifications"], []) new_task = Task.objects.all()[0] new_task.completed = True @@ -339,12 +354,16 @@ class AdminAPITests(APITestCase): url = "/v1/status/" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()['last_completed_task'][ - 'actions'][0]['data']['email'], 'test@example.com') - self.assertEqual(response.json()['last_created_task'][ - 'actions'][0]['data']['email'], 'test_2@example.com') + self.assertEqual( + response.json()["last_completed_task"]["actions"][0]["data"]["email"], + "test@example.com", + ) + self.assertEqual( + response.json()["last_created_task"]["actions"][0]["data"]["email"], + "test_2@example.com", + ) - self.assertEqual(response.json()['error_notifications'], []) + self.assertEqual(response.json()["error_notifications"], []) def test_task_update(self): """ @@ -358,43 +377,40 @@ class AdminAPITests(APITestCase): setup_identity_cache(projects=[project]) url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) data = { - 'project_name': "test_project2", - 'email': "test@example.com", - 'region': 'RegionOne', + "project_name": "test_project2", + "email": "test@example.com", + "region": "RegionOne", } - response = self.client.put(url, data, format='json', - headers=headers) + response = self.client.put(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.json(), - {'notes': ['Task successfully updated.']}) + self.assertEqual(response.json(), {"notes": ["Task successfully updated."]}) - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual( - response.json(), - {'notes': ['created token']}) + self.assertEqual(response.json(), {"notes": ["created token"]}) def test_notification_get(self): """ @@ -403,33 +419,31 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) new_task = Task.objects.all()[0] headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } note = Notification.objects.first().uuid url = "/v1/notifications/%s" % note response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json()["task"], new_task.uuid) self.assertEqual( - response.json()['task'], - new_task.uuid) - self.assertEqual( - response.json()['notes'], - {'notes': ["'create_project_and_user' task needs approval."]}) - self.assertEqual( - response.json()['error'], False) + response.json()["notes"], + {"notes": ["'create_project_and_user' task needs approval."]}, + ) + self.assertEqual(response.json()["error"], False) def test_notification_doesnt_exist(self): """ @@ -438,28 +452,28 @@ class AdminAPITests(APITestCase): setup_identity_cache() headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } note = "notarealnotifiactionuuid" url = "/v1/notifications/%s/" % note - response = self.client.get(url, headers=headers, format='json') + response = self.client.get(url, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response.json(), - {"errors": ["No notification with this id."]}) + self.assertEqual(response.json(), {"errors": ["No notification with this id."]}) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.task_defaults.notifications.standard_handlers": [ - {'operation': 'override', 'value': []}, + {"operation": "override", "value": []}, ], - }) + }, + ) def test_notification_acknowledge(self): """ Test that you can acknowledge a notification. @@ -467,45 +481,35 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) new_task = Task.objects.all()[0] headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/notifications" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.json()["notifications"][0]['task'], - new_task.uuid) + self.assertEqual(response.json()["notifications"][0]["task"], new_task.uuid) - url = ("/v1/notifications/%s/" % - response.json()["notifications"][0]['uuid']) - data = {'acknowledged': True} - response = self.client.post(url, data, format='json', headers=headers) - self.assertEqual(response.json(), - {'notes': ['Notification acknowledged.']}) + url = "/v1/notifications/%s/" % response.json()["notifications"][0]["uuid"] + data = {"acknowledged": True} + response = self.client.post(url, data, format="json", headers=headers) + self.assertEqual(response.json(), {"notes": ["Notification acknowledged."]}) url = "/v1/notifications" - params = { - "filters": json.dumps({ - "acknowledged": {"exact": False} - }) - } - response = self.client.get( - url, params, format='json', headers=headers - ) - self.assertEqual(response.json(), {'notifications': []}) + params = {"filters": json.dumps({"acknowledged": {"exact": False}})} + response = self.client.get(url, params, format="json", headers=headers) + self.assertEqual(response.json(), {"notifications": []}) def test_notification_acknowledge_doesnt_exist(self): """ @@ -514,28 +518,27 @@ class AdminAPITests(APITestCase): setup_identity_cache() headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/notifications/dasdaaaiooiiobksd/" response = self.client.post(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response.json(), - {'errors': - ['No notification with this id.']}) + self.assertEqual(response.json(), {"errors": ["No notification with this id."]}) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.task_defaults.notifications.standard_handlers": [ - {'operation': 'override', 'value': []}, + {"operation": "override", "value": []}, ], - }) + }, + ) def test_notification_re_acknowledge(self): """ Test that you cant reacknowledge a notification. @@ -543,39 +546,40 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } note_id = Notification.objects.first().uuid url = "/v1/notifications/%s/" % note_id - data = {'acknowledged': True} - response = self.client.post(url, data, format='json', headers=headers) + data = {"acknowledged": True} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json(), - {'notes': ['Notification acknowledged.']}) + self.assertEqual(response.json(), {"notes": ["Notification acknowledged."]}) - 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_200_OK) - self.assertEqual(response.json(), - {'notes': ['Notification already acknowledged.']}) + self.assertEqual( + response.json(), {"notes": ["Notification already acknowledged."]} + ) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.task_defaults.notifications.standard_handlers": [ - {'operation': 'override', 'value': []}, + {"operation": "override", "value": []}, ], - }) + }, + ) def test_notification_acknowledge_no_data(self): """ Test that you have to include 'acknowledged': True to the request. @@ -583,26 +587,25 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } note_id = Notification.objects.first().uuid url = "/v1/notifications/%s/" % note_id data = {} - 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.json(), - {u'acknowledged': [u'this field is required.']}) + self.assertEqual(response.json(), {"acknowledged": ["this field is required."]}) def test_notification_acknowledge_list(self): """ @@ -611,20 +614,20 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - data = {'project_name': "test_project2", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project2", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/notifications" @@ -633,21 +636,14 @@ class AdminAPITests(APITestCase): url = "/v1/notifications" notifications = response.json()["notifications"] - data = {'notifications': [note['uuid'] for note in notifications]} - response = self.client.post(url, data, format='json', headers=headers) - self.assertEqual(response.json(), - {'notes': ['Notifications acknowledged.']}) + data = {"notifications": [note["uuid"] for note in notifications]} + response = self.client.post(url, data, format="json", headers=headers) + self.assertEqual(response.json(), {"notes": ["Notifications acknowledged."]}) url = "/v1/notifications" - params = { - "filters": json.dumps({ - "acknowledged": {"exact": False} - }) - } - response = self.client.get( - url, params, format='json', headers=headers - ) - self.assertEqual(response.json(), {'notifications': []}) + params = {"filters": json.dumps({"acknowledged": {"exact": False}})} + response = self.client.get(url, params, format="json", headers=headers) + self.assertEqual(response.json(), {"notifications": []}) def test_notification_acknowledge_list_empty_list(self): """ @@ -656,54 +652,58 @@ class AdminAPITests(APITestCase): setup_identity_cache() headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/notifications" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - data = {'notifications': []} - response = self.client.post(url, data, format='json', headers=headers) + data = {"notifications": []} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json(), - {u'notifications': - [u'this field is required and needs to be a list.']}) + self.assertEqual( + response.json(), + {"notifications": ["this field is required and needs to be a list."]}, + ) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.create_project_and_user.notifications": [ - {'operation': 'override', 'value': { - "standard_handlers": ["EmailNotification"], - "error_handlers": ["EmailNotification"], - "standard_handler_config": { - "EmailNotification": { - 'emails': ['example@example.com'], - 'reply': 'no-reply@example.com', - } + { + "operation": "override", + "value": { + "standard_handlers": ["EmailNotification"], + "error_handlers": ["EmailNotification"], + "standard_handler_config": { + "EmailNotification": { + "emails": ["example@example.com"], + "reply": "no-reply@example.com", + } + }, + "error_handler_config": { + "EmailNotification": { + "emails": ["example@example.com"], + "reply": "no-reply@example.com", + } + }, }, - "error_handler_config": { - "EmailNotification": { - 'emails': ['example@example.com'], - 'reply': 'no-reply@example.com', - } - }, - }}, + }, ], "adjutant.workflow.tasks.create_project_and_user.emails": [ - {'operation': 'override', 'value': { - 'initial': None, - 'token': None, - 'completed': None - }}, + { + "operation": "override", + "value": {"initial": None, "token": None, "completed": None}, + }, ], - }) + }, + ) def test_notification_email(self): """ Tests the email notification handler @@ -711,34 +711,31 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) new_task = Task.objects.all()[0] headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/notifications" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data["notifications"][0]['task'], - new_task.uuid) + self.assertEqual(response.data["notifications"][0]["task"], new_task.uuid) self.assertEqual(len(mail.outbox), 1) - self.assertEqual( - mail.outbox[0].subject, 'create_project_and_user notification') + self.assertEqual(mail.outbox[0].subject, "create_project_and_user notification") self.assertTrue( - "'create_project_and_user' task needs approval." - in mail.outbox[0].body) + "'create_project_and_user' task needs approval." in mail.outbox[0].body + ) def test_token_expired_delete(self): """ @@ -746,28 +743,31 @@ class AdminAPITests(APITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) user2 = fake_clients.FakeUser( - name="test2@example.com", password="123", - email="test2@example.com") + name="test2@example.com", password="123", email="test2@example.com" + ) setup_identity_cache(users=[user, user2]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) - data = {'email': "test2@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test2@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) tokens = Token.objects.all() @@ -778,18 +778,17 @@ class AdminAPITests(APITestCase): new_token.save() headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tokens/" - response = self.client.delete(url, format='json', headers=headers) + response = self.client.delete(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json(), - {'notes': ['Deleted all expired tokens.']}) + self.assertEqual(response.json(), {"notes": ["Deleted all expired tokens."]}) self.assertEqual(Token.objects.count(), 1) def test_token_reissue(self): @@ -798,17 +797,19 @@ class AdminAPITests(APITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) task = Task.objects.all()[0] new_token = Token.objects.all()[0] @@ -816,20 +817,18 @@ class AdminAPITests(APITestCase): uuid = new_token.token headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tokens/" data = {"task": task.uuid} - 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_200_OK) - self.assertEqual(response.json(), - {'notes': ['Token reissued.']}) + self.assertEqual(response.json(), {"notes": ["Token reissued."]}) self.assertEqual(Token.objects.count(), 1) new_token = Token.objects.all()[0] self.assertNotEqual(new_token.token, uuid) @@ -845,18 +844,21 @@ class AdminAPITests(APITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) task = Task.objects.all()[0] new_token = Token.objects.all()[0] @@ -865,22 +867,18 @@ class AdminAPITests(APITestCase): url = "/v1/tokens/" data = {"task": task.uuid} - 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_200_OK) - self.assertEqual(response.json(), - {'notes': ['Token reissued.']}) + self.assertEqual(response.json(), {"notes": ["Token reissued."]}) self.assertEqual(Token.objects.count(), 1) new_token = Token.objects.all()[0] self.assertNotEqual(new_token.token, uuid) # Now confirm it is limited by project id properly. - headers['project_id'] = "test_project_id2" - response = self.client.post(url, data, format='json', - headers=headers) + headers["project_id"] = "test_project_id2" + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response.json(), - {'errors': ['No task with this id.']}) + self.assertEqual(response.json(), {"errors": ["No task with this id."]}) def test_token_reissue_task_cancelled(self): """ @@ -888,17 +886,19 @@ class AdminAPITests(APITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) task = Task.objects.all()[0] task.cancelled = True @@ -906,20 +906,18 @@ class AdminAPITests(APITestCase): self.assertEqual(Token.objects.count(), 1) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tokens/" data = {"task": task.uuid} - 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.json(), - {'errors': ['This task has been cancelled.']}) + self.assertEqual(response.json(), {"errors": ["This task has been cancelled."]}) def test_token_reissue_task_completed(self): """ @@ -927,17 +925,19 @@ class AdminAPITests(APITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) task = Task.objects.all()[0] task.completed = True @@ -945,20 +945,20 @@ class AdminAPITests(APITestCase): self.assertEqual(Token.objects.count(), 1) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tokens/" data = {"task": task.uuid} - 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.json(), - {'errors': ['This task has already been completed.']}) + self.assertEqual( + response.json(), {"errors": ["This task has already been completed."]} + ) def test_token_reissue_task_not_approve(self): """ @@ -968,29 +968,28 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'email': "test@example.com", "project_name": "test_project"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com", "project_name": "test_project"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual( - response.json()['notes'], [u'task created']) + self.assertEqual(response.json()["notes"], ["task created"]) task = Task.objects.all()[0] headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tokens/" data = {"task": task.uuid} - 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.json(), - {'errors': ['This task has not been approved.']}) + self.assertEqual( + response.json(), {"errors": ["This task has not been approved."]} + ) def test_cancel_task(self): """ @@ -1000,30 +999,29 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.delete(url, format='json', - headers=headers) + response = self.client.delete(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - response = self.client.put(url, format='json', - headers=headers) + response = self.client.put(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_cancel_task_sent_token(self): @@ -1034,34 +1032,34 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) new_token = Token.objects.all()[0] - response = self.client.delete(url, format='json', - headers=headers) + response = self.client.delete(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(0, Token.objects.count()) url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_reapprove_task_delete_tokens(self): @@ -1072,39 +1070,41 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual(len(Token.objects.all()), 1) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - response = self.client.get(url, format='json') + response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) # Reapprove url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Old token no longer found url = "/v1/tokens/" + new_token.token - response = self.client.get(url, format='json') + response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(len(Token.objects.all()), 1) @@ -1117,30 +1117,30 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) new_task = Task.objects.all()[0] self.assertEqual(new_task.approved, True) - data = {'project_name': "test_project2", 'email': "test2@example.com"} - response = self.client.put(url, data, format='json', - headers=headers) + data = {"project_name": "test_project2", "email": "test2@example.com"} + response = self.client.put(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_cancel_task_own(self): @@ -1154,32 +1154,34 @@ class AdminAPITests(APITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.delete(url, format='json', - headers=headers) + response = self.client.delete(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - headers['roles'] = "admin" - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + headers["roles"] = "admin" + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - response = self.client.put(url, format='json', - headers=headers) + response = self.client.put(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_cancel_task_own_fail(self): @@ -1193,24 +1195,26 @@ class AdminAPITests(APITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - headers['project_id'] = "fake_project_id" - response = self.client.delete(url, format='json', - headers=headers) + headers["project_id"] = "fake_project_id" + response = self.client.delete(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_task_list(self): @@ -1223,38 +1227,47 @@ class AdminAPITests(APITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - data = {'email': "test2@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test2@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - data = {'email': "test3@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test3@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tasks" - response = self.client.get(url, format='json', headers=headers) + response = self.client.get(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.json()['tasks']), 3) + self.assertEqual(len(response.json()["tasks"]), 3) def test_task_list_ordering(self): """ @@ -1267,44 +1280,52 @@ class AdminAPITests(APITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - data = {'email': "test2@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test2@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - data = {'email': "test3@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test3@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tasks" - response = self.client.get(url, format='json', headers=headers) + response = self.client.get(url, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) sorted_list = sorted( - response.json()['tasks'], - key=lambda k: k['created_on'], - reverse=True) + response.json()["tasks"], key=lambda k: k["created_on"], reverse=True + ) for i, task in enumerate(sorted_list): - self.assertEqual(task, response.json()['tasks'][i]) + self.assertEqual(task, response.json()["tasks"][i]) def test_task_list_filter(self): """ @@ -1315,58 +1336,56 @@ class AdminAPITests(APITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - data = {'email': "test2@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test2@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project2", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project2", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } params = { - "filters": json.dumps({ - "task_type": {"exact": "create_project_and_user"} - }) + "filters": json.dumps({"task_type": {"exact": "create_project_and_user"}}) } url = "/v1/tasks" - response = self.client.get( - url, params, format='json', headers=headers - ) + response = self.client.get(url, params, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.json()['tasks']), 1) + self.assertEqual(len(response.json()["tasks"]), 1) params = { - "filters": json.dumps({ - "task_type": {"exact": "invite_user_to_project"} - }) + "filters": json.dumps({"task_type": {"exact": "invite_user_to_project"}}) } - response = self.client.get( - url, params, format='json', headers=headers - ) + response = self.client.get(url, params, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.json()['tasks']), 2) + self.assertEqual(len(response.json()["tasks"]), 2) # TODO(adriant): enable this test again when filters are properly # blacklisted. @@ -1378,55 +1397,57 @@ class AdminAPITests(APITestCase): """ project = mock.Mock() - project.id = 'test_project_id' - project.name = 'test_project' - project.domain = 'default' + project.id = "test_project_id" + project.name = "test_project" + project.domain = "default" project.roles = {} project2 = mock.Mock() - project2.id = 'test_project_id_2' - project2.name = 'test_project_2' - project2.domain = 'default' + project2.id = "test_project_id_2" + project2.name = "test_project_2" + project2.domain = "default" project2.roles = {} - setup_identity_cache( - {'test_project': project, 'test_project_2': project2}, {}) + setup_identity_cache({"test_project": project, "test_project_2": project2}, {}) url = "/v1/actions/InviteUser" headers = { - 'project_name': project.name, - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "owner@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": project.name, + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "owner@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': 'test_project_id'} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": "test_project_id", + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) headers = { - 'project_name': project2.name, - 'project_id': project2.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": project2.name, + "project_id": project2.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } params = { - "filters": json.dumps({ - "project_id": {"exact": "test_project_id"}, - "task_type": {"exact": "invite_user_to_project"} - }) + "filters": json.dumps( + { + "project_id": {"exact": "test_project_id"}, + "task_type": {"exact": "invite_user_to_project"}, + } + ) } url = "/v1/tasks" - response = self.client.get( - url, params, format='json', headers=headers - ) + response = self.client.get(url, params, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.json()['tasks']), 0) + self.assertEqual(len(response.json()["tasks"]), 0) def test_task_list_filter_formating(self): """ @@ -1434,75 +1455,48 @@ class AdminAPITests(APITestCase): """ headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } # not proper json - params = { - "filters": { - "task_type": {"exact": "create_project"} - } - } + params = {"filters": {"task_type": {"exact": "create_project"}}} url = "/v1/tasks" - response = self.client.get( - url, params, format='json', headers=headers - ) + response = self.client.get(url, params, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # inncorrect format - params = { - "filters": json.dumps("gibbberish") - } - response = self.client.get( - url, params, format='json', headers=headers - ) + params = {"filters": json.dumps("gibbberish")} + response = self.client.get(url, params, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # inncorrect format - params = { - "filters": json.dumps({ - "task_type": ["exact", "value"] - }) - } - response = self.client.get( - url, params, format='json', headers=headers - ) + params = {"filters": json.dumps({"task_type": ["exact", "value"]})} + response = self.client.get(url, params, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # invalid operation - params = { - "filters": json.dumps({ - "task_type": {"dont_find": "value"} - }) - } - response = self.client.get( - url, params, format='json', headers=headers - ) + params = {"filters": json.dumps({"task_type": {"dont_find": "value"}})} + response = self.client.get(url, params, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # invalid field - params = { - "filters": json.dumps({ - "fake": {"exact": "value"} - }) - } - response = self.client.get( - url, params, format='json', headers=headers - ) + params = {"filters": json.dumps({"fake": {"exact": "value"}})} + response = self.client.get(url, params, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.action_defaults.ResetUserPasswordAction.blacklisted_roles": [ - {'operation': 'append', 'value': "admin"}, + {"operation": "append", "value": "admin"}, ], - }) + }, + ) def test_reset_admin(self): """ Ensure that you cannot issue a password reset for an @@ -1512,27 +1506,30 @@ class AdminAPITests(APITestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="admin", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) self.assertEqual(0, Token.objects.count()) - @mock.patch('adjutant.common.tests.fake_clients.FakeManager.find_project') + @mock.patch("adjutant.common.tests.fake_clients.FakeManager.find_project") def test_apiview_error_handler(self, mocked_find): """ Ensure the handle_task_error function works as expected for APIViews. @@ -1541,33 +1538,33 @@ class AdminAPITests(APITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') - self.assertEqual( - response.status_code, status.HTTP_202_ACCEPTED) + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) mocked_find.side_effect = KeyError("Forced key error for testing.") new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid data = { - 'project_name': "test_project2", 'email': "test@example.com", + "project_name": "test_project2", + "email": "test@example.com", } headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - response = self.client.put(url, data, format='json', headers=headers) - self.assertEqual( - response.status_code, status.HTTP_503_SERVICE_UNAVAILABLE) + response = self.client.put(url, data, format="json", headers=headers) + self.assertEqual(response.status_code, status.HTTP_503_SERVICE_UNAVAILABLE) self.assertEqual( - response.json()['errors'], - ['Service temporarily unavailable, try again later.']) + response.json()["errors"], + ["Service temporarily unavailable, try again later."], + ) new_task = Task.objects.all()[0] new_notification = Notification.objects.all()[1] @@ -1575,7 +1572,11 @@ class AdminAPITests(APITestCase): self.assertTrue(new_notification.error) self.assertEqual( new_notification.notes, - {'errors': [ - "Error: KeyError('Forced key error for testing.') while " - "setting up task. See task itself for details."]}) + { + "errors": [ + "Error: KeyError('Forced key error for testing.') while " + "setting up task. See task itself for details." + ] + }, + ) self.assertEqual(new_notification.task, new_task) diff --git a/adjutant/api/v1/tests/test_api_openstack.py b/adjutant/api/v1/tests/test_api_openstack.py index c879b98..afe0cf4 100644 --- a/adjutant/api/v1/tests/test_api_openstack.py +++ b/adjutant/api/v1/tests/test_api_openstack.py @@ -24,16 +24,25 @@ from confspirator.tests import utils as conf_utils from adjutant.api.models import Token, Task from adjutant.common.tests import fake_clients from adjutant.common.tests.fake_clients import ( - FakeManager, setup_identity_cache, get_fake_neutron, get_fake_novaclient, - get_fake_cinderclient, get_fake_octaviaclient, cinder_cache, nova_cache, - neutron_cache, octavia_cache, setup_mock_caches, setup_quota_cache, - FakeResource) + FakeManager, + setup_identity_cache, + get_fake_neutron, + get_fake_novaclient, + get_fake_cinderclient, + get_fake_octaviaclient, + cinder_cache, + nova_cache, + neutron_cache, + octavia_cache, + setup_mock_caches, + setup_quota_cache, + FakeResource, +) from adjutant.common.tests.utils import AdjutantAPITestCase from adjutant.config import CONF -@mock.patch('adjutant.common.user_store.IdentityManager', - FakeManager) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) class OpenstackAPITests(AdjutantAPITestCase): """ DelegateAPI tests specific to the openstack style urls. @@ -53,23 +62,26 @@ class OpenstackAPITests(AdjutantAPITestCase): url = "/v1/openstack/users" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) def test_user_list(self): @@ -82,37 +94,43 @@ class OpenstackAPITests(AdjutantAPITestCase): url = "/v1/openstack/users" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) url = "/v1/openstack/users" - data = {'email': "test2@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test2@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.json()['users']), 2) - self.assertTrue(b'test2@example.com' in response.content) + self.assertEqual(len(response.json()["users"]), 2) + self.assertTrue(b"test2@example.com" in response.content) def test_user_list_inherited(self): """ @@ -120,64 +138,69 @@ class OpenstackAPITests(AdjutantAPITestCase): """ project = fake_clients.FakeProject(name="test_project") project2 = fake_clients.FakeProject( - name="test_project/child", parent_id=project.id) + name="test_project/child", parent_id=project.id + ) project3 = fake_clients.FakeProject( - name="test_project/child/another", parent_id=project2.id) + name="test_project/child/another", parent_id=project2.id + ) user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) user2 = fake_clients.FakeUser( - name="test2@example.com", password="123", - email="test2@example.com") + name="test2@example.com", password="123", email="test2@example.com" + ) user3 = fake_clients.FakeUser( - name="test3@example.com", password="123", - email="test2@example.com") + name="test3@example.com", password="123", email="test2@example.com" + ) assignments = [ fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_admin", - user={'id': user.id}, + user={"id": user.id}, inherited=True, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project2.id}}, + scope={"project": {"id": project2.id}}, role_name="project_mod", - user={'id': user2.id}, + user={"id": user2.id}, inherited=True, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project3.id}}, + scope={"project": {"id": project3.id}}, role_name="member", - user={'id': user3.id} + user={"id": user3.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project3.id}}, + scope={"project": {"id": project3.id}}, role_name="member", - user={'id': user3.id}, + user={"id": user3.id}, inherited=True, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project3.id}}, + scope={"project": {"id": project3.id}}, role_name="project_mod", - user={'id': user3.id} + user={"id": user3.id}, ), ] setup_identity_cache( - projects=[project, project2, project3], users=[user, user2, user3], - role_assignments=assignments) + projects=[project, project2, project3], + users=[user, user2, user3], + role_assignments=assignments, + ) url = "/v1/openstack/users" headers = { - 'project_name': "test_project", - 'project_id': project3.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project3.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } response = self.client.get(url, headers=headers) @@ -185,8 +208,8 @@ class OpenstackAPITests(AdjutantAPITestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) project_users = [] inherited_users = [] - for u in response_json['users']: - if u['cohort'] == 'Inherited': + for u in response_json["users"]: + if u["cohort"] == "Inherited": inherited_users.append(u) else: project_users.append(u) @@ -194,14 +217,14 @@ class OpenstackAPITests(AdjutantAPITestCase): self.assertEqual(len(project_users), 1) for u in inherited_users: - if u['id'] == user.id: - self.assertEqual(u['roles'], ['project_admin']) - if u['id'] == user2.id: - self.assertEqual(u['roles'], ['project_mod']) + if u["id"] == user.id: + self.assertEqual(u["roles"], ["project_admin"]) + if u["id"] == user2.id: + self.assertEqual(u["roles"], ["project_mod"]) normal_user = project_users[0] - self.assertEqual(normal_user['roles'], ['member', 'project_mod']) - self.assertEqual(normal_user['inherited_roles'], ['member']) + self.assertEqual(normal_user["roles"], ["member", "project_mod"]) + self.assertEqual(normal_user["inherited_roles"], ["member"]) def test_user_detail(self): """ @@ -211,40 +234,42 @@ class OpenstackAPITests(AdjutantAPITestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignments = [ fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id}, + user={"id": user.id}, inherited=True, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ), ] setup_identity_cache( - projects=[project], users=[user], role_assignments=assignments) + projects=[project], users=[user], role_assignments=assignments + ) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/openstack/users/%s" % user.id response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()['username'], 'test@example.com') - self.assertEqual(response.json()['roles'], ["member"]) - self.assertEqual(response.json()['inherited_roles'], ["member"]) + self.assertEqual(response.json()["username"], "test@example.com") + self.assertEqual(response.json()["roles"], ["member"]) + self.assertEqual(response.json()["inherited_roles"], ["member"]) def test_user_list_manageable(self): """ @@ -254,100 +279,103 @@ class OpenstackAPITests(AdjutantAPITestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) user2 = fake_clients.FakeUser( - name="test2@example.com", password="123", - email="test2@example.com") + name="test2@example.com", password="123", email="test2@example.com" + ) assignments = [ fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_admin", - user={'id': user.id} + user={"id": user.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user2.id} + user={"id": user2.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_mod", - user={'id': user2.id} + user={"id": user2.id}, ), ] setup_identity_cache( - projects=[project], users=[user, user2], - role_assignments=assignments) + projects=[project], users=[user, user2], role_assignments=assignments + ) url = "/v1/openstack/users" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/openstack/users" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.json()['users']), 2) + self.assertEqual(len(response.json()["users"]), 2) - for adj_user in response.json()['users']: - if adj_user['id'] == user.id: - self.assertFalse(adj_user['manageable']) - if adj_user['id'] == user2.id: - self.assertTrue(adj_user['manageable']) + for adj_user in response.json()["users"]: + if adj_user["id"] == user.id: + self.assertFalse(adj_user["manageable"]) + if adj_user["id"] == user2.id: + self.assertTrue(adj_user["manageable"]) def test_remove_user_role(self): """ Remove all roles on a user from our project """ project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } # admins removes role from the test user url = "/v1/openstack/users/%s/roles" % user.id - data = {'roles': ["member"]} - response = self.client.delete(url, data, - format='json', headers=admin_headers) + data = {"roles": ["member"]} + response = self.client.delete(url, data, format="json", headers=admin_headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_new_user_username_not_email(self): """ Ensure the new user workflow goes as expected. @@ -359,50 +387,44 @@ class OpenstackAPITests(AdjutantAPITestCase): url = "/v1/openstack/users" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id, 'username': 'user_name'} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + "username": "user_name", + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) +@mock.patch("adjutant.common.openstack_clients.get_novaclient", get_fake_novaclient) +@mock.patch("adjutant.common.openstack_clients.get_neutronclient", get_fake_neutron) +@mock.patch("adjutant.common.openstack_clients.get_cinderclient", get_fake_cinderclient) @mock.patch( - 'adjutant.common.user_store.IdentityManager', - FakeManager) -@mock.patch( - 'adjutant.common.openstack_clients.get_novaclient', - get_fake_novaclient) -@mock.patch( - 'adjutant.common.openstack_clients.get_neutronclient', - get_fake_neutron) -@mock.patch( - 'adjutant.common.openstack_clients.get_cinderclient', - get_fake_cinderclient) -@mock.patch( - 'adjutant.common.openstack_clients.get_octaviaclient', - get_fake_octaviaclient) + "adjutant.common.openstack_clients.get_octaviaclient", get_fake_octaviaclient +) class QuotaAPITests(AdjutantAPITestCase): - def setUp(self): super(QuotaAPITests, self).setUp() - setup_mock_caches('RegionOne', 'test_project_id') - setup_mock_caches('RegionTwo', 'test_project_id') + setup_mock_caches("RegionOne", "test_project_id") + setup_mock_caches("RegionTwo", "test_project_id") - def check_quota_cache(self, region_name, project_id, size, - extra_services=None): + def check_quota_cache(self, region_name, project_id, size, extra_services=None): """ Helper function to check if the global quota caches now match the size defined in the config @@ -410,56 +432,53 @@ class QuotaAPITests(AdjutantAPITestCase): if extra_services is None: extra_services = [] - cinderquota = cinder_cache[region_name][project_id]['quota'] - gigabytes = CONF.quota.sizes[size]['cinder']['gigabytes'] - self.assertEqual(cinderquota['gigabytes'], gigabytes) + cinderquota = cinder_cache[region_name][project_id]["quota"] + gigabytes = CONF.quota.sizes[size]["cinder"]["gigabytes"] + self.assertEqual(cinderquota["gigabytes"], gigabytes) - novaquota = nova_cache[region_name][project_id]['quota'] - ram = CONF.quota.sizes[size]['nova']['ram'] - self.assertEqual(novaquota['ram'], ram) + novaquota = nova_cache[region_name][project_id]["quota"] + ram = CONF.quota.sizes[size]["nova"]["ram"] + self.assertEqual(novaquota["ram"], ram) - neutronquota = neutron_cache[region_name][project_id]['quota'] - network = CONF.quota.sizes[size]['neutron']['network'] - self.assertEqual(neutronquota['network'], network) + neutronquota = neutron_cache[region_name][project_id]["quota"] + network = CONF.quota.sizes[size]["neutron"]["network"] + self.assertEqual(neutronquota["network"], network) - if 'octavia' in extra_services: - octaviaquota = octavia_cache[region_name][project_id]['quota'] - load_balancer = CONF.quota.sizes.get( - size)['octavia']['load_balancer'] - self.assertEqual(octaviaquota['load_balancer'], load_balancer) + if "octavia" in extra_services: + octaviaquota = octavia_cache[region_name][project_id]["quota"] + load_balancer = CONF.quota.sizes.get(size)["octavia"]["load_balancer"] + self.assertEqual(octaviaquota["load_balancer"], load_balancer) def test_update_quota_no_history(self): """ Update the quota size of a project with no history """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'medium', - 'regions': ['RegionOne']} + data = {"size": "medium", "regions": ["RegionOne"]} - response = self.client.post(url, data, - headers=admin_headers, format='json') + response = self.client.post(url, data, headers=admin_headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") def test_update_quota_history(self): """ @@ -467,112 +486,102 @@ class QuotaAPITests(AdjutantAPITestCase): It should update the quota the first time but wait for admin approval the second time """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'medium', - 'regions': ['RegionOne']} - response = self.client.post(url, data, - headers=admin_headers, format='json') + data = {"size": "medium", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=admin_headers, format="json") # First check we can actually access the page correctly self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") - data = {'size': 'large', - 'regions': ['RegionOne']} - response = self.client.post(url, data, - headers=admin_headers, format='json') + data = {"size": "large", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=admin_headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have not changed - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") # Approve the quota change as admin headers = { - 'project_name': "admin_project", - 'project_id': project.id, - 'roles': "admin,member", - 'username': "admin", - 'user_id': "admin_id", - 'authenticated': True + "project_name": "admin_project", + "project_id": project.id, + "roles": "admin,member", + "username": "admin", + "user_id": "admin_id", + "authenticated": True, } # Grab the details for the second task and approve it new_task = Task.objects.all()[1] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data, - {'notes': ['Task completed successfully.']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {"notes": ["Task completed successfully."]}) # Quotas should have changed to large - self.check_quota_cache('RegionOne', project.id, 'large') + self.check_quota_cache("RegionOne", project.id, "large") def test_update_quota_history_smaller(self): """ Update quota to a smaller quota right after a change to a larger quota. Should auto approve to smaller quotas regardless of history. """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'medium', - 'regions': ['RegionOne']} - response = self.client.post(url, data, - headers=admin_headers, format='json') + data = {"size": "medium", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=admin_headers, format="json") # First check we can actually access the page correctly self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") - data = {'size': 'small', - 'regions': ['RegionOne']} - response = self.client.post(url, data, - headers=admin_headers, format='json') + data = {"size": "small", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=admin_headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed - self.check_quota_cache('RegionOne', project.id, 'small') + self.check_quota_cache("RegionOne", project.id, "small") def test_update_quota_old_history(self): """ @@ -580,49 +589,45 @@ class QuotaAPITests(AdjutantAPITestCase): It should update the quota the first time without approval """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'medium', - 'regions': ['RegionOne']} - response = self.client.post(url, data, - headers=admin_headers, format='json') + data = {"size": "medium", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=admin_headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") # Fudge the data to make the task occur 31 days ago task = Task.objects.all()[0] task.completed_on = timezone.now() - timedelta(days=32) task.save() - data = {'size': 'small', - 'regions': ['RegionOne']} - response = self.client.post(url, data, - headers=admin_headers, format='json') + data = {"size": "small", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=admin_headers, format="json") # First check we can actually access the page correctly self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed - self.check_quota_cache('RegionOne', project.id, 'small') + self.check_quota_cache("RegionOne", project.id, "small") def test_update_quota_other_project_history(self): """ @@ -630,55 +635,52 @@ class QuotaAPITests(AdjutantAPITestCase): with the 30 days per project limit. """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") - project2 = fake_clients.FakeProject( - name="second_project") + project2 = fake_clients.FakeProject(name="second_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project, project2], users=[user]) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } - setup_mock_caches('RegionOne', project2.id) + setup_mock_caches("RegionOne", project2.id) url = "/v1/openstack/quotas/" - data = {'size': 'medium', - 'regions': ['RegionOne']} - response = self.client.post(url, data, headers=headers, format='json') + data = {"size": "medium", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") headers = { - 'project_name': "second_project", - 'project_id': project2.id, - 'roles': "project_admin,member,project_mod", - 'username': "test2@example.com", - 'user_id': user.id, - 'authenticated': True + "project_name": "second_project", + "project_id": project2.id, + "roles": "project_admin,member,project_mod", + "username": "test2@example.com", + "user_id": user.id, + "authenticated": True, } - data = {'regions': ["RegionOne"], 'size': 'medium', - 'project_id': project2.id} - response = self.client.post(url, data, headers=headers, format='json') + data = {"regions": ["RegionOne"], "size": "medium", "project_id": project2.id} + response = self.client.post(url, data, headers=headers, format="json") # First check we can actually access the page correctly self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed - self.check_quota_cache('RegionOne', project2.id, 'medium') + self.check_quota_cache("RegionOne", project2.id, "medium") def test_update_quota_outside_range(self): """ @@ -686,319 +688,319 @@ class QuotaAPITests(AdjutantAPITestCase): project's pre-approved range. """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'large', - 'regions': ['RegionOne']} - response = self.client.post(url, data, - headers=admin_headers, format='json') + data = {"size": "large", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=admin_headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have not changed (stayed small) - self.check_quota_cache('RegionOne', project.id, 'small') + self.check_quota_cache("RegionOne", project.id, "small") # Approve and test for change # Approve the quota change as admin headers = { - 'project_name': "admin_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "admin", - 'user_id': "admin_id", - 'authenticated': True + "project_name": "admin_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "admin", + "user_id": "admin_id", + "authenticated": True, } # Grab the details for the task and approve it new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data, - {'notes': ['Task completed successfully.']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {"notes": ["Task completed successfully."]}) - self.check_quota_cache('RegionOne', project.id, 'large') + self.check_quota_cache("RegionOne", project.id, "large") def test_calculate_custom_quota_size(self): """ Calculates the best 'fit' quota size from a custom quota. """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': user.id, - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": user.id, + "authenticated": True, } - cinderquota = cinder_cache['RegionOne']['test_project_id']['quota'] - cinderquota['gigabytes'] = 6000 - novaquota = nova_cache['RegionOne']['test_project_id']['quota'] - novaquota['ram'] = 70000 - neutronquota = neutron_cache['RegionOne']['test_project_id']['quota'] - neutronquota['network'] = 4 + cinderquota = cinder_cache["RegionOne"]["test_project_id"]["quota"] + cinderquota["gigabytes"] = 6000 + novaquota = nova_cache["RegionOne"]["test_project_id"]["quota"] + novaquota["ram"] = 70000 + neutronquota = neutron_cache["RegionOne"]["test_project_id"]["quota"] + neutronquota["network"] = 4 url = "/v1/openstack/quotas/?regions=RegionOne" response = self.client.get(url, headers=admin_headers) # First check we can actually access the page correctly self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data['regions'][0]['current_quota_size'], 'small') + self.assertEqual(response.data["regions"][0]["current_quota_size"], "small") @conf_utils.modify_conf( CONF, operations={ "adjutant.quota.sizes": [ - {'operation': 'update', 'value': { - "zero": { - 'nova': { - 'instances': 0, 'cores': 0, 'ram': 0, 'floating_ips': 0, - 'fixed_ips': 0, 'metadata_items': 0, 'injected_files': 0, - 'injected_file_content_bytes': 0, 'key_pairs': 50, - 'security_groups': 0, 'security_group_rules': 0, }, - 'cinder': { - 'gigabytes': 0, 'snapshots': 0, 'volumes': 0, }, - 'neutron': { - 'floatingip': 0, 'network': 0, 'port': 0, 'router': 0, - 'security_group': 0, 'security_group_rule': 0} - } - }}, + { + "operation": "update", + "value": { + "zero": { + "nova": { + "instances": 0, + "cores": 0, + "ram": 0, + "floating_ips": 0, + "fixed_ips": 0, + "metadata_items": 0, + "injected_files": 0, + "injected_file_content_bytes": 0, + "key_pairs": 50, + "security_groups": 0, + "security_group_rules": 0, + }, + "cinder": {"gigabytes": 0, "snapshots": 0, "volumes": 0,}, + "neutron": { + "floatingip": 0, + "network": 0, + "port": 0, + "router": 0, + "security_group": 0, + "security_group_rule": 0, + }, + } + }, + }, ], "adjutant.quota.sizes_ascending": [ - {'operation': 'prepend', 'value': "zero"}, + {"operation": "prepend", "value": "zero"}, ], - }) + }, + ) def test_calculate_quota_size_zero(self): """ Ensures that a zero quota enabled picks up """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } - setup_quota_cache('RegionOne', project.id, 'small') + setup_quota_cache("RegionOne", project.id, "small") url = "/v1/openstack/quotas/?regions=RegionOne" response = self.client.get(url, headers=admin_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data['regions'][0]['current_quota_size'], 'small') + self.assertEqual(response.data["regions"][0]["current_quota_size"], "small") - cinderquota = cinder_cache['RegionOne'][project.id]['quota'] - cinderquota['gigabytes'] = 0 + cinderquota = cinder_cache["RegionOne"][project.id]["quota"] + cinderquota["gigabytes"] = 0 # Check that the zero value doesn't interfer with being small response = self.client.get(url, headers=admin_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data['regions'][0]['current_quota_size'], 'small') + self.assertEqual(response.data["regions"][0]["current_quota_size"], "small") - setup_quota_cache('RegionOne', project.id, 'zero') + setup_quota_cache("RegionOne", project.id, "zero") url = "/v1/openstack/quotas/?regions=RegionOne" response = self.client.get(url, headers=admin_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data['regions'][0]['current_quota_size'], 'zero') + self.assertEqual(response.data["regions"][0]["current_quota_size"], "zero") # Check that the zero quota will still be counted even if # one value is not zero - cinderquota = cinder_cache['RegionOne'][project.id]['quota'] - cinderquota['gigabytes'] = 600 + cinderquota = cinder_cache["RegionOne"][project.id]["quota"] + cinderquota["gigabytes"] = 600 response = self.client.get(url, headers=admin_headers) # First check we can actually access the page correctly self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data['regions'][0]['current_quota_size'], 'zero') + self.assertEqual(response.data["regions"][0]["current_quota_size"], "zero") def test_return_quota_history(self): """ Ensures that the correct quota history and usage data is returned """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'large', - 'regions': ['RegionOne', 'RegionTwo']} - response = self.client.post(url, data, headers=headers, format='json') + data = {"size": "large", "regions": ["RegionOne", "RegionTwo"]} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - recent_task = response.data['active_quota_tasks'][0] - self.assertEqual( - recent_task['size'], 'large') - self.assertEqual( - recent_task['request_user'], 'test@example.com') - self.assertEqual( - recent_task['status'], 'Awaiting Approval') + recent_task = response.data["active_quota_tasks"][0] + self.assertEqual(recent_task["size"], "large") + self.assertEqual(recent_task["request_user"], "test@example.com") + self.assertEqual(recent_task["status"], "Awaiting Approval") def test_set_multi_region_quota(self): """ Sets a quota to all to all regions in a project """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'medium', 'regions': ['RegionOne', 'RegionTwo']} - response = self.client.post(url, data, headers=headers, format='json') + data = {"size": "medium", "regions": ["RegionOne", "RegionTwo"]} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.check_quota_cache('RegionOne', 'test_project_id', 'medium') + self.check_quota_cache("RegionOne", "test_project_id", "medium") - self.check_quota_cache('RegionTwo', 'test_project_id', 'medium') + self.check_quota_cache("RegionTwo", "test_project_id", "medium") def test_set_multi_region_quota_history(self): """ Attempts to set a multi region quota with a multi region update history """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'medium', - 'regions': ['RegionOne', 'RegionTwo']} - response = self.client.post(url, data, headers=headers, format='json') + data = {"size": "medium", "regions": ["RegionOne", "RegionTwo"]} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") - self.check_quota_cache('RegionTwo', project.id, 'medium') + self.check_quota_cache("RegionTwo", project.id, "medium") - data = {'size': 'large'} - response = self.client.post(url, data, headers=headers, format='json') + data = {"size": "large"} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # All of them stay the same - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") - self.check_quota_cache('RegionTwo', project.id, 'medium') + self.check_quota_cache("RegionTwo", project.id, "medium") # Approve the task headers = { - 'project_name': "admin_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "admin", - 'user_id': "admin_id", - 'authenticated': True + "project_name": "admin_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "admin", + "user_id": "admin_id", + "authenticated": True, } new_task = Task.objects.all()[1] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data, - {'notes': ['Task completed successfully.']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {"notes": ["Task completed successfully."]}) - self.check_quota_cache('RegionOne', project.id, 'large') + self.check_quota_cache("RegionOne", project.id, "large") - self.check_quota_cache('RegionTwo', project.id, 'large') + self.check_quota_cache("RegionTwo", project.id, "large") def test_set_multi_quota_single_history(self): """ @@ -1006,170 +1008,156 @@ class QuotaAPITests(AdjutantAPITestCase): update history """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } # Setup custom parts of the quota still within 'small' however url = "/v1/openstack/quotas/" - data = {'size': 'medium', - 'regions': ['RegionOne']} - response = self.client.post(url, data, headers=headers, format='json') + data = {"size": "medium", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=headers, format="json") # First check we can actually access the page correctly self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") url = "/v1/openstack/quotas/" - data = {'size': 'small', - 'regions': ['RegionOne', 'RegionTwo']} - response = self.client.post(url, data, headers=headers, format='json') + data = {"size": "small", "regions": ["RegionOne", "RegionTwo"]} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Quotas stay the same - self.check_quota_cache('RegionOne', project.id, 'medium') - self.check_quota_cache('RegionTwo', project.id, 'small') + self.check_quota_cache("RegionOne", project.id, "medium") + self.check_quota_cache("RegionTwo", project.id, "small") headers = { - 'project_name': "admin_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "admin", - 'user_id': "admin_id", - 'authenticated': True + "project_name": "admin_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "admin", + "user_id": "admin_id", + "authenticated": True, } new_task = Task.objects.all()[1] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data, - {'notes': ['Task completed successfully.']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {"notes": ["Task completed successfully."]}) - self.check_quota_cache('RegionOne', project.id, 'small') - self.check_quota_cache('RegionTwo', project.id, 'small') + self.check_quota_cache("RegionOne", project.id, "small") + self.check_quota_cache("RegionTwo", project.id, "small") def test_set_quota_over_limit(self): """ Attempts to set a smaller quota than the current usage """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } - setup_quota_cache('RegionOne', project.id, 'medium') + setup_quota_cache("RegionOne", project.id, "medium") # Setup current quota as medium # Create a number of lists with limits higher than the small quota global nova_cache - nova_cache['RegionOne'][project.id][ - 'absolute']["totalInstancesUsed"] = 11 + nova_cache["RegionOne"][project.id]["absolute"]["totalInstancesUsed"] = 11 url = "/v1/openstack/quotas/" - data = {'size': 'small', - 'regions': ['RegionOne']} - response = self.client.post(url, data, - headers=headers, format='json') + data = {"size": "small", "regions": ["RegionOne"]} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") - data = {'size': 'small', - 'regions': ['RegionOne']} + data = {"size": "small", "regions": ["RegionOne"]} - nova_cache['RegionOne'][project.id][ - 'absolute']["totalInstancesUsed"] = 10 + nova_cache["RegionOne"][project.id]["absolute"]["totalInstancesUsed"] = 10 # Test for cinder resources volume_list = [FakeResource(10) for i in range(21)] - cinder_cache['RegionOne'][project.id]['volumes'] = volume_list + cinder_cache["RegionOne"][project.id]["volumes"] = volume_list - response = self.client.post(url, data, - headers=headers, format='json') + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") # Test for neutron resources - cinder_cache['RegionOne'][project.id]['volumes'] = [] + cinder_cache["RegionOne"][project.id]["volumes"] = [] net_list = [{} for i in range(4)] - neutron_cache['RegionOne'][project.id]['networks'] = net_list - response = self.client.post(url, data, - headers=headers, format='json') + neutron_cache["RegionOne"][project.id]["networks"] = net_list + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.check_quota_cache('RegionOne', project.id, 'medium') + self.check_quota_cache("RegionOne", project.id, "medium") # Check that after they are all cleared to sub small levels # the quota updates - neutron_cache['RegionOne'][project.id]['networks'] = [] - response = self.client.post(url, data, - headers=headers, format='json') + neutron_cache["RegionOne"][project.id]["networks"] = [] + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.check_quota_cache('RegionOne', project.id, 'small') + self.check_quota_cache("RegionOne", project.id, "small") def test_set_quota_invalid_region(self): """ Attempts to set a quota on a non-existent region """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'small', - 'regions': ['RegionThree']} - response = self.client.post(url, data, - headers=headers, format='json') + data = {"size": "small", "regions": ["RegionThree"]} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -1177,125 +1165,128 @@ class QuotaAPITests(AdjutantAPITestCase): CONF, operations={ "adjutant.workflow.tasks.update_quota.allow_auto_approve": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_no_auto_approved_quota_change(self): """ Test allow_auto_approve config setting on a task.""" - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'medium', 'regions': ['RegionOne', 'RegionTwo']} - response = self.client.post(url, data, headers=headers, format='json') + data = {"size": "medium", "regions": ["RegionOne", "RegionTwo"]} + response = self.client.post(url, data, headers=headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.check_quota_cache('RegionOne', 'test_project_id', 'small') + self.check_quota_cache("RegionOne", "test_project_id", "small") - self.check_quota_cache('RegionTwo', 'test_project_id', 'small') + self.check_quota_cache("RegionTwo", "test_project_id", "small") def test_view_correct_sizes(self): """ Calculates the best 'fit' quota size from a custom quota. """ - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': user.id, - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": user.id, + "authenticated": True, } url = "/v1/openstack/quotas/?regions=RegionOne" response = self.client.get(url, headers=admin_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["regions"][0]["current_quota_size"], "small") self.assertEqual( - response.data['regions'][0]['current_quota_size'], 'small') - self.assertEqual( - response.data['regions'][0]['quota_change_options'], ['medium']) + response.data["regions"][0]["quota_change_options"], ["medium"] + ) - cinder_cache['RegionOne'][project.id][ - 'quota'] = CONF.quota.sizes['large']['cinder'] + cinder_cache["RegionOne"][project.id]["quota"] = CONF.quota.sizes["large"][ + "cinder" + ] - nova_cache['RegionOne'][project.id][ - 'quota'] = CONF.quota.sizes['large']['nova'] + nova_cache["RegionOne"][project.id]["quota"] = CONF.quota.sizes["large"]["nova"] - neutron_cache['RegionOne'][project.id][ - 'quota'] = CONF.quota.sizes['large']['neutron'] + neutron_cache["RegionOne"][project.id]["quota"] = CONF.quota.sizes["large"][ + "neutron" + ] response = self.client.get(url, headers=admin_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["regions"][0]["current_quota_size"], "large") self.assertEqual( - response.data['regions'][0]['current_quota_size'], 'large') - self.assertEqual( - response.data['regions'][0]['quota_change_options'], - ['small', 'medium']) + response.data["regions"][0]["quota_change_options"], ["small", "medium"] + ) @conf_utils.modify_conf( CONF, operations={ "adjutant.quota.services": [ - {'operation': 'override', 'value': { - '*': ['cinder', 'neutron', 'nova', 'octavia']}}, + { + "operation": "override", + "value": {"*": ["cinder", "neutron", "nova", "octavia"]}, + }, ], - }) + }, + ) def test_update_quota_no_history_with_octavia(self): """ Update quota for octavia.""" - project = fake_clients.FakeProject( - name="test_project", id='test_project_id') + project = fake_clients.FakeProject(name="test_project", id="test_project_id") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) admin_headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "user_id", + "authenticated": True, } url = "/v1/openstack/quotas/" - data = {'size': 'medium', - 'regions': ['RegionOne']} + data = {"size": "medium", "regions": ["RegionOne"]} - response = self.client.post(url, data, - headers=admin_headers, format='json') + response = self.client.post(url, data, headers=admin_headers, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Then check to see the quotas have changed self.check_quota_cache( - 'RegionOne', project.id, 'medium', extra_services=['octavia']) + "RegionOne", project.id, "medium", extra_services=["octavia"] + ) diff --git a/adjutant/api/v1/tests/test_api_taskview.py b/adjutant/api/v1/tests/test_api_taskview.py index 119db81..ce2214c 100644 --- a/adjutant/api/v1/tests/test_api_taskview.py +++ b/adjutant/api/v1/tests/test_api_taskview.py @@ -23,15 +23,13 @@ from confspirator.tests import utils as conf_utils from adjutant.api.models import Token, Notification from adjutant.tasks.models import Task from adjutant.tasks.v1.projects import CreateProjectAndUser -from adjutant.common.tests.fake_clients import ( - FakeManager, setup_identity_cache) +from adjutant.common.tests.fake_clients import FakeManager, setup_identity_cache from adjutant.common.tests import fake_clients from adjutant.common.tests.utils import AdjutantAPITestCase from adjutant.config import CONF -@mock.patch('adjutant.common.user_store.IdentityManager', - FakeManager) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) class DelegateAPITests(AdjutantAPITestCase): """ Tests to ensure the approval/token workflow does what is @@ -51,39 +49,55 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'wrong_email_field': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "wrong_email_field": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.json(), - {'errors': {'email': ['This field is required.']}}) + response.json(), {"errors": {"email": ["This field is required."]}} + ) - data = {'email': "not_a_valid_email", 'roles': ["not_a_valid_role"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "not_a_valid_email", + "roles": ["not_a_valid_role"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( response.json(), - {'errors': { - 'email': ['Enter a valid email address.'], - 'roles': ['"not_a_valid_role" is not a valid choice.']}}) + { + "errors": { + "email": ["Enter a valid email address."], + "roles": ['"not_a_valid_role" is not a valid choice.'], + } + }, + ) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.invite_user_to_project.emails": [ - {'operation': 'update', 'value': { - "initial": None, "token": {"subject": "invite_user_to_project"}}}, + { + "operation": "update", + "value": { + "initial": None, + "token": {"subject": "invite_user_to_project"}, + }, + }, ], - }) + }, + ) def test_new_user(self): """ Ensure the new user workflow goes as expected. @@ -95,31 +109,34 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, 'invite_user_to_project') + self.assertEqual(mail.outbox[0].subject, "invite_user_to_project") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(mail.outbox), 2) self.assertEqual( - fake_clients.identity_cache['new_users'][0].name, - 'test@example.com') + fake_clients.identity_cache["new_users"][0].name, "test@example.com" + ) def test_new_user_no_project(self): """ @@ -129,18 +146,21 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': 'test_project_id'} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": "test_project_id", + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json(), {'errors': ['actions invalid']}) + self.assertEqual(response.json(), {"errors": ["actions invalid"]}) def test_new_user_not_my_project(self): """ @@ -151,16 +171,19 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': 'test_project_id'} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": "test_project_id", + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_new_user_not_authenticated(self): @@ -172,13 +195,15 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = {} - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': 'test_project_id'} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": "test_project_id", + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual( - response.json(), - {'errors': ["Credentials incorrect or none given."]} + response.json(), {"errors": ["Credentials incorrect or none given."]} ) def test_add_user_existing(self): @@ -188,29 +213,33 @@ class DelegateAPITests(AdjutantAPITestCase): project = fake_clients.FakeProject(name="parent_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(projects=[project], users=[user]) url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True} - response = self.client.post(url, data, format='json') + data = {"confirm": True} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) def test_add_user_existing_with_role(self): @@ -223,33 +252,36 @@ class DelegateAPITests(AdjutantAPITestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual( - response.json(), - {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) tasks = Task.objects.all() self.assertEqual(1, len(tasks)) @@ -263,51 +295,53 @@ class DelegateAPITests(AdjutantAPITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual( - response.json(), - {'notes': ['created token']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual(response.json(), {"notes": ["created token"]}) - new_project = fake_clients.identity_cache['new_projects'][0] - self.assertEqual(new_project.name, 'test_project') + new_project = fake_clients.identity_cache["new_projects"][0] + self.assertEqual(new_project.name, "test_project") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.create_project_and_user.notifications": [ - {'operation': 'override', 'value': { - "standard_handler_config": { - "EmailNotification": { - 'emails': ['example_notification@example.com'], - 'reply': 'no-reply@example.com', + { + "operation": "override", + "value": { + "standard_handler_config": { + "EmailNotification": { + "emails": ["example_notification@example.com"], + "reply": "no-reply@example.com", + } } - } - }}, + }, + }, ], - }) + }, + ) def test_new_project_invalid_on_submit(self): """ Ensures that when a project becomes invalid at the submit stage @@ -317,35 +351,33 @@ class DelegateAPITests(AdjutantAPITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual( - response.data, - {'notes': ['created token']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual(response.data, {"notes": ["created token"]}) self.assertEqual(len(mail.outbox), 3) - fake_clients.identity_cache['projects'] = {} + fake_clients.identity_cache["projects"] = {} new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(len(mail.outbox), 3) @@ -360,26 +392,25 @@ class DelegateAPITests(AdjutantAPITestCase): setup_identity_cache(projects=[project]) url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual( - response.json(), - {'errors': ['actions invalid']}) + self.assertEqual(response.json(), {"errors": ["actions invalid"]}) def test_new_project_existing_user(self): """ @@ -388,34 +419,33 @@ class DelegateAPITests(AdjutantAPITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) # unauthenticated sign up as existing user url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': user.email} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": user.email} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # approve the sign-up as admin headers = { - 'project_name': "admin_project", - 'project_id': "admin_project_id", - 'roles': "admin,member", - 'username': "admin", - 'user_id': "admin_id", - 'authenticated': True + "project_name": "admin_project", + "project_id": "admin_project_id", + "roles": "admin,member", + "username": "admin", + "user_id": "admin_id", + "authenticated": True, } new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.json(), - {'notes': ['Task completed successfully.']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json(), {"notes": ["Task completed successfully."]}) def test_new_project_existing_project_new_user(self): """ @@ -425,44 +455,40 @@ class DelegateAPITests(AdjutantAPITestCase): # create signup#1 - project1 with user 1 url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Create signup#2 - project1 with user 2 - data = {'project_name': "test_project", 'email': "test2@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test2@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) headers = { - 'project_name': "admin_project", - 'project_id': "admin_project_id", - 'roles': "admin,member", - 'username': "admin", - 'user_id': "admin_id", - 'authenticated': True + "project_name": "admin_project", + "project_id": "admin_project_id", + "roles": "admin,member", + "username": "admin", + "user_id": "admin_id", + "authenticated": True, } # approve signup #1 new_task1 = Task.objects.all()[0] url = "/v1/tasks/" + new_task1.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual( - response.json(), - {'notes': ['created token']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual(response.json(), {"notes": ["created token"]}) # Attempt to approve signup #2 new_task2 = Task.objects.all()[1] url = "/v1/tasks/" + new_task2.uuid - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual( - response.json(), - {'errors': ['actions invalid']} + response = self.client.post( + url, {"approved": True}, format="json", headers=headers ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.json(), {"errors": ["actions invalid"]}) def test_reset_user(self): """ @@ -471,24 +497,26 @@ class DelegateAPITests(AdjutantAPITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'new_test_password'} - response = self.client.post(url, data, format='json') + data = {"password": "new_test_password"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(user.password, 'new_test_password') + self.assertEqual(user.password, "new_test_password") def test_reset_user_duplicate(self): """ @@ -498,28 +526,31 @@ class DelegateAPITests(AdjutantAPITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) # Submit password reset url = "/v1/actions/ResetPassword" - data = {'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) # Verify the first token doesn't work first_token = Token.objects.all()[0] # Submit password reset again - response = self.client.post(url, data, format='json') + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) # confirm the old toke has been cleared: second_token = Token.objects.all()[0] @@ -527,10 +558,10 @@ class DelegateAPITests(AdjutantAPITestCase): # Now reset with the second token url = "/v1/tokens/" + second_token.token - data = {'password': 'new_test_password2'} - response = self.client.post(url, data, format='json') + data = {"password": "new_test_password2"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(user.password, 'new_test_password2') + self.assertEqual(user.password, "new_test_password2") def test_reset_user_no_existing(self): """ @@ -540,12 +571,13 @@ class DelegateAPITests(AdjutantAPITestCase): setup_identity_cache() url = "/v1/actions/ResetPassword" - data = {'email': "test@exampleinvalid.com"} - response = self.client.post(url, data, format='json') + data = {"email": "test@exampleinvalid.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) self.assertFalse(len(Token.objects.all())) @@ -557,27 +589,25 @@ class DelegateAPITests(AdjutantAPITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) new_task = Task.objects.all()[0] headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/notifications" response = self.client.get(url, headers=headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.json()['notifications'][0]['task'], - new_task.uuid) + self.assertEqual(response.json()["notifications"][0]["task"], new_task.uuid) def test_duplicate_tasks_new_project(self): """ @@ -587,14 +617,14 @@ class DelegateAPITests(AdjutantAPITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - response = self.client.post(url, data, format='json') + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_409_CONFLICT) - data = {'project_name': "test_project_2", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project_2", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) def test_duplicate_tasks_new_user(self): @@ -607,27 +637,33 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) - response = self.client.post(url, data, format='json', headers=headers) + self.assertEqual(response.json(), {"notes": ["task created"]}) + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_409_CONFLICT) - data = {'email': "test2@example.com", 'roles': ["member"], - 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test2@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) - response = self.client.post(url, data, format='json', headers=headers) + self.assertEqual(response.json(), {"notes": ["task created"]}) + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_409_CONFLICT) def test_update_email_task(self): @@ -637,57 +673,67 @@ class DelegateAPITests(AdjutantAPITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/UpdateEmail" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': user.id, - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": user.id, + "authenticated": True, } - data = {'new_email': "new_test@example.com"} - response = self.client.post(url, data, format='json', headers=headers) + data = {"new_email": "new_test@example.com"} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True} - response = self.client.post(url, data, format='json') + data = {"confirm": True} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(user.name, 'new_test@example.com') + self.assertEqual(user.name, "new_test@example.com") @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.update_user_email.additional_actions": [ - {'operation': 'append', 'value': "SendAdditionalEmailAction"}, + {"operation": "append", "value": "SendAdditionalEmailAction"}, ], "adjutant.workflow.tasks.update_user_email.emails": [ - {'operation': 'update', 'value': { - "initial": None, "token": {"subject": "update_user_email_token"}}}, + { + "operation": "update", + "value": { + "initial": None, + "token": {"subject": "update_user_email_token"}, + }, + }, ], "adjutant.workflow.tasks.update_user_email.actions": [ - {'operation': 'update', 'value': { - "SendAdditionalEmailAction": { - "prepare": { - 'subject': 'update_user_email_additional', - 'template': 'update_user_email_started.txt', - 'email_roles': [], - 'email_current_user': True, + { + "operation": "update", + "value": { + "SendAdditionalEmailAction": { + "prepare": { + "subject": "update_user_email_additional", + "template": "update_user_email_started.txt", + "email_roles": [], + "email_current_user": True, + } } - } - }}, + }, + }, ], - }) + }, + ) def test_update_email_task_send_email_to_current_user(self): """ Tests the email update workflow, and ensures that when setup @@ -695,42 +741,42 @@ class DelegateAPITests(AdjutantAPITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/UpdateEmail" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': user.id, - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": user.id, + "authenticated": True, } - data = {'new_email': "new_test@example.com"} - response = self.client.post(url, data, format='json', headers=headers) + data = {"new_email": "new_test@example.com"} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.data, {'notes': ['task created']}) + self.assertEqual(response.data, {"notes": ["task created"]}) self.assertEqual(len(mail.outbox), 2) - self.assertEqual(mail.outbox[0].to, ['test@example.com']) - self.assertEqual( - mail.outbox[0].subject, 'update_user_email_additional') + self.assertEqual(mail.outbox[0].to, ["test@example.com"]) + self.assertEqual(mail.outbox[0].subject, "update_user_email_additional") - self.assertEqual(mail.outbox[1].to, ['new_test@example.com']) - self.assertEqual(mail.outbox[1].subject, 'update_user_email_token') + self.assertEqual(mail.outbox[1].to, ["new_test@example.com"]) + self.assertEqual(mail.outbox[1].subject, "update_user_email_token") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True} - response = self.client.post(url, data, format='json') + data = {"confirm": True} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(user.name, 'new_test@example.com') + self.assertEqual(user.name, "new_test@example.com") self.assertEqual(len(mail.outbox), 3) @@ -738,28 +784,37 @@ class DelegateAPITests(AdjutantAPITestCase): CONF, operations={ "adjutant.workflow.tasks.update_user_email.additional_actions": [ - {'operation': 'append', 'value': "SendAdditionalEmailAction"}, + {"operation": "append", "value": "SendAdditionalEmailAction"}, ], "adjutant.workflow.tasks.update_user_email.emails": [ - {'operation': 'update', 'value': { - "initial": None, "token": {"subject": "update_user_email_token"}}}, + { + "operation": "update", + "value": { + "initial": None, + "token": {"subject": "update_user_email_token"}, + }, + }, ], "adjutant.workflow.tasks.update_user_email.actions": [ - {'operation': 'update', 'value': { - "SendAdditionalEmailAction": { - "prepare": { - 'subject': 'update_user_email_additional', - 'template': 'update_user_email_started.txt', - 'email_roles': [], - 'email_current_user': True, + { + "operation": "update", + "value": { + "SendAdditionalEmailAction": { + "prepare": { + "subject": "update_user_email_additional", + "template": "update_user_email_started.txt", + "email_roles": [], + "email_current_user": True, + } } - } - }}, + }, + }, ], "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_update_email_task_send_email_current_name_not_email(self): """ Tests the email update workflow when USERNAME_IS_EMAIL=False, and @@ -768,40 +823,40 @@ class DelegateAPITests(AdjutantAPITestCase): """ user = fake_clients.FakeUser( - name="nkdfslnkls", password="123", email="test@example.com") + name="nkdfslnkls", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/UpdateEmail" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "nkdfslnkls", - 'user_id': user.id, - 'authenticated': True, - 'email': 'test@example.com', + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "nkdfslnkls", + "user_id": user.id, + "authenticated": True, + "email": "test@example.com", } - data = {'new_email': "new_test@example.com"} - response = self.client.post(url, data, format='json', headers=headers) + data = {"new_email": "new_test@example.com"} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.data, {'notes': ['task created']}) + self.assertEqual(response.data, {"notes": ["task created"]}) self.assertEqual(len(mail.outbox), 2) - self.assertEqual(mail.outbox[0].to, ['test@example.com']) - self.assertEqual( - mail.outbox[0].subject, 'update_user_email_additional') + self.assertEqual(mail.outbox[0].to, ["test@example.com"]) + self.assertEqual(mail.outbox[0].subject, "update_user_email_additional") - self.assertEqual(mail.outbox[1].to, ['new_test@example.com']) - self.assertEqual(mail.outbox[1].subject, 'update_user_email_token') + self.assertEqual(mail.outbox[1].to, ["new_test@example.com"]) + self.assertEqual(mail.outbox[1].subject, "update_user_email_token") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True} - response = self.client.post(url, data, format='json') + data = {"confirm": True} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(mail.outbox), 3) @@ -809,65 +864,68 @@ class DelegateAPITests(AdjutantAPITestCase): def test_update_email_task_invalid_email(self): user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/UpdateEmail" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': user.id, - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": user.id, + "authenticated": True, } - data = {'new_email': "new_test@examplecom"} - response = self.client.post(url, data, format='json', headers=headers) + data = {"new_email": "new_test@examplecom"} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.json(), - {'errors': {'new_email': [u'Enter a valid email address.']}}) + response.json(), {"errors": {"new_email": ["Enter a valid email address."]}} + ) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], "adjutant.workflow.tasks.update_user_email.emails": [ - {'operation': 'update', 'value': {"initial": None}}, + {"operation": "update", "value": {"initial": None}}, ], - }) + }, + ) def test_update_email_pre_existing_user_with_email(self): user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) user2 = fake_clients.FakeUser( - name="new_test@example.com", password="123", - email="new_test@example.com") + name="new_test@example.com", password="123", email="new_test@example.com" + ) setup_identity_cache(users=[user, user2]) url = "/v1/actions/UpdateEmail" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True, - 'project_domain_id': 'default', + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, + "project_domain_id": "default", } - data = {'new_email': "new_test@example.com"} - response = self.client.post(url, data, format='json', headers=headers) + data = {"new_email": "new_test@example.com"} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json(), {'errors': ['actions invalid']}) + self.assertEqual(response.json(), {"errors": ["actions invalid"]}) self.assertEqual(len(Token.objects.all()), 0) self.assertEqual(len(mail.outbox), 0) @@ -876,48 +934,50 @@ class DelegateAPITests(AdjutantAPITestCase): CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], "adjutant.workflow.tasks.update_user_email.emails": [ - {'operation': 'update', 'value': {"initial": None}}, + {"operation": "update", "value": {"initial": None}}, ], - }) + }, + ) def test_update_email_user_with_email_username_not_email(self): user = fake_clients.FakeUser( - name="test", password="123", email="test@example.com") + name="test", password="123", email="test@example.com" + ) user2 = fake_clients.FakeUser( - name="new_test", password="123", - email="new_test@example.com") + name="new_test", password="123", email="new_test@example.com" + ) setup_identity_cache(users=[user, user2]) url = "/v1/actions/UpdateEmail" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': user.id, - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": user.id, + "authenticated": True, } - data = {'new_email': "new_test@example.com"} - response = self.client.post(url, data, format='json', headers=headers) + data = {"new_email": "new_test@example.com"} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) self.assertEqual(len(mail.outbox), 1) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True} - response = self.client.post(url, data, format='json') + data = {"confirm": True} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(user.email, 'new_test@example.com') + self.assertEqual(user.email, "new_test@example.com") def test_update_email_task_not_authenticated(self): """ @@ -925,16 +985,16 @@ class DelegateAPITests(AdjutantAPITestCase): """ user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/UpdateEmail" - headers = { - } + headers = {} - data = {'new_email': "new_test@examplecom"} - response = self.client.post(url, data, format='json', headers=headers) + data = {"new_email": "new_test@examplecom"} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) @@ -942,53 +1002,61 @@ class DelegateAPITests(AdjutantAPITestCase): CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_update_email_task_username_not_email(self): user = fake_clients.FakeUser( - name="test_user", password="123", email="test@example.com") + name="test_user", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) url = "/v1/actions/UpdateEmail" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "test_user", - 'user_id': user.id, - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "test_user", + "user_id": user.id, + "authenticated": True, } - data = {'new_email': "new_test@example.com"} - response = self.client.post(url, data, format='json', headers=headers) + data = {"new_email": "new_test@example.com"} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True} - response = self.client.post(url, data, format='json') + data = {"confirm": True} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(user.name, "test_user") - self.assertEqual(user.email, 'new_test@example.com') + self.assertEqual(user.email, "new_test@example.com") # Tests for USERNAME_IS_EMAIL=False @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], "adjutant.workflow.tasks.invite_user_to_project.emails": [ - {'operation': 'update', 'value': { - "initial": None, "token": {"subject": "invite_user_to_project"}}}, + { + "operation": "update", + "value": { + "initial": None, + "token": {"subject": "invite_user_to_project"}, + }, + }, ], - }) + }, + ) def test_invite_user_to_project_email_not_username(self): """ Invites a user where the email is different to the username. @@ -999,45 +1067,53 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "user", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "user", + "user_id": "test_user_id", + "authenticated": True, } - data = {'username': 'new_user', 'email': "new@example.com", - 'roles': ["member"], 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "username": "new_user", + "email": "new@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, 'invite_user_to_project') - self.assertEqual(mail.outbox[0].to[0], 'new@example.com') + self.assertEqual(mail.outbox[0].subject, "invite_user_to_project") + self.assertEqual(mail.outbox[0].to[0], "new@example.com") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(mail.outbox), 2) - self.assertEqual( - fake_clients.identity_cache['new_users'][0].name, - 'new_user') + self.assertEqual(fake_clients.identity_cache["new_users"][0].name, "new_user") @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], "adjutant.workflow.tasks.reset_user_password.emails": [ - {'operation': 'update', 'value': { - "initial": None, "token": {"subject": "Password Reset for OpenStack"}}}, + { + "operation": "update", + "value": { + "initial": None, + "token": {"subject": "Password Reset for OpenStack"}, + }, + }, ], - }) + }, + ) def test_reset_user_username_not_email(self): """ Ensure the reset user workflow goes as expected. @@ -1045,7 +1121,8 @@ class DelegateAPITests(AdjutantAPITestCase): """ user = fake_clients.FakeUser( - name="test_user", password="123", email="test@example.com") + name="test_user", password="123", email="test@example.com" + ) setup_identity_cache(users=[user]) @@ -1058,91 +1135,103 @@ class DelegateAPITests(AdjutantAPITestCase): # store emails in their own field # Currently this is an issue for the forked adjutant # horizon - data = {'email': "test@example.com", 'username': 'test_user'} - response = self.client.post(url, data, format='json') + data = {"email": "test@example.com", "username": "test_user"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual( - response.json()['notes'], - ['If user with email exists, reset token will be issued.']) + response.json()["notes"], + ["If user with email exists, reset token will be issued."], + ) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, - 'Password Reset for OpenStack') - self.assertEqual(mail.outbox[0].to[0], 'test@example.com') + self.assertEqual(mail.outbox[0].subject, "Password Reset for OpenStack") + self.assertEqual(mail.outbox[0].to[0], "test@example.com") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'new_test_password'} - response = self.client.post(url, data, format='json') + data = {"password": "new_test_password"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(user.password, 'new_test_password') + self.assertEqual(user.password, "new_test_password") @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.username_is_email": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_new_project_username_not_email(self): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com", - 'username': 'test'} - response = self.client.post(url, data, format='json') + data = { + "project_name": "test_project", + "email": "test@example.com", + "username": "test", + } + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - data = {'email': "new_test@example.com", 'username': "new", - 'project_name': 'new_project'} - response = self.client.post(url, data, format='json') + data = { + "email": "new_test@example.com", + "username": "new", + "project_name": "new_project", + } + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) new_task = Task.objects.all()[0] url = "/v1/tasks/" + new_task.uuid headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin", - 'username': "test", - 'user_id': "test_user_id", - 'email': "test@example.com", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin", + "username": "test", + "user_id": "test_user_id", + "email": "test@example.com", + "authenticated": True, } - response = self.client.post(url, {'approved': True}, format='json', - headers=headers) + response = self.client.post( + url, {"approved": True}, format="json", headers=headers + ) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True, 'password': '1234'} - response = self.client.post(url, data, format='json') + data = {"confirm": True, "password": "1234"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.invite_user_to_project.additional_actions": [ - {'operation': 'append', 'value': "SendAdditionalEmailAction"}, + {"operation": "append", "value": "SendAdditionalEmailAction"}, ], "adjutant.workflow.tasks.invite_user_to_project.emails": [ - {'operation': 'update', 'value': {"initial": None}}, + {"operation": "update", "value": {"initial": None}}, ], "adjutant.workflow.tasks.invite_user_to_project.actions": [ - {'operation': 'update', 'value': { - "SendAdditionalEmailAction": { - "prepare": { - 'subject': 'invite_user_to_project_additional', - 'template': 'update_user_email_started.txt', - 'email_roles': ['project_admin'], + { + "operation": "update", + "value": { + "SendAdditionalEmailAction": { + "prepare": { + "subject": "invite_user_to_project_additional", + "template": "update_user_email_started.txt", + "email_roles": ["project_admin"], + } } - } - }}, + }, + }, ], - }) + }, + ) def test_additional_emails_roles(self): """ Tests the sending of additional emails to a set of roles in a project @@ -1156,109 +1245,120 @@ class DelegateAPITests(AdjutantAPITestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) user2 = fake_clients.FakeUser( - name="test2@example.com", password="123", - email="test2@example.com") + name="test2@example.com", password="123", email="test2@example.com" + ) user3 = fake_clients.FakeUser( - name="test3@example.com", password="123", - email="test2@example.com") + name="test3@example.com", password="123", email="test2@example.com" + ) assignments = [ fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_admin", - user={'id': user.id} + user={"id": user.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user2.id} + user={"id": user2.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_admin", - user={'id': user2.id} + user={"id": user2.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user3.id} + user={"id": user3.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_mod", - user={'id': user3.id} + user={"id": user3.id}, ), ] setup_identity_cache( - projects=[project], users=[user, user2, user3], - role_assignments=assignments) + projects=[project], users=[user, user2, user3], role_assignments=assignments + ) url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "new_test@example.com", - 'roles': ['member'], 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "new_test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) self.assertEqual(len(mail.outbox), 2) self.assertEqual(len(mail.outbox[0].to), 2) - self.assertEqual(set(mail.outbox[0].to), - set([user.email, user2.email])) - self.assertEqual( - mail.outbox[0].subject, 'invite_user_to_project_additional') + self.assertEqual(set(mail.outbox[0].to), set([user.email, user2.email])) + self.assertEqual(mail.outbox[0].subject, "invite_user_to_project_additional") # Test that the token email gets sent to the other addresses - self.assertEqual(mail.outbox[1].to[0], 'new_test@example.com') + self.assertEqual(mail.outbox[1].to[0], "new_test@example.com") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True, 'password': '1234'} - response = self.client.post(url, data, format='json') + data = {"confirm": True, "password": "1234"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.invite_user_to_project.additional_actions": [ - {'operation': 'append', 'value': "SendAdditionalEmailAction"}, + {"operation": "append", "value": "SendAdditionalEmailAction"}, ], "adjutant.workflow.tasks.invite_user_to_project.emails": [ - {'operation': 'update', 'value': { - "initial": None, "token": {"subject": "invite_user_to_project_token"}}}, + { + "operation": "update", + "value": { + "initial": None, + "token": {"subject": "invite_user_to_project_token"}, + }, + }, ], "adjutant.workflow.tasks.invite_user_to_project.actions": [ - {'operation': 'update', 'value': { - "SendAdditionalEmailAction": { - "prepare": { - 'subject': 'invite_user_to_project_additional', - 'template': 'update_user_email_started.txt', - 'email_roles': ['project_admin'], + { + "operation": "update", + "value": { + "SendAdditionalEmailAction": { + "prepare": { + "subject": "invite_user_to_project_additional", + "template": "update_user_email_started.txt", + "email_roles": ["project_admin"], + } } - } - }}, + }, + }, ], - }) + }, + ) def test_additional_emails_role_no_email(self): """ Tests that setting email roles to something that has no people to @@ -1268,66 +1368,71 @@ class DelegateAPITests(AdjutantAPITestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignment = fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ) setup_identity_cache( - projects=[project], users=[user], role_assignments=[assignment]) + projects=[project], users=[user], role_assignments=[assignment] + ) url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "new_test@example.com", - 'roles': ['member']} - response = self.client.post(url, data, format='json', headers=headers) + data = {"email": "new_test@example.com", "roles": ["member"]} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.data, {'notes': ['task created']}) + self.assertEqual(response.data, {"notes": ["task created"]}) self.assertEqual(len(mail.outbox), 1) # Test that the token email gets sent to the other addresses - self.assertEqual(mail.outbox[0].to[0], 'new_test@example.com') + self.assertEqual(mail.outbox[0].to[0], "new_test@example.com") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'confirm': True, 'password': '1234'} - response = self.client.post(url, data, format='json') + data = {"confirm": True, "password": "1234"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.invite_user_to_project.additional_actions": [ - {'operation': 'append', 'value': "SendAdditionalEmailAction"}, + {"operation": "append", "value": "SendAdditionalEmailAction"}, ], "adjutant.workflow.tasks.invite_user_to_project.emails": [ - {'operation': 'update', 'value': {"initial": None}}, + {"operation": "update", "value": {"initial": None}}, ], "adjutant.workflow.tasks.invite_user_to_project.actions": [ - {'operation': 'update', 'value': { - "SendAdditionalEmailAction": { - "prepare": { - 'subject': 'invite_user_to_project_additional', - 'template': 'update_user_email_started.txt', - 'email_additional_addresses': ['admin@example.com'], + { + "operation": "update", + "value": { + "SendAdditionalEmailAction": { + "prepare": { + "subject": "invite_user_to_project_additional", + "template": "update_user_email_started.txt", + "email_additional_addresses": ["admin@example.com"], + } } - } - }}, + }, + }, ], - }) + }, + ) def test_email_additional_addresses(self): """ Tests the sending of additional emails an admin email set in @@ -1336,78 +1441,87 @@ class DelegateAPITests(AdjutantAPITestCase): project = fake_clients.FakeProject(name="test_project") user = fake_clients.FakeUser( - name="test@example.com", password="123", email="test@example.com") + name="test@example.com", password="123", email="test@example.com" + ) assignments = [ fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="member", - user={'id': user.id} + user={"id": user.id}, ), fake_clients.FakeRoleAssignment( - scope={'project': {'id': project.id}}, + scope={"project": {"id": project.id}}, role_name="project_admin", - user={'id': user.id} + user={"id": user.id}, ), ] setup_identity_cache( - projects=[project], users=[user], role_assignments=assignments) + projects=[project], users=[user], role_assignments=assignments + ) url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "new_test@example.com", 'roles': ['member']} - response = self.client.post(url, data, format='json', headers=headers) + data = {"email": "new_test@example.com", "roles": ["member"]} + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) self.assertEqual(len(mail.outbox), 2) - self.assertEqual(set(mail.outbox[0].to), - set(['admin@example.com'])) - self.assertEqual( - mail.outbox[0].subject, 'invite_user_to_project_additional') + self.assertEqual(set(mail.outbox[0].to), set(["admin@example.com"])) + self.assertEqual(mail.outbox[0].subject, "invite_user_to_project_additional") # Test that the token email gets sent to the other addresses - self.assertEqual(mail.outbox[1].to[0], 'new_test@example.com') + self.assertEqual(mail.outbox[1].to[0], "new_test@example.com") new_token = Token.objects.all()[0] url = "/v1/tokens/" + new_token.token - data = {'password': 'testpassword'} - response = self.client.post(url, data, format='json') + data = {"password": "testpassword"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.invite_user_to_project.additional_actions": [ - {'operation': 'append', 'value': "SendAdditionalEmailAction"}, + {"operation": "append", "value": "SendAdditionalEmailAction"}, ], "adjutant.workflow.tasks.invite_user_to_project.emails": [ - {'operation': 'update', 'value': { - "initial": None, "token": {"subject": "invite_user_to_project_token"}}}, + { + "operation": "update", + "value": { + "initial": None, + "token": {"subject": "invite_user_to_project_token"}, + }, + }, ], "adjutant.workflow.tasks.invite_user_to_project.actions": [ - {'operation': 'update', 'value': { - "SendAdditionalEmailAction": { - "prepare": { - 'subject': 'invite_user_to_project_additional', - 'template': 'update_user_email_started.txt', - 'email_additional_addresses': ['admin@example.com'], + { + "operation": "update", + "value": { + "SendAdditionalEmailAction": { + "prepare": { + "subject": "invite_user_to_project_additional", + "template": "update_user_email_started.txt", + "email_additional_addresses": ["admin@example.com"], + } } - } - }}, + }, + }, ], - }) + }, + ) def test_email_additional_action_invalid(self): """ The additional email actions should not send an email if the @@ -1418,21 +1532,24 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "project_admin,member,project_mod", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "project_admin,member,project_mod", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } - data = {'email': "test@example.com", 'roles': ["member"], - 'project_id': 'test_project_id'} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "email": "test@example.com", + "roles": ["member"], + "project_id": "test_project_id", + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json(), {'errors': ['actions invalid']}) + self.assertEqual(response.json(), {"errors": ["actions invalid"]}) self.assertEqual(len(mail.outbox), 0) - @mock.patch('adjutant.common.tests.fake_clients.FakeManager.find_project') + @mock.patch("adjutant.common.tests.fake_clients.FakeManager.find_project") def test_all_actions_setup(self, mocked_find): """ Ensures that all actions have been setup before prepare is @@ -1446,10 +1563,9 @@ class DelegateAPITests(AdjutantAPITestCase): mocked_find.side_effect = KeyError("Error forced for testing") url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') - self.assertEqual( - response.status_code, status.HTTP_503_SERVICE_UNAVAILABLE) + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, status.HTTP_503_SERVICE_UNAVAILABLE) new_task = Task.objects.all()[0] @@ -1461,7 +1577,7 @@ class DelegateAPITests(AdjutantAPITestCase): observed_action_names = [a.action_name for a in actions] self.assertEqual(observed_action_names, expected_action_names) - @mock.patch('adjutant.common.tests.fake_clients.FakeManager.find_project') + @mock.patch("adjutant.common.tests.fake_clients.FakeManager.find_project") def test_task_error_handler(self, mocked_find): """ Ensure the _handle_task_error function works as expected. @@ -1472,14 +1588,14 @@ class DelegateAPITests(AdjutantAPITestCase): mocked_find.side_effect = KeyError("Error forced for testing") url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') - self.assertEqual( - response.status_code, status.HTTP_503_SERVICE_UNAVAILABLE) + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, status.HTTP_503_SERVICE_UNAVAILABLE) self.assertEqual( response.json(), - {'errors': ['Service temporarily unavailable, try again later.']}) + {"errors": ["Service temporarily unavailable, try again later."]}, + ) new_task = Task.objects.all()[0] new_notification = Notification.objects.all()[0] @@ -1487,18 +1603,23 @@ class DelegateAPITests(AdjutantAPITestCase): self.assertTrue(new_notification.error) self.assertEqual( new_notification.notes, - {'errors': [ - "Error: KeyError('Error forced for testing') while setting up " - "task. See task itself for details."]}) + { + "errors": [ + "Error: KeyError('Error forced for testing') while setting up " + "task. See task itself for details." + ] + }, + ) self.assertEqual(new_notification.task, new_task) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.can_edit_users": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_user_invite_cant_edit_users(self): """ When can_edit_users is false, and a new user is invited, @@ -1511,26 +1632,31 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "user", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "user", + "user_id": "test_user_id", + "authenticated": True, } - data = {'username': 'new_user', 'email': "new@example.com", - 'roles': ["member"], 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "username": "new_user", + "email": "new@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json(), {'errors': ['actions invalid']}) + self.assertEqual(response.json(), {"errors": ["actions invalid"]}) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.can_edit_users": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_user_invite_cant_edit_users_existing_user(self): """ When can_edit_users is false, and a new user is invited, @@ -1544,26 +1670,31 @@ class DelegateAPITests(AdjutantAPITestCase): url = "/v1/actions/InviteUser" headers = { - 'project_name': "test_project", - 'project_id': project.id, - 'roles': "project_admin,member,project_mod", - 'username': "user", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": project.id, + "roles": "project_admin,member,project_mod", + "username": "user", + "user_id": "test_user_id", + "authenticated": True, } - data = {'username': 'new_user', 'email': "test@example.com", - 'roles': ["member"], 'project_id': project.id} - response = self.client.post(url, data, format='json', headers=headers) + data = { + "username": "new_user", + "email": "test@example.com", + "roles": ["member"], + "project_id": project.id, + } + response = self.client.post(url, data, format="json", headers=headers) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) @conf_utils.modify_conf( CONF, operations={ "adjutant.identity.can_edit_users": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_project_create_cant_edit_users(self): """ When can_edit_users is false, and a new signup comes in, @@ -1576,10 +1707,10 @@ class DelegateAPITests(AdjutantAPITestCase): setup_identity_cache() url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) task = Task.objects.all()[0] action_models = task.actions actions = [act.get_action() for act in action_models] @@ -1589,9 +1720,10 @@ class DelegateAPITests(AdjutantAPITestCase): CONF, operations={ "adjutant.identity.can_edit_users": [ - {'operation': 'override', 'value': False}, + {"operation": "override", "value": False}, ], - }) + }, + ) def test_project_create_cant_edit_users_existing_user(self): """ When can_edit_users is false, and a new signup comes in, @@ -1606,10 +1738,10 @@ class DelegateAPITests(AdjutantAPITestCase): setup_identity_cache(users=[user]) url = "/v1/actions/CreateProjectAndUser" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response.json(), {'notes': ['task created']}) + self.assertEqual(response.json(), {"notes": ["task created"]}) task = Task.objects.all()[0] action_models = task.actions actions = [act.get_action() for act in action_models] diff --git a/adjutant/api/v1/urls.py b/adjutant/api/v1/urls.py index eeaa585..63a5385 100644 --- a/adjutant/api/v1/urls.py +++ b/adjutant/api/v1/urls.py @@ -19,19 +19,16 @@ from adjutant import api from adjutant.config import CONF urlpatterns = [ - url(r'^status/?$', views.StatusView.as_view()), - url(r'^tasks/(?P\w+)/?$', views.TaskDetail.as_view()), - url(r'^tasks/?$', views.TaskList.as_view()), - url(r'^tokens/(?P\w+)', views.TokenDetail.as_view()), - url(r'^tokens/?$', views.TokenList.as_view()), - url(r'^notifications/(?P\w+)/?$', - views.NotificationDetail.as_view()), - url(r'^notifications/?$', views.NotificationList.as_view()), + url(r"^status/?$", views.StatusView.as_view()), + url(r"^tasks/(?P\w+)/?$", views.TaskDetail.as_view()), + url(r"^tasks/?$", views.TaskList.as_view()), + url(r"^tokens/(?P\w+)", views.TokenDetail.as_view()), + url(r"^tokens/?$", views.TokenList.as_view()), + url(r"^notifications/(?P\w+)/?$", views.NotificationDetail.as_view()), + url(r"^notifications/?$", views.NotificationList.as_view()), ] for active_view in CONF.api.active_delegate_apis: delegate_api = api.DELEGATE_API_CLASSES[active_view] - urlpatterns.append( - url(delegate_api.url, delegate_api.as_view()) - ) + urlpatterns.append(url(delegate_api.url, delegate_api.as_view())) diff --git a/adjutant/api/v1/utils.py b/adjutant/api/v1/utils.py index 1793db2..54b6814 100644 --- a/adjutant/api/v1/utils.py +++ b/adjutant/api/v1/utils.py @@ -31,7 +31,7 @@ def parse_filters(func, *args, **kwargs): BE AWARE! WILL NOT WORK UNLESS POSITIONAL ARGUMENT 3 IS FILTERS! """ request = args[1] - filters = request.query_params.get('filters', None) + filters = request.query_params.get("filters", None) if not filters: return func(*args, **kwargs) @@ -40,14 +40,16 @@ def parse_filters(func, *args, **kwargs): filters = json.loads(filters) for field, operations in filters.items(): for operation, value in operations.items(): - cleaned_filters['%s__%s' % (field, operation)] = value + cleaned_filters["%s__%s" % (field, operation)] = value except (ValueError, AttributeError): return Response( - {'errors': [ - "Filters incorrectly formatted. Required format: " - "{'filters': {'fieldname': { 'operation': 'value'}}" - ]}, - status=400 + { + "errors": [ + "Filters incorrectly formatted. Required format: " + "{'filters': {'fieldname': { 'operation': 'value'}}" + ] + }, + status=400, ) try: @@ -57,4 +59,4 @@ def parse_filters(func, *args, **kwargs): args[2] = cleaned_filters return func(*args, **kwargs) except FieldError as e: - return Response({'errors': [str(e)]}, status=400) + return Response({"errors": [str(e)]}, status=400) diff --git a/adjutant/api/v1/views.py b/adjutant/api/v1/views.py index 0b8fc08..522ef46 100644 --- a/adjutant/api/v1/views.py +++ b/adjutant/api/v1/views.py @@ -31,21 +31,21 @@ from adjutant.tasks.models import Task class V1VersionEndpoint(SingleVersionView): - version = '1.0' + version = "1.0" class APIViewWithLogger(APIView): """ APIView with a logger. """ + def __init__(self, *args, **kwargs): super(APIViewWithLogger, self).__init__(*args, **kwargs) - self.logger = getLogger('adjutant') + self.logger = getLogger("adjutant") self.task_manager = TaskManager() class StatusView(APIViewWithLogger): - @utils.admin def get(self, request, filters=None, format=None): """ @@ -56,33 +56,31 @@ class StatusView(APIViewWithLogger): Can returns None, if there are no tasks. """ - notifications = Notification.objects.filter( - error=1, - acknowledged=0 - ) + notifications = Notification.objects.filter(error=1, acknowledged=0) try: - last_created_task = Task.objects.filter( - completed=0).order_by("-created_on")[0].to_dict() + last_created_task = ( + Task.objects.filter(completed=0).order_by("-created_on")[0].to_dict() + ) except IndexError: last_created_task = None try: - last_completed_task = Task.objects.filter( - completed=1).order_by("-completed_on")[0].to_dict() + last_completed_task = ( + Task.objects.filter(completed=1).order_by("-completed_on")[0].to_dict() + ) except IndexError: last_completed_task = None status = { "error_notifications": [note.to_dict() for note in notifications], "last_created_task": last_created_task, - "last_completed_task": last_completed_task + "last_completed_task": last_completed_task, } return Response(status, status=200) class NotificationList(APIViewWithLogger): - @utils.admin @parse_filters def get(self, request, filters=None, format=None): @@ -90,33 +88,37 @@ class NotificationList(APIViewWithLogger): A list of Notification objects as dicts. """ if filters: - notifications = Notification.objects.filter( - **filters).order_by("-created_on") + notifications = Notification.objects.filter(**filters).order_by( + "-created_on" + ) else: notifications = Notification.objects.all().order_by("-created_on") - page = request.GET.get('page', 1) - notifs_per_page = request.GET.get('notifications_per_page', None) + page = request.GET.get("page", 1) + notifs_per_page = request.GET.get("notifications_per_page", None) if notifs_per_page: paginator = Paginator(notifications, notifs_per_page) try: notifications = paginator.page(page) except EmptyPage: - return Response({'errors': ['Empty page']}, status=400) + return Response({"errors": ["Empty page"]}, status=400) except PageNotAnInteger: - return Response({'errors': ['Page not an integer']}, - status=400) + return Response({"errors": ["Page not an integer"]}, status=400) note_list = [] for notification in notifications: note_list.append(notification.to_dict()) if notifs_per_page: - return Response({'notifications': note_list, - 'pages': paginator.num_pages, - 'has_more': notifications.has_next(), - 'has_prev': notifications.has_previous()}, - status=200) + return Response( + { + "notifications": note_list, + "pages": paginator.num_pages, + "has_more": notifications.has_next(), + "has_prev": notifications.has_previous(), + }, + status=200, + ) return Response({"notifications": note_list}, status=200) @@ -125,24 +127,21 @@ class NotificationList(APIViewWithLogger): """ Acknowledge notifications. """ - note_list = request.data.get('notifications', None) + note_list = request.data.get("notifications", None) if note_list and isinstance(note_list, list): notifications = Notification.objects.filter(uuid__in=note_list) for notification in notifications: notification.acknowledged = True notification.save() - return Response({'notes': ['Notifications acknowledged.']}, - status=200) + return Response({"notes": ["Notifications acknowledged."]}, status=200) else: return Response( - {'notifications': [ - "this field is required and needs to be a list." - ]}, - status=400) + {"notifications": ["this field is required and needs to be a list."]}, + status=400, + ) class NotificationDetail(APIViewWithLogger): - @utils.admin def get(self, request, uuid, format=None): """ @@ -152,9 +151,7 @@ class NotificationDetail(APIViewWithLogger): try: notification = Notification.objects.get(uuid=uuid) except Notification.DoesNotExist: - return Response( - {'errors': ['No notification with this id.']}, - status=404) + return Response({"errors": ["No notification with this id."]}, status=404) return Response(notification.to_dict()) @utils.admin @@ -165,25 +162,21 @@ class NotificationDetail(APIViewWithLogger): try: notification = Notification.objects.get(uuid=uuid) except Notification.DoesNotExist: - return Response( - {'errors': ['No notification with this id.']}, - status=404) + return Response({"errors": ["No notification with this id."]}, status=404) if notification.acknowledged: - return Response({'notes': ['Notification already acknowledged.']}, - status=200) - if request.data.get('acknowledged', False) is True: + return Response( + {"notes": ["Notification already acknowledged."]}, status=200 + ) + if request.data.get("acknowledged", False) is True: notification.acknowledged = True notification.save() - return Response({'notes': ['Notification acknowledged.']}, - status=200) + return Response({"notes": ["Notification acknowledged."]}, status=200) else: - return Response({'acknowledged': ["this field is required."]}, - status=400) + return Response({"acknowledged": ["this field is required."]}, status=400) class TaskList(APIViewWithLogger): - @utils.admin @parse_filters def get(self, request, filters=None, format=None): @@ -192,20 +185,20 @@ class TaskList(APIViewWithLogger): and their related actions. """ - page = request.GET.get('page', 1) - tasks_per_page = request.GET.get('tasks_per_page', None) + page = request.GET.get("page", 1) + tasks_per_page = request.GET.get("tasks_per_page", None) if not filters: filters = {} # TODO(adriant): better handle this bit of incode policy - if 'admin' not in request.keystone_user['roles']: + if "admin" not in request.keystone_user["roles"]: # Ignore any filters with project_id in them for field_filter in filters.keys(): if "project_id" in field_filter: filters.pop(field_filter) - filters['project_id__exact'] = request.keystone_user['project_id'] + filters["project_id__exact"] = request.keystone_user["project_id"] tasks = Task.objects.filter(**filters).order_by("-created_on") @@ -214,27 +207,30 @@ class TaskList(APIViewWithLogger): try: tasks = paginator.page(page) except EmptyPage: - return Response({'errors': ['Empty page']}, status=400) + return Response({"errors": ["Empty page"]}, status=400) except PageNotAnInteger: - return Response({'errors': ['Page not an integer']}, - status=400) + return Response({"errors": ["Page not an integer"]}, status=400) task_list = [] for task in tasks: task_list.append(task.to_dict()) if tasks_per_page: - return Response({'tasks': task_list, - 'pages': paginator.num_pages, - 'has_more': tasks.has_next(), - 'has_prev': tasks.has_previous()}, status=200) + return Response( + { + "tasks": task_list, + "pages": paginator.num_pages, + "has_more": tasks.has_next(), + "has_prev": tasks.has_previous(), + }, + status=200, + ) # NOTE(amelia): 'has_more'and 'has_prev' names are # based on the horizon pagination table pagination names else: - return Response({'tasks': task_list}) + return Response({"tasks": task_list}) class TaskDetail(APIViewWithLogger): - @utils.mod_or_admin def get(self, request, uuid, format=None): """ @@ -243,16 +239,15 @@ class TaskDetail(APIViewWithLogger): """ try: # TODO(adriant): better handle this bit of incode policy - if 'admin' in request.keystone_user['roles']: + if "admin" in request.keystone_user["roles"]: task = Task.objects.get(uuid=uuid) else: task = Task.objects.get( - uuid=uuid, project_id=request.keystone_user['project_id']) + uuid=uuid, project_id=request.keystone_user["project_id"] + ) return Response(task.to_dict()) except Task.DoesNotExist: - return Response( - {'errors': ['No task with this id.']}, - status=404) + return Response({"errors": ["No task with this id."]}, status=404) @utils.admin def put(self, request, uuid, format=None): @@ -262,9 +257,7 @@ class TaskDetail(APIViewWithLogger): """ self.task_manager.update(uuid, request.data) - return Response( - {'notes': ["Task successfully updated."]}, - status=200) + return Response({"notes": ["Task successfully updated."]}, status=200) @utils.admin def post(self, request, uuid, format=None): @@ -274,21 +267,21 @@ class TaskDetail(APIViewWithLogger): and if valid will setup and create a related token. """ try: - if request.data.get('approved') is not True: + if request.data.get("approved") is not True: raise exceptions.TaskSerializersInvalid( - {'approved': ["this is a required boolean field."]}) + {"approved": ["this is a required boolean field."]} + ) except ParseError: raise exceptions.TaskSerializersInvalid( - {'approved': ["this is a required boolean field."]}) + {"approved": ["this is a required boolean field."]} + ) task = self.task_manager.approve(uuid, request.keystone_user) if task.completed: - return Response( - {'notes': ["Task completed successfully."]}, status=200) + return Response({"notes": ["Task completed successfully."]}, status=200) else: - return Response( - {'notes': ['created token']}, status=202) + return Response({"notes": ["created token"]}, status=202) @utils.mod_or_admin def delete(self, request, uuid, format=None): @@ -300,21 +293,18 @@ class TaskDetail(APIViewWithLogger): """ try: # TODO(adriant): better handle this bit of incode policy - if 'admin' in request.keystone_user['roles']: + if "admin" in request.keystone_user["roles"]: task = Task.objects.get(uuid=uuid) else: task = Task.objects.get( - uuid=uuid, project_id=request.keystone_user['project_id']) + uuid=uuid, project_id=request.keystone_user["project_id"] + ) except Task.DoesNotExist: - return Response( - {'errors': ['No task with this id.']}, - status=404) + return Response({"errors": ["No task with this id."]}, status=404) self.task_manager.cancel(task) - return Response( - {'notes': ["Task cancelled successfully."]}, - status=200) + return Response({"notes": ["Task cancelled successfully."]}, status=200) class TokenList(APIViewWithLogger): @@ -344,26 +334,24 @@ class TokenList(APIViewWithLogger): Clears other tokens for it. """ - uuid = request.data.get('task', None) + uuid = request.data.get("task", None) if uuid is None: return Response( - {'errors': {'task': ["This field is required.", ]}}, - status=400) + {"errors": {"task": ["This field is required.",]}}, status=400 + ) try: # TODO(adriant): better handle this bit of incode policy - if 'admin' in request.keystone_user['roles']: + if "admin" in request.keystone_user["roles"]: task = Task.objects.get(uuid=uuid) else: task = Task.objects.get( - uuid=uuid, project_id=request.keystone_user['project_id']) + uuid=uuid, project_id=request.keystone_user["project_id"] + ) except Task.DoesNotExist: - return Response( - {'errors': ['No task with this id.']}, - status=404) + return Response({"errors": ["No task with this id."]}, status=404) self.task_manager.reissue_token(task) - return Response( - {'notes': ['Token reissued.']}, status=200) + return Response({"notes": ["Token reissued."]}, status=200) @utils.admin def delete(self, request, format=None): @@ -372,12 +360,10 @@ class TokenList(APIViewWithLogger): """ now = timezone.now() Token.objects.filter(expires__lt=now).delete() - return Response( - {'notes': ['Deleted all expired tokens.']}, status=200) + return Response({"notes": ["Deleted all expired tokens."]}, status=200) class TokenDetail(APIViewWithLogger): - def get(self, request, id, format=None): """ Returns a response with the list of required fields @@ -390,20 +376,16 @@ class TokenDetail(APIViewWithLogger): token = Token.objects.get(token=id) except Token.DoesNotExist: return Response( - {'errors': ['This token does not exist or has expired.']}, - status=404) + {"errors": ["This token does not exist or has expired."]}, status=404 + ) if token.task.completed: return Response( - {'errors': - ['This task has already been completed.']}, - status=400) + {"errors": ["This task has already been completed."]}, status=400 + ) if token.task.cancelled: - return Response( - {'errors': - ['This task has been cancelled.']}, - status=400) + return Response({"errors": ["This task has been cancelled."]}, status=400) required_fields = [] actions = [] @@ -415,9 +397,13 @@ class TokenDetail(APIViewWithLogger): if field not in required_fields: required_fields.append(field) - return Response({'actions': [str(act) for act in actions], - 'required_fields': required_fields, - 'task_type': token.task.task_type}) + return Response( + { + "actions": [str(act) for act in actions], + "required_fields": required_fields, + "task_type": token.task.task_type, + } + ) def post(self, request, id, format=None): """ @@ -432,11 +418,9 @@ class TokenDetail(APIViewWithLogger): token = Token.objects.get(token=id) except Token.DoesNotExist: return Response( - {'errors': ['This token does not exist or has expired.']}, - status=404) + {"errors": ["This token does not exist or has expired."]}, status=404 + ) self.task_manager.submit(token.task, request.data) - return Response( - {'notes': ["Token submitted successfully."]}, - status=200) + return Response({"notes": ["Token submitted successfully."]}, status=200) diff --git a/adjutant/api/views.py b/adjutant/api/views.py index b154a88..84ff618 100644 --- a/adjutant/api/views.py +++ b/adjutant/api/views.py @@ -14,38 +14,31 @@ def build_version_details(id, status, links=None, relative_endpoint=None): relative_endpoint = "v%s/" % int_id mime_type = "application/vnd.openstack.adjutant-v%s+json" % int_id version_details = { - 'status': status, - 'id': id, - 'media-types': [ - { - 'base': 'application/json', - 'type': mime_type - } - ], - 'links': [] + "status": status, + "id": id, + "media-types": [{"base": "application/json", "type": mime_type}], + "links": [], } if links: - version_details['links'] = links + version_details["links"] = links - version_details['relative_endpoint'] = relative_endpoint + version_details["relative_endpoint"] = relative_endpoint _VERSIONS[id] = version_details return version_details class VersionView(APIView): - def get(self, request): versions = [] for version in _VERSIONS.values(): version = version.copy() - rel_endpoint = version.pop('relative_endpoint') + rel_endpoint = version.pop("relative_endpoint") url = request.build_absolute_uri() + rel_endpoint - version['links'] = version['links'] + [{'href': url, - 'rel': 'self'}] + version["links"] = version["links"] + [{"href": url, "rel": "self"}] versions.append(version) - return Response({'versions': versions}, status=200) + return Response({"versions": versions}, status=200) class SingleVersionView(APIView): @@ -58,11 +51,11 @@ class SingleVersionView(APIView): version = _VERSIONS.get(self.version, {}).copy() if not version: - return Response({'error': 'Not Found'}, status=404) + return Response({"error": "Not Found"}, status=404) - version.pop('relative_endpoint') + version.pop("relative_endpoint") - version['links'] = version['links'] + [ - {'href': request.build_absolute_uri(), - 'rel': 'self'}] - return Response({'version': version}, status=200) + version["links"] = version["links"] + [ + {"href": request.build_absolute_uri(), "rel": "self"} + ] + return Response({"version": version}, status=200) diff --git a/adjutant/commands/management/commands/exampleconfig.py b/adjutant/commands/management/commands/exampleconfig.py index c0fc767..b52203d 100644 --- a/adjutant/commands/management/commands/exampleconfig.py +++ b/adjutant/commands/management/commands/exampleconfig.py @@ -10,8 +10,8 @@ from adjutant import config def make_yaml_lines(val, depth, comment=False): new_lines = [] line_prefix = " " * (depth + 1) - for line in yaml.dump(val).split('\n'): - if line == '': + for line in yaml.dump(val).split("\n"): + if line == "": continue if comment: new_lines.append(line_prefix + "# %s" % line) @@ -28,7 +28,7 @@ def make_field_lines(field, depth): field_help_text = "# %s" % field.help_text field_lines.append(line_prefix + field_help_text) - default = '' + default = "" if field.default is not None: default = field.default @@ -48,7 +48,7 @@ def make_field_lines(field, depth): else: field_lines.append(line_prefix + "# %s:" % field.name) else: - if default == '': + if default == "": field_lines.append(line_prefix + "# %s: " % field.name) else: default_str = " " + str(default) @@ -70,20 +70,20 @@ def make_group_lines(group, depth=0): class Command(BaseCommand): - help = '' + help = "" def add_arguments(self, parser): - parser.add_argument('--output-file', default="adjutant.yaml") + parser.add_argument("--output-file", default="adjutant.yaml") def handle(self, *args, **options): - print("Generating example file to: '%s'" % options['output_file']) + print("Generating example file to: '%s'" % options["output_file"]) base_lines = [] for group in config._root_config: base_lines += make_group_lines(group) base_lines.append("") - with open(options['output_file'], "w") as f: + with open(options["output_file"], "w") as f: for line in base_lines: f.write(line) f.write("\n") diff --git a/adjutant/common/openstack_clients.py b/adjutant/common/openstack_clients.py index 853daae..8a30cbc 100644 --- a/adjutant/common/openstack_clients.py +++ b/adjutant/common/openstack_clients.py @@ -56,38 +56,25 @@ def get_auth_session(): def get_keystoneclient(version=DEFAULT_IDENTITY_VERSION): - return ks_client.Client( - version, - session=get_auth_session()) + return ks_client.Client(version, session=get_auth_session()) def get_neutronclient(region): # always returns neutron client v2 - return neutronclient.Client( - session=get_auth_session(), - region_name=region) + return neutronclient.Client(session=get_auth_session(), region_name=region) def get_novaclient(region, version=DEFAULT_COMPUTE_VERSION): - return novaclient.Client( - version, - session=get_auth_session(), - region_name=region) + return novaclient.Client(version, session=get_auth_session(), region_name=region) def get_cinderclient(region, version=DEFAULT_VOLUME_VERSION): - return cinderclient.Client( - version, - session=get_auth_session(), - region_name=region) + return cinderclient.Client(version, session=get_auth_session(), region_name=region) def get_octaviaclient(region): ks = get_keystoneclient() - service = ks.services.list(name='octavia')[0] - endpoint = ks.endpoints.list(service=service, - region=region, interface='public')[0] - return octavia.OctaviaAPI( - session=get_auth_session(), - endpoint=endpoint.url) + service = ks.services.list(name="octavia")[0] + endpoint = ks.endpoints.list(service=service, region=region, interface="public")[0] + return octavia.OctaviaAPI(session=get_auth_session(), endpoint=endpoint.url) diff --git a/adjutant/common/quota.py b/adjutant/common/quota.py index cca6ed3..ed0d2cd 100644 --- a/adjutant/common/quota.py +++ b/adjutant/common/quota.py @@ -22,7 +22,7 @@ class QuotaManager(object): across all services. """ - default_size_diff_threshold = .2 + default_size_diff_threshold = 0.2 class ServiceQuotaHelper(object): def set_quota(self, values): @@ -30,8 +30,7 @@ class QuotaManager(object): class ServiceQuotaCinderHelper(ServiceQuotaHelper): def __init__(self, region_name, project_id): - self.client = openstack_clients.get_cinderclient( - region=region_name) + self.client = openstack_clients.get_cinderclient(region=region_name) self.project_id = project_id def get_quota(self): @@ -39,39 +38,40 @@ class QuotaManager(object): def get_usage(self): volumes = self.client.volumes.list( - search_opts={'all_tenants': 1, 'project_id': self.project_id}) + search_opts={"all_tenants": 1, "project_id": self.project_id} + ) snapshots = self.client.volume_snapshots.list( - search_opts={'all_tenants': 1, 'project_id': self.project_id}) + search_opts={"all_tenants": 1, "project_id": self.project_id} + ) # gigabytesUsed should be a total of volumes and snapshots - gigabytes = sum([getattr(volume, 'size', 0) for volume - in volumes]) - gigabytes += sum([getattr(snap, 'size', 0) for snap - in snapshots]) + gigabytes = sum([getattr(volume, "size", 0) for volume in volumes]) + gigabytes += sum([getattr(snap, "size", 0) for snap in snapshots]) - return {'gigabytes': gigabytes, - 'volumes': len(volumes), - 'snapshots': len(snapshots) - } + return { + "gigabytes": gigabytes, + "volumes": len(volumes), + "snapshots": len(snapshots), + } class ServiceQuotaNovaHelper(ServiceQuotaHelper): def __init__(self, region_name, project_id): - self.client = openstack_clients.get_novaclient( - region=region_name) + self.client = openstack_clients.get_novaclient(region=region_name) self.project_id = project_id def get_quota(self): return self.client.quotas.get(self.project_id).to_dict() def get_usage(self): - nova_usage = self.client.limits.get( - tenant_id=self.project_id).to_dict()['absolute'] + nova_usage = self.client.limits.get(tenant_id=self.project_id).to_dict()[ + "absolute" + ] nova_usage_keys = [ - ('instances', 'totalInstancesUsed'), - ('floating_ips', 'totalFloatingIpsUsed'), - ('ram', 'totalRAMUsed'), - ('cores', 'totalCoresUsed'), - ('secuirty_groups', 'totalSecurityGroupsUsed') + ("instances", "totalInstancesUsed"), + ("floating_ips", "totalFloatingIpsUsed"), + ("ram", "totalRAMUsed"), + ("cores", "totalCoresUsed"), + ("secuirty_groups", "totalSecurityGroupsUsed"), ] nova_usage_dict = {} @@ -82,53 +82,48 @@ class QuotaManager(object): class ServiceQuotaNeutronHelper(ServiceQuotaHelper): def __init__(self, region_name, project_id): - self.client = openstack_clients.get_neutronclient( - region=region_name) + self.client = openstack_clients.get_neutronclient(region=region_name) self.project_id = project_id def set_quota(self, values): - body = { - 'quota': values - } + body = {"quota": values} self.client.update_quota(self.project_id, body) def get_usage(self): - networks = self.client.list_networks( - tenant_id=self.project_id)['networks'] - routers = self.client.list_routers( - tenant_id=self.project_id)['routers'] - floatingips = self.client.list_floatingips( - tenant_id=self.project_id)['floatingips'] - ports = self.client.list_ports( - tenant_id=self.project_id)['ports'] - subnets = self.client.list_subnets( - tenant_id=self.project_id)['subnets'] + networks = self.client.list_networks(tenant_id=self.project_id)["networks"] + routers = self.client.list_routers(tenant_id=self.project_id)["routers"] + floatingips = self.client.list_floatingips(tenant_id=self.project_id)[ + "floatingips" + ] + ports = self.client.list_ports(tenant_id=self.project_id)["ports"] + subnets = self.client.list_subnets(tenant_id=self.project_id)["subnets"] security_groups = self.client.list_security_groups( - tenant_id=self.project_id)['security_groups'] + tenant_id=self.project_id + )["security_groups"] security_group_rules = self.client.list_security_group_rules( - tenant_id=self.project_id)['security_group_rules'] + tenant_id=self.project_id + )["security_group_rules"] - return {'network': len(networks), - 'router': len(routers), - 'floatingip': len(floatingips), - 'port': len(ports), - 'subnet': len(subnets), - 'secuirty_group': len(security_groups), - 'security_group_rule': len(security_group_rules) - } + return { + "network": len(networks), + "router": len(routers), + "floatingip": len(floatingips), + "port": len(ports), + "subnet": len(subnets), + "secuirty_group": len(security_groups), + "security_group_rule": len(security_group_rules), + } def get_quota(self): - return self.client.show_quota(self.project_id)['quota'] + return self.client.show_quota(self.project_id)["quota"] class ServiceQuotaOctaviaHelper(ServiceQuotaNeutronHelper): def __init__(self, region_name, project_id): - self.client = openstack_clients.get_octaviaclient( - region=region_name) + self.client = openstack_clients.get_octaviaclient(region=region_name) self.project_id = project_id def get_quota(self): - project_quota = self.client.quota_show( - project_id=self.project_id) + project_quota = self.client.quota_show(project_id=self.project_id) # NOTE(amelia): Instead of returning the default quota if ANY # of the quotas are the default, the endpoint @@ -137,40 +132,45 @@ class QuotaManager(object): for name, quota in project_quota.items(): if quota is None: if not default_quota: - default_quota = self.client.quota_defaults_show()[ - 'quota'] + default_quota = self.client.quota_defaults_show()["quota"] project_quota[name] = default_quota[name] return project_quota def set_quota(self, values): - self.client.quota_set(self.project_id, json={'quota': values}) + self.client.quota_set(self.project_id, json={"quota": values}) def get_usage(self): usage = {} - usage['load_balancer'] = len(self.client.load_balancer_list( - project_id=self.project_id)['loadbalancers']) - usage['listener'] = len(self.client.listener_list( - project_id=self.project_id)['listeners']) + usage["load_balancer"] = len( + self.client.load_balancer_list(project_id=self.project_id)[ + "loadbalancers" + ] + ) + usage["listener"] = len( + self.client.listener_list(project_id=self.project_id)["listeners"] + ) - pools = self.client.pool_list( - project_id=self.project_id)['pools'] - usage['pool'] = len(pools) + pools = self.client.pool_list(project_id=self.project_id)["pools"] + usage["pool"] = len(pools) members = [] for pool in pools: - members += pool['members'] + members += pool["members"] - usage['member'] = len(members) - usage['health_monitor'] = len(self.client.health_monitor_list( - project_id=self.project_id)['healthmonitors']) + usage["member"] = len(members) + usage["health_monitor"] = len( + self.client.health_monitor_list(project_id=self.project_id)[ + "healthmonitors" + ] + ) return usage _quota_updaters = { - 'cinder': ServiceQuotaCinderHelper, - 'nova': ServiceQuotaNovaHelper, - 'neutron': ServiceQuotaNeutronHelper, - 'octavia': ServiceQuotaOctaviaHelper, + "cinder": ServiceQuotaCinderHelper, + "nova": ServiceQuotaNovaHelper, + "neutron": ServiceQuotaNeutronHelper, + "octavia": ServiceQuotaOctaviaHelper, } def __init__(self, project_id, size_difference_threshold=None): @@ -182,24 +182,23 @@ class QuotaManager(object): quota_services = dict(CONF.quota.services) - all_regions = quota_services.pop('*', None) + all_regions = quota_services.pop("*", None) if all_regions: self.default_helpers = {} for service in all_regions: if service in self._quota_updaters: - self.default_helpers[service] = \ - self._quota_updaters[service] + self.default_helpers[service] = self._quota_updaters[service] for region, services in quota_services.items(): self.helpers[region] = {} for service in services: if service in self._quota_updaters: - self.helpers[region][service] = \ - self._quota_updaters[service] + self.helpers[region][service] = self._quota_updaters[service] self.project_id = project_id - self.size_diff_threshold = (size_difference_threshold - or self.default_size_diff_threshold) + self.size_diff_threshold = ( + size_difference_threshold or self.default_size_diff_threshold + ) def get_current_region_quota(self, region_id): current_quota = {} @@ -239,7 +238,8 @@ class QuotaManager(object): match_percentages.append(0.0) # Calculate the average of how much it matches the setting difference = abs( - (sum(match_percentages) / float(len(match_percentages))) - 1) + (sum(match_percentages) / float(len(match_percentages))) - 1 + ) quota_differences[size] = difference @@ -253,15 +253,14 @@ class QuotaManager(object): quota_differences_pruned = {} for size, difference in quota_differences.items(): - if (difference <= diff_threshold): + if difference <= diff_threshold: quota_differences_pruned[size] = difference if len(quota_differences_pruned) > 0: - return min( - quota_differences_pruned, key=quota_differences_pruned.get) + return min(quota_differences_pruned, key=quota_differences_pruned.get) # If we don't get a match return custom which means the project will # need admin approval for any change - return 'custom' + return "custom" def get_quota_change_options(self, quota_size): """ Get's the pre-approved quota change options for a given size """ @@ -294,14 +293,14 @@ class QuotaManager(object): change_options = self.get_quota_change_options(current_quota_size) region_data = { - 'region': region_id, + "region": region_id, "current_quota": current_quota, "current_quota_size": current_quota_size, "quota_change_options": change_options, } if include_usage: - region_data['current_usage'] = self.get_current_usage(region_id) + region_data["current_usage"] = self.get_current_usage(region_id) return region_data @@ -317,11 +316,11 @@ class QuotaManager(object): def set_region_quota(self, region_id, quota_dict): notes = [] for service_name, values in quota_dict.items(): - updater_class = self.helpers.get( - region_id, self.default_helpers).get(service_name) + updater_class = self.helpers.get(region_id, self.default_helpers).get( + service_name + ) if not updater_class: - notes.append("No quota updater found for %s. Ignoring" % - service_name) + notes.append("No quota updater found for %s. Ignoring" % service_name) continue service_helper = updater_class(region_id, self.project_id) diff --git a/adjutant/common/tests/fake_clients.py b/adjutant/common/tests/fake_clients.py index 190e0f3..af279cb 100644 --- a/adjutant/common/tests/fake_clients.py +++ b/adjutant/common/tests/fake_clients.py @@ -27,10 +27,16 @@ octavia_cache = {} class FakeProject(object): - - def __init__(self, name, description="", - domain_id='default', parent_id=None, - enabled=True, is_domain=False, **kwargs): + def __init__( + self, + name, + description="", + domain_id="default", + parent_id=None, + enabled=True, + is_domain=False, + **kwargs, + ): self.id = uuid4().hex self.name = name self.description = description @@ -45,10 +51,15 @@ class FakeProject(object): class FakeUser(object): - - def __init__(self, name, password="123", domain_id='default', - enabled=True, default_project_id=None, - **kwargs): + def __init__( + self, + name, + password="123", + domain_id="default", + enabled=True, + default_project_id=None, + **kwargs, + ): self.id = uuid4().hex self.name = name self.password = password @@ -62,14 +73,12 @@ class FakeUser(object): class FakeRole(object): - def __init__(self, name): self.id = uuid4().hex self.name = name class FakeCredential(object): - def __init__(self, user_id, cred_type, blob, project_id=None): self.id = uuid4().hex self.user_id = user_id @@ -79,27 +88,28 @@ class FakeCredential(object): class FakeRoleAssignment(object): - - def __init__(self, scope, role=None, role_name=None, user=None, - group=None, inherited=False): + def __init__( + self, scope, role=None, role_name=None, user=None, group=None, inherited=False + ): if role: self.role = role elif role_name: - self.role = {'name': role_name} + self.role = {"name": role_name} else: raise AttributeError("must supply 'role' or 'role_name'.") self.scope = scope self.user = user self.group = group if inherited: - self.scope['OS-INHERIT:inherited_to'] = "projects" + self.scope["OS-INHERIT:inherited_to"] = "projects" def __eq__(self, other): return self.__dict__ == other.__dict__ -def setup_identity_cache(projects=None, users=None, role_assignments=None, - credentials=None, extra_roles=None): +def setup_identity_cache( + projects=None, users=None, role_assignments=None, credentials=None, extra_roles=None +): if extra_roles is None: extra_roles = [] if not projects: @@ -111,15 +121,17 @@ def setup_identity_cache(projects=None, users=None, role_assignments=None, if not credentials: credentials = [] - default_domain = FakeProject( - name="Default", is_domain=True) - default_domain.id = 'default' + default_domain = FakeProject(name="Default", is_domain=True) + default_domain.id = "default" projects.append(default_domain) admin_user = FakeUser( - name="admin", password="password", email="admin@example.com", - domain_id=default_domain.id) + name="admin", + password="password", + email="admin@example.com", + domain_id=default_domain.id, + ) users.append(admin_user) @@ -132,34 +144,28 @@ def setup_identity_cache(projects=None, users=None, role_assignments=None, ] + extra_roles region_one = mock.Mock() - region_one.id = 'RegionOne' + region_one.id = "RegionOne" region_two = mock.Mock() - region_two.id = 'RegionTwo' + region_two.id = "RegionTwo" global identity_cache identity_cache = { - 'users': {u.id: u for u in users}, - 'new_users': [], - 'projects': {p.id: p for p in projects}, - 'new_projects': [], - 'role_assignments': role_assignments, - 'new_role_assignments': [], - 'roles': {r.id: r for r in roles}, - 'regions': { - 'RegionOne': region_one, - 'RegionTwo': region_two - }, - 'domains': { - default_domain.id: default_domain, - }, - 'credentials': credentials, + "users": {u.id: u for u in users}, + "new_users": [], + "projects": {p.id: p for p in projects}, + "new_projects": [], + "role_assignments": role_assignments, + "new_role_assignments": [], + "roles": {r.id: r for r in roles}, + "regions": {"RegionOne": region_one, "RegionTwo": region_two}, + "domains": {default_domain.id: default_domain,}, + "credentials": credentials, } class FakeManager(object): - def __init__(self): # TODO(adriant): decide if we want to have some function calls # throw errors if this is false. @@ -192,34 +198,33 @@ class FakeManager(object): def find_user(self, name, domain): domain = self._domain_from_id(domain) global identity_cache - for user in identity_cache['users'].values(): - if (user.name.lower() == name.lower() - and user.domain_id == domain.id): + for user in identity_cache["users"].values(): + if user.name.lower() == name.lower() and user.domain_id == domain.id: return user return None def get_user(self, user_id): global identity_cache - return identity_cache['users'].get(user_id, None) + return identity_cache["users"].get(user_id, None) def list_users(self, project): project = self._project_from_id(project) global identity_cache users = {} - for assignment in identity_cache['role_assignments']: - if assignment.scope['project']['id'] == project.id: + for assignment in identity_cache["role_assignments"]: + if assignment.scope["project"]["id"] == project.id: - user = users.get(assignment.user['id']) + user = users.get(assignment.user["id"]) if not user: - user = self.get_user(assignment.user['id']) + user = self.get_user(assignment.user["id"]) user.roles = [] user.inherited_roles = [] users[user.id] = user - r = self.find_role(assignment.role['name']) + r = self.find_role(assignment.role["name"]) - if assignment.scope.get('OS-INHERIT:inherited_to'): + if assignment.scope.get("OS-INHERIT:inherited_to"): user.inherited_roles.append(r) else: user.roles.append(r) @@ -233,34 +238,39 @@ class FakeManager(object): while project.parent_id: project = self._project_from_id(project.parent_id) - for assignment in identity_cache['role_assignments']: - if assignment.scope['project']['id'] == project.id: - if not assignment.scope.get('OS-INHERIT:inherited_to'): + for assignment in identity_cache["role_assignments"]: + if assignment.scope["project"]["id"] == project.id: + if not assignment.scope.get("OS-INHERIT:inherited_to"): continue - user = users.get(assignment.user['id']) + user = users.get(assignment.user["id"]) if not user: - user = self.get_user(assignment.user['id']) + user = self.get_user(assignment.user["id"]) user.roles = [] user.inherited_roles = [] users[user.id] = user - r = self.find_role(assignment.role['name']) + r = self.find_role(assignment.role["name"]) user.roles.append(r) return users.values() - def create_user(self, name, password, email, created_on, - domain='default', default_project=None): + def create_user( + self, name, password, email, created_on, domain="default", default_project=None + ): domain = self._domain_from_id(domain) default_project = self._project_from_id(default_project) global identity_cache user = FakeUser( - name=name, password=password, email=email, - domain_id=domain.id, default_project=default_project) - identity_cache['users'][user.id] = user - identity_cache['new_users'].append(user) + name=name, + password=password, + email=email, + domain_id=domain.id, + default_project=default_project, + ) + identity_cache["users"][user.id] = user + identity_cache["new_users"].append(user) return user def update_user_password(self, user, password): @@ -285,7 +295,7 @@ class FakeManager(object): def find_role(self, name): global identity_cache - for role in identity_cache['roles'].values(): + for role in identity_cache["roles"].values(): if role.name == name: return role return None @@ -297,17 +307,20 @@ class FakeManager(object): roles = [] - for assignment in identity_cache['role_assignments']: - if (assignment.user['id'] == user.id - and assignment.scope['project']['id'] == project.id): + for assignment in identity_cache["role_assignments"]: + if ( + assignment.user["id"] == user.id + and assignment.scope["project"]["id"] == project.id + ): - if (assignment.scope.get('OS-INHERIT:inherited_to') and not - inherited) or ( - inherited and not - assignment.scope.get('OS-INHERIT:inherited_to')): + if ( + assignment.scope.get("OS-INHERIT:inherited_to") and not inherited + ) or ( + inherited and not assignment.scope.get("OS-INHERIT:inherited_to") + ): continue - r = self.find_role(assignment.role['name']) + r = self.find_role(assignment.role["name"]) roles.append(r) return roles @@ -319,25 +332,21 @@ class FakeManager(object): user = self._user_from_id(user) global identity_cache projects = {} - for assignment in identity_cache['role_assignments']: - if assignment.user['id'] == user.id: - r = self.find_role(assignment.role['name']) + for assignment in identity_cache["role_assignments"]: + if assignment.user["id"] == user.id: + r = self.find_role(assignment.role["name"]) try: - projects[assignment.scope['project']['id']].append(r) + projects[assignment.scope["project"]["id"]].append(r) except KeyError: - projects[assignment.scope['project']['id']] = [r] + projects[assignment.scope["project"]["id"]] = [r] return projects def _make_role_assignment(self, user, role, project, inherited=False): - scope = { - 'project': { - 'id': project.id}} + scope = {"project": {"id": project.id}} if inherited: - scope['OS-INHERIT:inherited_to'] = "projects" + scope["OS-INHERIT:inherited_to"] = "projects" role_assignment = FakeRoleAssignment( - scope=scope, - role={"name": role.name}, - user={'id': user.id}, + scope=scope, role={"name": role.name}, user={"id": user.id}, ) return role_assignment @@ -350,45 +359,51 @@ class FakeManager(object): global identity_cache - if role_assignment not in identity_cache['role_assignments']: - identity_cache['role_assignments'].append(role_assignment) - identity_cache['new_role_assignments'].append(role_assignment) + if role_assignment not in identity_cache["role_assignments"]: + identity_cache["role_assignments"].append(role_assignment) + identity_cache["new_role_assignments"].append(role_assignment) def remove_user_role(self, user, role, project, inherited=False): user = self._user_from_id(user) role = self._role_from_id(role) project = self._project_from_id(project) - role_assignment = self._make_role_assignment(user, role, project, - inherited=inherited) + role_assignment = self._make_role_assignment( + user, role, project, inherited=inherited + ) global identity_cache - if role_assignment in identity_cache['role_assignments']: - identity_cache['role_assignments'].remove(role_assignment) + if role_assignment in identity_cache["role_assignments"]: + identity_cache["role_assignments"].remove(role_assignment) def find_project(self, project_name, domain): domain = self._domain_from_id(domain) global identity_cache - for project in identity_cache['projects'].values(): - if (project.name.lower() == project_name.lower() - and project.domain_id == domain.id): + for project in identity_cache["projects"].values(): + if ( + project.name.lower() == project_name.lower() + and project.domain_id == domain.id + ): return project return None - def get_project(self, project_id, subtree_as_ids=False, - parents_as_ids=False): + def get_project(self, project_id, subtree_as_ids=False, parents_as_ids=False): global identity_cache - project = identity_cache['projects'].get(project_id, None) + project = identity_cache["projects"].get(project_id, None) if subtree_as_ids: subtree_list = [] - prev_layer = [project.id, ] + prev_layer = [ + project.id, + ] current_layer = True while current_layer: - current_layer = [s_project.id for s_project in - identity_cache['projects'].values() - if project.parent_id in prev_layer] + current_layer = [ + s_project.id + for s_project in identity_cache["projects"].values() + if project.parent_id in prev_layer + ] prev_layer = current_layer subtree_list.append(current_layer) @@ -398,8 +413,8 @@ class FakeManager(object): parent_list = [] parent_id = project.parent_id parent_list.append(parent_id) - while identity_cache['projects'].get(parent_id, None): - parent_id = identity_cache['projects'].get(parent_id, None) + while identity_cache["projects"].get(parent_id, None): + parent_id = identity_cache["projects"].get(parent_id, None) parent_list.append(parent_id) project.parent_ids = parent_list @@ -408,20 +423,23 @@ class FakeManager(object): return project - def create_project(self, project_name, created_on, parent=None, - domain='default', description=""): + def create_project( + self, project_name, created_on, parent=None, domain="default", description="" + ): parent = self._project_from_id(parent) domain = self._domain_from_id(domain) global identity_cache project = FakeProject( - name=project_name, created_on=created_on, description=description, - domain_id=domain.id + name=project_name, + created_on=created_on, + description=description, + domain_id=domain.id, ) if parent: project.parent_id = parent.id - identity_cache['projects'][project.id] = project - identity_cache['new_projects'].append(project) + identity_cache["projects"][project.id] = project + identity_cache["new_projects"].append(project) return project def update_project(self, project, **kwargs): @@ -433,27 +451,27 @@ class FakeManager(object): def find_domain(self, domain_name): global identity_cache - for domain in identity_cache['domains'].values(): + for domain in identity_cache["domains"].values(): if domain.name.lower() == domain_name.lower(): return domain return None def get_domain(self, domain_id): global identity_cache - return identity_cache['domains'].get(domain_id, None) + return identity_cache["domains"].get(domain_id, None) def get_region(self, region_id): global identity_cache - return identity_cache['regions'].get(region_id, None) + return identity_cache["regions"].get(region_id, None) def list_regions(self): global identity_cache - return identity_cache['regions'].values() + return identity_cache["regions"].values() def list_credentials(self, user_id, cred_type=None): global identity_cache found = [] - for cred in identity_cache['credentials']: + for cred in identity_cache["credentials"]: if cred.user_id == user_id: if cred_type and cred.type == cred_type: found.append(cred) @@ -465,21 +483,20 @@ class FakeManager(object): global identity_cache user = self._user_from_id(user) project = self._project_from_id(project) - cred = FakeCredential( - user_id=user.id, blob=blob, cred_type=cred_type) + cred = FakeCredential(user_id=user.id, blob=blob, cred_type=cred_type) if project: cred.project_id = project.id - identity_cache['credentials'].append(cred) + identity_cache["credentials"].append(cred) return cred def clear_credential_type(self, user_id, cred_type): global identity_cache found = [] - for cred in identity_cache['credentials']: + for cred in identity_cache["credentials"]: if cred.user_id == user_id and cred.type == cred_type: found.append(cred) for cred in found: - identity_cache['credentials'].remove(cred) + identity_cache["credentials"].remove(cred) # TODO(adriant): Move this to a BaseIdentityManager class when # it exists. @@ -499,9 +516,12 @@ class FakeManager(object): return list(set(all_roles)) # merge mapping lists to form a flat permitted roles list - manageable_role_names = [mrole for role_name in user_roles - if role_name in roles_mapping - for mrole in roles_mapping[role_name]] + manageable_role_names = [ + mrole + for role_name in user_roles + if role_name in roles_mapping + for mrole in roles_mapping[role_name] + ] # a set has unique items manageable_role_names = set(manageable_role_names) return manageable_role_names @@ -510,6 +530,7 @@ class FakeManager(object): class FakeOpenstackClient(object): class Quotas(object): """ Stub class for testing quotas """ + def __init__(self, service): self.service = service @@ -518,7 +539,8 @@ class FakeOpenstackClient(object): def get(self, project_id): return self.QuotaSet( - self.service._cache[self.service.region][project_id]['quota']) + self.service._cache[self.service.region][project_id]["quota"] + ) class QuotaSet(object): def __init__(self, data): @@ -536,58 +558,66 @@ class FakeOpenstackClient(object): if self.region not in self._cache: self._cache[self.region] = {} if project_id not in self._cache[self.region]: - self._cache[self.region][project_id] = { - 'quota': {} - } - quota = self._cache[self.region][project_id]['quota'] + self._cache[self.region][project_id] = {"quota": {}} + quota = self._cache[self.region][project_id]["quota"] quota.update(kwargs) class FakeNeutronClient(object): - def __init__(self, region): self.region = region def create_network(self, body): global neutron_cache - project_id = body['network']['tenant_id'] - net = {'network': {'id': 'net_id_%s' % neutron_cache['RegionOne']['i'], - 'body': body}} - net_id = net['network']['id'] - neutron_cache['RegionOne'][project_id]['networks'][net_id] = net - neutron_cache['RegionOne']['i'] += 1 + project_id = body["network"]["tenant_id"] + net = { + "network": { + "id": "net_id_%s" % neutron_cache["RegionOne"]["i"], + "body": body, + } + } + net_id = net["network"]["id"] + neutron_cache["RegionOne"][project_id]["networks"][net_id] = net + neutron_cache["RegionOne"]["i"] += 1 return net def create_subnet(self, body): global neutron_cache - project_id = body['subnet']['tenant_id'] - subnet = {'subnet': {'id': 'subnet_id_%s' - % neutron_cache['RegionOne']['i'], - 'body': body}} - sub_id = subnet['subnet']['id'] - neutron_cache['RegionOne'][project_id]['subnets'][sub_id] = subnet - neutron_cache['RegionOne']['i'] += 1 + project_id = body["subnet"]["tenant_id"] + subnet = { + "subnet": { + "id": "subnet_id_%s" % neutron_cache["RegionOne"]["i"], + "body": body, + } + } + sub_id = subnet["subnet"]["id"] + neutron_cache["RegionOne"][project_id]["subnets"][sub_id] = subnet + neutron_cache["RegionOne"]["i"] += 1 return subnet def create_router(self, body): global neutron_cache - project_id = body['router']['tenant_id'] - router = {'router': {'id': 'router_id_%s' - % neutron_cache['RegionOne']['i'], - 'body': body}} - router_id = router['router']['id'] - neutron_cache['RegionOne'][project_id]['routers'][router_id] = router - neutron_cache['RegionOne']['i'] += 1 + project_id = body["router"]["tenant_id"] + router = { + "router": { + "id": "router_id_%s" % neutron_cache["RegionOne"]["i"], + "body": body, + } + } + router_id = router["router"]["id"] + neutron_cache["RegionOne"][project_id]["routers"][router_id] = router + neutron_cache["RegionOne"]["i"] += 1 return router def add_interface_router(self, router_id, body): global neutron_cache - port_id = "port_id_%s" % neutron_cache['RegionOne']['i'] - neutron_cache['RegionOne']['i'] += 1 + port_id = "port_id_%s" % neutron_cache["RegionOne"]["i"] + neutron_cache["RegionOne"]["i"] += 1 interface = { - 'port_id': port_id, - 'id': router_id, - 'subnet_id': body['subnet_id']} + "port_id": port_id, + "id": router_id, + "subnet_id": body["subnet_id"], + } return interface def update_quota(self, project_id, body): @@ -597,14 +627,14 @@ class FakeNeutronClient(object): if project_id not in neutron_cache[self.region]: neutron_cache[self.region][project_id] = {} - if 'quota' not in neutron_cache[self.region][project_id]: - neutron_cache[self.region][project_id]['quota'] = {} + if "quota" not in neutron_cache[self.region][project_id]: + neutron_cache[self.region][project_id]["quota"] = {} - quota = neutron_cache[self.region][project_id]['quota'] - quota.update(body['quota']) + quota = neutron_cache[self.region][project_id]["quota"] + quota.update(body["quota"]) def show_quota(self, project_id): - return {"quota": neutron_cache[self.region][project_id]['quota']} + return {"quota": neutron_cache[self.region][project_id]["quota"]} def list_networks(self, tenant_id): return neutron_cache[self.region][tenant_id] @@ -630,11 +660,13 @@ class FakeNeutronClient(object): class FakeOctaviaClient(object): # {name in client call: name in response} - resource_dict = {'load_balancer': 'loadbalancers', - 'listener': 'listeners', - 'member': 'members', - 'pool': 'pools', - 'health_monitor': 'healthmonitors'} + resource_dict = { + "load_balancer": "loadbalancers", + "listener": "listeners", + "member": "members", + "pool": "pools", + "health_monitor": "healthmonitors", + } # NOTE(amelia): Using the current octavia client we will get back # dicts for everything, rather than the resources the @@ -651,15 +683,15 @@ class FakeOctaviaClient(object): def quota_show(self, project_id): self._ensure_project_exists(project_id) - quota = self.cache.get(project_id, {}).get('quota', []) + quota = self.cache.get(project_id, {}).get("quota", []) for item in self.resource_dict: if item not in quota: quota[item] = None - return {'quota': quota} + return {"quota": quota} def quota_set(self, project_id, json): self._ensure_project_exists(project_id) - self.cache[project_id]['quota'] = json['quota'] + self.cache[project_id]["quota"] = json["quota"] def quota_defaults_show(self): return { @@ -668,7 +700,7 @@ class FakeOctaviaClient(object): "listener": -1, "member": 50, "pool": -1, - "health_monitor": -1 + "health_monitor": -1, } } @@ -676,29 +708,27 @@ class FakeOctaviaClient(object): def action(project_id=None): self._ensure_project_exists(project_id) resource = self.cache.get(project_id, {}).get(resource_type, []) - links_name = resource_type + '_links' + links_name = resource_type + "_links" resource_name = self.resource_dict[resource_type] return {resource_name: resource, links_name: []} + return action def _ensure_project_exists(self, project_id): if project_id not in self.cache: - self.cache[project_id] = { - name: [] for name in self.resource_dict.keys()} - self.cache[project_id]['quota'] = dict( - CONF.quota.sizes['small']['octavia']) + self.cache[project_id] = {name: [] for name in self.resource_dict.keys()} + self.cache[project_id]["quota"] = dict(CONF.quota.sizes["small"]["octavia"]) def __getattr__(self, name): # NOTE(amelia): This is out of pure laziness global octavia_cache - if name[-5:] == '_list' and name[:-5] in self.resource_dict: + if name[-5:] == "_list" and name[:-5] in self.resource_dict: return self.lister(name[:-5]) else: raise AttributeError class FakeNovaClient(FakeOpenstackClient): - def __init__(self, region): global nova_cache super(FakeNovaClient, self).__init__(region, nova_cache) @@ -730,7 +760,7 @@ class FakeCinderClient(FakeOpenstackClient): def list(self, search_opts=None): if search_opts: - project_id = search_opts['project_id'] + project_id = search_opts["project_id"] global cinder_cache return cinder_cache[self.region][project_id][self.key] @@ -739,9 +769,8 @@ class FakeCinderClient(FakeOpenstackClient): self.region = region self._cache = cinder_cache self.quotas = FakeOpenstackClient.Quotas(self) - self.volumes = self.FakeResourceGroup(region, 'volumes') - self.volume_snapshots = self.FakeResourceGroup(region, - 'volume_snapshots') + self.volumes = self.FakeResourceGroup(region, "volumes") + self.volume_snapshots = self.FakeResourceGroup(region, "volume_snapshots") class FakeResource(object): @@ -755,24 +784,25 @@ class FakeResource(object): def setup_neutron_cache(region, project_id): global neutron_cache if region not in neutron_cache: - neutron_cache[region] = {'i': 0} + neutron_cache[region] = {"i": 0} else: - neutron_cache[region]['i'] = 0 + neutron_cache[region]["i"] = 0 if project_id not in neutron_cache[region]: neutron_cache[region][project_id] = {} neutron_cache[region][project_id] = { - 'networks': {}, - 'subnets': {}, - 'routers': {}, - 'security_groups': {}, - 'floatingips': {}, - 'security_group_rules': {}, - 'ports': {}, + "networks": {}, + "subnets": {}, + "routers": {}, + "security_groups": {}, + "floatingips": {}, + "security_group_rules": {}, + "ports": {}, } - neutron_cache[region][project_id]['quota'] = dict( - CONF.quota.sizes['small']['neutron']) + neutron_cache[region][project_id]["quota"] = dict( + CONF.quota.sizes["small"]["neutron"] + ) def setup_cinder_cache(region, project_id): @@ -783,12 +813,13 @@ def setup_cinder_cache(region, project_id): cinder_cache[region][project_id] = {} cinder_cache[region][project_id] = { - 'volumes': [], - 'volume_snapshots': [], + "volumes": [], + "volume_snapshots": [], } - cinder_cache[region][project_id]['quota'] = dict( - CONF.quota.sizes['small']['cinder']) + cinder_cache[region][project_id]["quota"] = dict( + CONF.quota.sizes["small"]["cinder"] + ) def setup_nova_cache(region, project_id): @@ -800,19 +831,18 @@ def setup_nova_cache(region, project_id): # Mocking the nova limits api nova_cache[region][project_id] = { - 'absolute': { + "absolute": { "totalInstancesUsed": 0, "totalFloatingIpsUsed": 0, "totalRAMUsed": 0, "totalCoresUsed": 0, - "totalSecurityGroupsUsed": 0 + "totalSecurityGroupsUsed": 0, } } - nova_cache[region][project_id]['quota'] = dict( - CONF.quota.sizes['small']['nova']) + nova_cache[region][project_id]["quota"] = dict(CONF.quota.sizes["small"]["nova"]) -def setup_quota_cache(region_name, project_id, size='small'): +def setup_quota_cache(region_name, project_id, size="small"): """ Sets up the quota cache for a given region and project """ global cinder_cache @@ -820,36 +850,31 @@ def setup_quota_cache(region_name, project_id, size='small'): cinder_cache[region_name] = {} if project_id not in cinder_cache[region_name]: - cinder_cache[region_name][project_id] = { - 'quota': {} - } + cinder_cache[region_name][project_id] = {"quota": {}} - cinder_cache[region_name][project_id]['quota'] = dict( - CONF.quota.sizes[size]['cinder']) + cinder_cache[region_name][project_id]["quota"] = dict( + CONF.quota.sizes[size]["cinder"] + ) global nova_cache if region_name not in nova_cache: nova_cache[region_name] = {} if project_id not in nova_cache[region_name]: - nova_cache[region_name][project_id] = { - 'quota': {} - } + nova_cache[region_name][project_id] = {"quota": {}} - nova_cache[region_name][project_id]['quota'] = dict( - CONF.quota.sizes[size]['nova']) + nova_cache[region_name][project_id]["quota"] = dict(CONF.quota.sizes[size]["nova"]) global neutron_cache if region_name not in neutron_cache: neutron_cache[region_name] = {} if project_id not in neutron_cache[region_name]: - neutron_cache[region_name][project_id] = { - 'quota': {} - } + neutron_cache[region_name][project_id] = {"quota": {}} - neutron_cache[region_name][project_id]['quota'] = dict( - CONF.quota.sizes[size]['neutron']) + neutron_cache[region_name][project_id]["quota"] = dict( + CONF.quota.sizes[size]["neutron"] + ) def setup_mock_caches(region, project_id): diff --git a/adjutant/common/tests/utils.py b/adjutant/common/tests/utils.py index 7f43651..38589ae 100644 --- a/adjutant/common/tests/utils.py +++ b/adjutant/common/tests/utils.py @@ -19,7 +19,6 @@ from adjutant.common.tests import fake_clients class AdjutantTestCase(TestCase): - def tearDown(self): fake_clients.identity_cache.clear() fake_clients.neutron_cache.clear() @@ -28,7 +27,6 @@ class AdjutantTestCase(TestCase): class AdjutantAPITestCase(APITestCase): - def tearDown(self): fake_clients.identity_cache.clear() fake_clients.neutron_cache.clear() diff --git a/adjutant/common/user_store.py b/adjutant/common/user_store.py index 90b2623..2ee0bfc 100644 --- a/adjutant/common/user_store.py +++ b/adjutant/common/user_store.py @@ -82,23 +82,20 @@ class IdentityManager(object): # pragma: no cover users = {} - user_assignments = self.ks_client.role_assignments.list( - project=project) + user_assignments = self.ks_client.role_assignments.list(project=project) for assignment in user_assignments: try: - user = users.get(assignment.user['id'], None) + user = users.get(assignment.user["id"], None) if not user: - user = self.ks_client.users.get( - assignment.user['id']) + user = self.ks_client.users.get(assignment.user["id"]) user.roles = [] user.inherited_roles = [] users[user.id] = user - if assignment.scope.get('OS-INHERIT:inherited_to'): - user.inherited_roles.append( - role_dict[assignment.role['id']]) + if assignment.scope.get("OS-INHERIT:inherited_to"): + user.inherited_roles.append(role_dict[assignment.role["id"]]) else: - user.roles.append(role_dict[assignment.role['id']]) + user.roles.append(role_dict[assignment.role["id"]]) except AttributeError: # Just means the assignment is a group, so ignore it. pass @@ -119,22 +116,19 @@ class IdentityManager(object): # pragma: no cover project = self.ks_client.projects.get(project) while project.parent_id: project = self.ks_client.projects.get(project.parent_id) - user_assignments = self.ks_client.role_assignments.list( - project=project) + user_assignments = self.ks_client.role_assignments.list(project=project) for assignment in user_assignments: - if not assignment.scope.get('OS-INHERIT:inherited_to'): + if not assignment.scope.get("OS-INHERIT:inherited_to"): continue try: - user = users.get( - assignment.user['id'], None) + user = users.get(assignment.user["id"], None) if user: - user.roles.append( - role_dict[assignment.role['id']]) + user.roles.append(role_dict[assignment.role["id"]]) else: - user = self.ks_client.users.get( - assignment.user['id']) + user = self.ks_client.users.get(assignment.user["id"]) user.roles = [ - role_dict[assignment.role['id']], ] + role_dict[assignment.role["id"]], + ] user.inherited_roles = [] users[user.id] = user except AttributeError: @@ -146,12 +140,18 @@ class IdentityManager(object): # pragma: no cover return [] return users.values() - def create_user(self, name, password, email, created_on, domain=None, - default_project=None): + def create_user( + self, name, password, email, created_on, domain=None, default_project=None + ): user = self.ks_client.users.create( - name=name, password=password, domain=domain, email=email, - default_project=default_project, created_on=created_on) + name=name, + password=password, + domain=domain, + email=email, + default_project=default_project, + created_on=created_on, + ) return user def enable_user(self, user): @@ -182,14 +182,14 @@ class IdentityManager(object): # pragma: no cover user_roles = [] user_assignments = self.ks_client.role_assignments.list( - user=user, project=project) + user=user, project=project + ) for assignment in user_assignments: - if (assignment.scope.get('OS-INHERIT:inherited_to') and not - inherited) or ( - inherited and not - assignment.scope.get('OS-INHERIT:inherited_to')): + if (assignment.scope.get("OS-INHERIT:inherited_to") and not inherited) or ( + inherited and not assignment.scope.get("OS-INHERIT:inherited_to") + ): continue - user_roles.append(role_dict[assignment.role['id']]) + user_roles.append(role_dict[assignment.role["id"]]) return user_roles def get_all_roles(self, user): @@ -204,8 +204,8 @@ class IdentityManager(object): # pragma: no cover user_assignments = self.ks_client.role_assignments.list(user=user) projects = defaultdict(list) for assignment in user_assignments: - project = assignment.scope['project']['id'] - projects[project].append(role_dict[assignment.role['id']]) + project = assignment.scope["project"]["id"] + projects[project].append(role_dict[assignment.role["id"]]) return projects @@ -213,8 +213,11 @@ class IdentityManager(object): # pragma: no cover try: if inherited: self.ks_client.roles.grant( - role, user=user, project=project, - os_inherit_extension_inherited=inherited) + role, + user=user, + project=project, + os_inherit_extension_inherited=inherited, + ) else: self.ks_client.roles.grant(role, user=user, project=project) except ks_exceptions.Conflict: @@ -224,8 +227,11 @@ class IdentityManager(object): # pragma: no cover def remove_user_role(self, user, role, project, inherited=False): if inherited: self.ks_client.roles.revoke( - role, user=user, project=project, - os_inherit_extension_inherited=inherited) + role, + user=user, + project=project, + os_inherit_extension_inherited=inherited, + ) else: self.ks_client.roles.revoke(role, user=user, project=project) @@ -233,8 +239,7 @@ class IdentityManager(object): # pragma: no cover try: # Using a filtered list as find is more efficient than # using the client find - projects = self.ks_client.projects.list( - name=project_name, domain=domain) + projects = self.ks_client.projects.list(name=project_name, domain=domain) if projects: # NOTE(adriant) project names are unique in a domain so # it is safe to assume filtering on project name and domain @@ -245,12 +250,11 @@ class IdentityManager(object): # pragma: no cover except ks_exceptions.NotFound: return None - def get_project(self, project_id, subtree_as_ids=False, - parents_as_ids=False): + def get_project(self, project_id, subtree_as_ids=False, parents_as_ids=False): try: project = self.ks_client.projects.get( - project_id, subtree_as_ids=subtree_as_ids, - parents_as_ids=parents_as_ids) + project_id, subtree_as_ids=subtree_as_ids, parents_as_ids=parents_as_ids + ) if parents_as_ids: depth = 1 last_root = None @@ -276,21 +280,31 @@ class IdentityManager(object): # pragma: no cover except ks_exceptions.NotFound: return [] - def update_project(self, project, name=None, domain=None, description=None, - enabled=None, **kwargs): + def update_project( + self, project, name=None, domain=None, description=None, enabled=None, **kwargs + ): try: return self.ks_client.projects.update( - project=project, domain=domain, name=name, - description=description, enabled=enabled, - **kwargs) + 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=None, description=""): + def create_project( + self, project_name, created_on, parent=None, domain=None, description="" + ): project = self.ks_client.projects.create( - project_name, domain, parent=parent, created_on=created_on, - description=description) + project_name, + domain, + parent=parent, + created_on=created_on, + description=description, + ) return project def get_domain(self, domain_id): @@ -321,20 +335,19 @@ class IdentityManager(object): # pragma: no cover return self.ks_client.regions.list(**kwargs) def list_credentials(self, user_id, cred_type=None): - return self.ks_client.credentials.list( - user_id=user_id, type=cred_type) + return self.ks_client.credentials.list(user_id=user_id, type=cred_type) def add_credential(self, user, cred_type, blob, project=None): return self.ks_client.credentials.create( - user=user, type=cred_type, blob=blob, project=project) + user=user, type=cred_type, blob=blob, project=project + ) def delete_credential(self, credential): return self.ks_client.credentials.delete(credential) def clear_credential_type(self, user_id, cred_type): # list credentials of the type for the user - credentials = self.ks_client.credentials.list( - user_id=user_id, type=cred_type) + credentials = self.ks_client.credentials.list(user_id=user_id, type=cred_type) for cred in credentials: if cred.user_id == user_id and cred.type == cred_type: self.ks_client.credentials.delete(cred) @@ -357,9 +370,12 @@ class IdentityManager(object): # pragma: no cover return list(set(all_roles)) # merge mapping lists to form a flat permitted roles list - manageable_role_names = [mrole for role_name in user_roles - if role_name in roles_mapping - for mrole in roles_mapping[role_name]] + manageable_role_names = [ + mrole + for role_name in user_roles + if role_name in roles_mapping + for mrole in roles_mapping[role_name] + ] # a set has unique items manageable_role_names = set(manageable_role_names) return manageable_role_names diff --git a/adjutant/config/__init__.py b/adjutant/config/__init__.py index 7a70f54..ee98186 100644 --- a/adjutant/config/__init__.py +++ b/adjutant/config/__init__.py @@ -40,13 +40,13 @@ _old_config_file = "/etc/adjutant/conf.yaml" _test_mode_commands = [ # Adjutant commands: - 'exampleconfig', + "exampleconfig", # Django commands: - 'check', - 'makemigrations', - 'squashmigrations', - 'test', - 'testserver', + "check", + "makemigrations", + "squashmigrations", + "test", + "testserver", ] @@ -81,7 +81,11 @@ def _load_config(): % conf_file_loc ) - if used_config_loc != conf_file and used_config_loc == _old_config_file and not test_mode: + if ( + used_config_loc != conf_file + and used_config_loc == _old_config_file + and not test_mode + ): print( "DEPRECATED: Using the old default config location '%s' is deprecated " "in favor of '%s', or setting a config location via the environment " diff --git a/adjutant/config/api.py b/adjutant/config/api.py index 2d15b62..14ef7e2 100644 --- a/adjutant/config/api.py +++ b/adjutant/config/api.py @@ -24,26 +24,26 @@ config_group.register_child_config( help_text="List of Active Delegate APIs.", required=True, default=[ - 'UserRoles', - 'UserDetail', - 'UserResetPassword', - 'UserList', - 'RoleList', + "UserRoles", + "UserDetail", + "UserResetPassword", + "UserList", + "RoleList", ], # NOTE(adriant): for testing purposes we include ALL default APIs test_default=[ - 'UserRoles', - 'UserDetail', - 'UserResetPassword', - 'UserList', - 'RoleList', - 'SignUp', - 'UpdateProjectQuotas', - 'CreateProjectAndUser', - 'InviteUser', - 'ResetPassword', - 'EditUser', - 'UpdateEmail', + "UserRoles", + "UserDetail", + "UserResetPassword", + "UserList", + "RoleList", + "SignUp", + "UpdateProjectQuotas", + "CreateProjectAndUser", + "InviteUser", + "ResetPassword", + "EditUser", + "UpdateEmail", ], ) ) diff --git a/adjutant/config/django.py b/adjutant/config/django.py index d77ceaa..682f977 100644 --- a/adjutant/config/django.py +++ b/adjutant/config/django.py @@ -49,7 +49,7 @@ config_group.register_child_config( fields.StrConfig( "secure_proxy_ssl_header", help_text="The header representing a HTTP header/value combination " - "that signifies a request is secure.", + "that signifies a request is secure.", default="HTTP_X_FORWARDED_PROTO", ) ) @@ -57,7 +57,7 @@ config_group.register_child_config( fields.StrConfig( "secure_proxy_ssl_header_value", help_text="The value representing a HTTP header/value combination " - "that signifies a request is secure.", + "that signifies a request is secure.", default="https", ) ) @@ -83,7 +83,7 @@ config_group.register_child_config( fields.StrConfig( "log_file", help_text="The name and location of the Adjutant log file, " - "superceded by 'adjutant.django.logging'.", + "superceded by 'adjutant.django.logging'.", default="adjutant.log", ) ) diff --git a/adjutant/config/identity.py b/adjutant/config/identity.py index 74e59f7..1472f4d 100644 --- a/adjutant/config/identity.py +++ b/adjutant/config/identity.py @@ -50,23 +50,14 @@ config_group.register_child_config( check_value_type=True, is_json=True, default={ - 'admin': [ - 'project_admin', - 'project_mod', - 'heat_stack_owner', - 'member', - ], - 'project_admin': [ - 'project_admin', - 'project_mod', - 'heat_stack_owner', - 'member', - ], - 'project_mod': [ - 'project_mod', - 'heat_stack_owner', - 'member', + "admin": ["project_admin", "project_mod", "heat_stack_owner", "member",], + "project_admin": [ + "project_admin", + "project_mod", + "heat_stack_owner", + "member", ], + "project_mod": ["project_mod", "heat_stack_owner", "member",], }, test_default={ "admin": ["project_admin", "project_mod", "member", "heat_stack_owner"], diff --git a/adjutant/config/quota.py b/adjutant/config/quota.py index 5222b12..2470537 100644 --- a/adjutant/config/quota.py +++ b/adjutant/config/quota.py @@ -18,36 +18,32 @@ from confspirator import types DEFAULT_QUOTA_SIZES = { - 'small': { - 'nova': { - 'instances': 10, - 'cores': 20, - 'ram': 65536, - 'floating_ips': 10, - 'fixed_ips': 0, - 'metadata_items': 128, - 'injected_files': 5, - 'injected_file_content_bytes': 10240, - 'key_pairs': 50, - 'security_groups': 20, - 'security_group_rules': 100, + "small": { + "nova": { + "instances": 10, + "cores": 20, + "ram": 65536, + "floating_ips": 10, + "fixed_ips": 0, + "metadata_items": 128, + "injected_files": 5, + "injected_file_content_bytes": 10240, + "key_pairs": 50, + "security_groups": 20, + "security_group_rules": 100, }, - 'cinder': { - 'gigabytes': 5000, - 'snapshots': 50, - 'volumes': 20, - }, - 'neutron': { - 'floatingip': 10, - 'network': 3, - 'port': 50, - 'router': 3, - 'security_group': 20, - 'security_group_rule': 100, - 'subnet': 3, + "cinder": {"gigabytes": 5000, "snapshots": 50, "volumes": 20,}, + "neutron": { + "floatingip": 10, + "network": 3, + "port": 50, + "router": 3, + "security_group": 20, + "security_group_rule": 100, + "subnet": 3, }, "octavia": { - 'health_monitor': 5, + "health_monitor": 5, "listener": 1, "load_balancer": 1, "member": 2, @@ -55,11 +51,7 @@ DEFAULT_QUOTA_SIZES = { }, }, "medium": { - "cinder": { - "gigabytes": 10000, - "volumes": 100, - "snapshots": 300 - }, + "cinder": {"gigabytes": 10000, "volumes": 100, "snapshots": 300}, "nova": { "metadata_items": 128, "injected_file_content_bytes": 10240, @@ -71,7 +63,7 @@ DEFAULT_QUOTA_SIZES = { "injected_files": 5, "cores": 100, "fixed_ips": 0, - "security_groups": 50 + "security_groups": 50, }, "neutron": { "security_group_rule": 400, @@ -80,10 +72,10 @@ DEFAULT_QUOTA_SIZES = { "floatingip": 25, "security_group": 50, "router": 5, - "port": 250 + "port": 250, }, "octavia": { - 'health_monitor': 50, + "health_monitor": 50, "listener": 5, "load_balancer": 5, "member": 5, @@ -91,11 +83,7 @@ DEFAULT_QUOTA_SIZES = { }, }, "large": { - "cinder": { - "gigabytes": 50000, - "volumes": 200, - "snapshots": 600 - }, + "cinder": {"gigabytes": 50000, "volumes": 200, "snapshots": 600}, "nova": { "metadata_items": 128, "injected_file_content_bytes": 10240, @@ -107,7 +95,7 @@ DEFAULT_QUOTA_SIZES = { "injected_files": 5, "cores": 200, "fixed_ips": 0, - "security_groups": 100 + "security_groups": 100, }, "neutron": { "security_group_rule": 800, @@ -116,10 +104,10 @@ DEFAULT_QUOTA_SIZES = { "floatingip": 50, "security_group": 100, "router": 10, - "port": 500 + "port": 500, }, "octavia": { - 'health_monitor': 100, + "health_monitor": 100, "listener": 10, "load_balancer": 10, "member": 10, @@ -145,16 +133,16 @@ config_group.register_child_config( fields.ListConfig( "sizes_ascending", help_text="An ascending list of all the quota size names, " - "so that Adjutant knows their relative sizes/order.", - default=['small', 'medium', 'large'], + "so that Adjutant knows their relative sizes/order.", + default=["small", "medium", "large"], ) ) config_group.register_child_config( fields.DictConfig( "services", help_text="A per region definition of what services Adjutant should manage " - "quotas for. '*' means all or default region.", + "quotas for. '*' means all or default region.", value_type=types.List(), - default={'*': ['cinder', 'neutron', 'nova']}, + default={"*": ["cinder", "neutron", "nova"]}, ) ) diff --git a/adjutant/config/workflow.py b/adjutant/config/workflow.py index 9f88f0e..16ec999 100644 --- a/adjutant/config/workflow.py +++ b/adjutant/config/workflow.py @@ -50,31 +50,34 @@ def _build_default_email_group( fields.StrConfig( "subject", help_text="Default email subject for this stage", - default=email_subject) + default=email_subject, + ) ) email_group.register_child_config( fields.StrConfig( - "from", - help_text="Default from email for this stage", - default=email_from) + "from", help_text="Default from email for this stage", default=email_from + ) ) email_group.register_child_config( fields.StrConfig( "reply", help_text="Default reply-to email for this stage", - default=email_reply) + default=email_reply, + ) ) email_group.register_child_config( fields.StrConfig( "template", help_text="Default email template for this stage", - default=email_template) + default=email_template, + ) ) email_group.register_child_config( fields.StrConfig( "html_template", help_text="Default email html template for this stage", - default=email_html_template) + default=email_html_template, + ) ) return email_group @@ -123,9 +126,7 @@ _notifications_defaults_group.register_child_config( "standard_handlers", help_text="Handlers to use for standard notifications.", required=True, - default=[ - 'EmailNotification', - ], + default=["EmailNotification",], ) ) _notifications_defaults_group.register_child_config( @@ -133,9 +134,7 @@ _notifications_defaults_group.register_child_config( "error_handlers", help_text="Handlers to use for error notifications.", required=True, - default=[ - 'EmailNotification', - ], + default=["EmailNotification",], ) ) _notifications_defaults_group.register_child_config( @@ -159,7 +158,7 @@ _notifications_defaults_group.register_child_config( "safe_errors", help_text="Error types which are safe to acknowledge automatically.", required=True, - default=['SMTPException'], + default=["SMTPException"], ) ) diff --git a/adjutant/core.py b/adjutant/core.py index 9d3c77b..e2d269e 100644 --- a/adjutant/core.py +++ b/adjutant/core.py @@ -36,28 +36,23 @@ class AdjutantCore(BaseFeatureSet): project_actions.NewProjectWithUserAction, project_actions.NewProjectAction, project_actions.AddDefaultUsersToProjectAction, - resource_actions.NewDefaultNetworkAction, resource_actions.NewProjectDefaultNetworkAction, resource_actions.SetProjectQuotaAction, resource_actions.UpdateProjectQuotasAction, - user_actions.NewUserAction, user_actions.ResetUserPasswordAction, user_actions.EditUserRolesAction, user_actions.UpdateUserEmailAction, - misc_actions.SendAdditionalEmailAction, ] tasks = [ project_tasks.CreateProjectAndUser, - user_tasks.EditUserRoles, user_tasks.InviteUser, user_tasks.ResetUserPassword, user_tasks.UpdateUserEmail, - resource_tasks.UpdateProjectQuotas, ] @@ -67,7 +62,6 @@ class AdjutantCore(BaseFeatureSet): task_apis.ResetPassword, task_apis.EditUser, task_apis.UpdateEmail, - openstack_apis.UserList, openstack_apis.UserDetail, openstack_apis.UserRoles, diff --git a/adjutant/exceptions.py b/adjutant/exceptions.py index 89f1f73..a03c306 100644 --- a/adjutant/exceptions.py +++ b/adjutant/exceptions.py @@ -24,6 +24,7 @@ class BaseServiceException(Exception): If thrown during the course of an API call will be caught and returned to the user as an ServiceUnavailable error with a 503 response. """ + default_message = "A internal service error has occured." def __init__(self, message=None): @@ -34,28 +35,23 @@ class BaseServiceException(Exception): class InvalidActionClass(BaseServiceException): - default_message = ( - "Cannot register action not built off the BaseAction class.") + default_message = "Cannot register action not built off the BaseAction class." class InvalidActionSerializer(BaseServiceException): - default_message = ( - "Action serializer must be a valid DRF serializer.") + default_message = "Action serializer must be a valid DRF serializer." class InvalidTaskClass(BaseServiceException): - default_message = ( - "Action serializer must be a valid DRF serializer.") + default_message = "Action serializer must be a valid DRF serializer." class InvalidAPIClass(BaseServiceException): - default_message = ( - "Cannot register task not built off the BaseTask class.") + default_message = "Cannot register task not built off the BaseTask class." class DelegateAPINotRegistered(BaseServiceException): - default_message = ( - "Failed to setup DelegateAPI that has not been registered.") + default_message = "Failed to setup DelegateAPI that has not been registered." class TaskNotRegistered(BaseServiceException): @@ -76,6 +72,7 @@ class ConfigurationException(BaseServiceException): class BaseAPIException(Exception): """An Task error occurred.""" + status_code = status.HTTP_400_BAD_REQUEST def __init__(self, message=None, internal_message=None): @@ -95,17 +92,17 @@ class BaseAPIException(Exception): class NotFound(BaseAPIException): status_code = status.HTTP_404_NOT_FOUND - default_message = 'Not found.' + default_message = "Not found." class TaskNotFound(NotFound): status_code = status.HTTP_404_NOT_FOUND - default_message = 'Task not found.' + default_message = "Task not found." class ServiceUnavailable(BaseAPIException): status_code = status.HTTP_503_SERVICE_UNAVAILABLE - default_message = 'Service temporarily unavailable, try again later.' + default_message = "Service temporarily unavailable, try again later." class TaskSerializersInvalid(BaseAPIException): @@ -145,5 +142,6 @@ class TaskStateInvalid(BaseTaskException): class TaskActionsFailed(BaseTaskException): """For use when Task processing fails and we want to wrap that.""" + status_code = status.HTTP_503_SERVICE_UNAVAILABLE - default_message = 'Service temporarily unavailable, try again later.' + default_message = "Service temporarily unavailable, try again later." diff --git a/adjutant/feature_set.py b/adjutant/feature_set.py index 6185d4b..e019a2b 100644 --- a/adjutant/feature_set.py +++ b/adjutant/feature_set.py @@ -43,14 +43,13 @@ from adjutant.config.feature_sets import config_group as feature_set_config def register_action_class(action_class): if not issubclass(action_class, BaseAction): raise exceptions.InvalidActionClass( - "'%s' is not a built off the BaseAction class." - % action_class.__name__ + "'%s' is not a built off the BaseAction class." % action_class.__name__ ) if action_class.serializer and not issubclass( - action_class.serializer, drf_serializers.Serializer): + action_class.serializer, drf_serializers.Serializer + ): raise exceptions.InvalidActionSerializer( - "serializer for '%s' is not a valid DRF serializer." - % action_class.__name__ + "serializer for '%s' is not a valid DRF serializer." % action_class.__name__ ) data = {} data[action_class.__name__] = action_class @@ -59,16 +58,14 @@ def register_action_class(action_class): # NOTE(adriant): We copy the config_group before naming it # to avoid cases where a subclass inherits but doesn't extend it setting_group = action_class.config_group.copy() - setting_group.set_name( - action_class.__name__, reformat_name=False) + setting_group.set_name(action_class.__name__, reformat_name=False) action_defaults_group.register_child_config(setting_group) def register_task_class(task_class): if not issubclass(task_class, tasks_base.BaseTask): raise exceptions.InvalidTaskClass( - "'%s' is not a built off the BaseTask class." - % task_class.__name__ + "'%s' is not a built off the BaseTask class." % task_class.__name__ ) data = {} data[task_class.task_type] = task_class @@ -78,16 +75,14 @@ def register_task_class(task_class): tasks.TASK_CLASSES.update(data) config_group = tasks_base.make_task_config(task_class) - config_group.set_name( - task_class.task_type, reformat_name=False) + config_group.set_name(task_class.task_type, reformat_name=False) tasks_group.register_child_config(config_group) def register_delegate_api_class(api_class): if not issubclass(api_class, BaseDelegateAPI): raise exceptions.InvalidAPIClass( - "'%s' is not a built off the BaseDelegateAPI class." - % api_class.__name__ + "'%s' is not a built off the BaseDelegateAPI class." % api_class.__name__ ) data = {} data[api_class.__name__] = api_class @@ -96,8 +91,7 @@ def register_delegate_api_class(api_class): # NOTE(adriant): We copy the config_group before naming it # to avoid cases where a subclass inherits but doesn't extend it setting_group = api_class.config_group.copy() - setting_group.set_name( - api_class.__name__, reformat_name=False) + setting_group.set_name(api_class.__name__, reformat_name=False) api_config.register_child_config(setting_group) @@ -121,7 +115,8 @@ def register_notification_handler(notification_handler): def register_feature_set_config(feature_set_group): if not isinstance(feature_set_group, groups.ConfigGroup): raise conf_exceptions.InvalidConfigClass( - "'%s' is not a valid config group class" % feature_set_group) + "'%s' is not a valid config group class" % feature_set_group + ) feature_set_config.register_child_config(feature_set_group) @@ -149,7 +144,7 @@ class BaseFeatureSet(object): config = None def __init__(self): - self.logger = getLogger('adjutant') + self.logger = getLogger("adjutant") def load(self): self.logger.info("Loading feature set: '%s'" % self.__class__.__name__) diff --git a/adjutant/middleware.py b/adjutant/middleware.py index 25fe16e..ca4d70a 100644 --- a/adjutant/middleware.py +++ b/adjutant/middleware.py @@ -22,20 +22,21 @@ class KeystoneHeaderUnwrapper: Middleware to build an easy to use dict of important data from what the keystone wsgi middleware gives us. """ + def __init__(self, get_response): self.get_response = get_response def __call__(self, request): try: token_data = { - 'project_domain_id': request.META['HTTP_X_PROJECT_DOMAIN_ID'], - 'project_name': request.META['HTTP_X_PROJECT_NAME'], - 'project_id': request.META['HTTP_X_PROJECT_ID'], - 'roles': request.META['HTTP_X_ROLES'].split(','), - 'user_domain_id': request.META['HTTP_X_USER_DOMAIN_ID'], - 'username': request.META['HTTP_X_USER_NAME'], - 'user_id': request.META['HTTP_X_USER_ID'], - 'authenticated': request.META['HTTP_X_IDENTITY_STATUS'] + "project_domain_id": request.META["HTTP_X_PROJECT_DOMAIN_ID"], + "project_name": request.META["HTTP_X_PROJECT_NAME"], + "project_id": request.META["HTTP_X_PROJECT_ID"], + "roles": request.META["HTTP_X_ROLES"].split(","), + "user_domain_id": request.META["HTTP_X_USER_DOMAIN_ID"], + "username": request.META["HTTP_X_USER_NAME"], + "user_id": request.META["HTTP_X_USER_ID"], + "authenticated": request.META["HTTP_X_IDENTITY_STATUS"], } except KeyError: token_data = {} @@ -49,6 +50,7 @@ class TestingHeaderUnwrapper: """ Replacement for the KeystoneHeaderUnwrapper for testing purposes. """ + def __init__(self, get_response): self.get_response = get_response @@ -58,17 +60,18 @@ class TestingHeaderUnwrapper: # TODO(adriant): follow up patch to update all the test # headers to provide domain values. # Default here is just a temporary measure. - 'project_domain_id': - request.META['headers'].get( - 'project_domain_id', 'default'), - 'project_name': request.META['headers']['project_name'], - 'project_id': request.META['headers']['project_id'], - 'roles': request.META['headers']['roles'].split(','), - 'user_domain_id': - request.META['headers'].get('user_domain_id', 'default'), - 'username': request.META['headers']['username'], - 'user_id': request.META['headers']['user_id'], - 'authenticated': request.META['headers']['authenticated'] + "project_domain_id": request.META["headers"].get( + "project_domain_id", "default" + ), + "project_name": request.META["headers"]["project_name"], + "project_id": request.META["headers"]["project_id"], + "roles": request.META["headers"]["roles"].split(","), + "user_domain_id": request.META["headers"].get( + "user_domain_id", "default" + ), + "username": request.META["headers"]["username"], + "user_id": request.META["headers"]["user_id"], + "authenticated": request.META["headers"]["authenticated"], } except KeyError: token_data = {} @@ -86,29 +89,29 @@ class RequestLoggingMiddleware: def __init__(self, get_response): self.get_response = get_response - self.logger = getLogger('adjutant') + self.logger = getLogger("adjutant") def __call__(self, request): self.logger.info( - '(%s) - <%s> %s [%s]', + "(%s) - <%s> %s [%s]", timezone.now(), request.method, - request.META['REMOTE_ADDR'], - request.get_full_path() + request.META["REMOTE_ADDR"], + request.get_full_path(), ) request.timer = time() response = self.get_response(request) - if hasattr(request, 'timer'): + if hasattr(request, "timer"): time_delta = time() - request.timer else: time_delta = -1 self.logger.info( - '(%s) - <%s> [%s] - (%.1fs)', + "(%s) - <%s> [%s] - (%.1fs)", timezone.now(), response.status_code, request.get_full_path(), - time_delta + time_delta, ) return response diff --git a/adjutant/notifications/utils.py b/adjutant/notifications/utils.py index adf2629..7f42d84 100644 --- a/adjutant/notifications/utils.py +++ b/adjutant/notifications/utils.py @@ -17,11 +17,7 @@ from adjutant.api.models import Notification def create_notification(task, notes, error=False, handlers=True): - notification = Notification.objects.create( - task=task, - notes=notes, - error=error - ) + notification = Notification.objects.create(task=task, notes=notes, error=error) notification.save() if not handlers: diff --git a/adjutant/notifications/v1/base.py b/adjutant/notifications/v1/base.py index f3f06e6..118be8c 100644 --- a/adjutant/notifications/v1/base.py +++ b/adjutant/notifications/v1/base.py @@ -34,7 +34,8 @@ class BaseNotificationHandler(object): """ try: notif_config = CONF.notifications.handler_defaults.get( - self.__class__.__name__) + self.__class__.__name__ + ) except KeyError: # Handler has no config return {} @@ -44,10 +45,12 @@ class BaseNotificationHandler(object): try: if notification.error: task_defaults = task_defaults.error_handler_config.get( - self.__class__.__name__) + self.__class__.__name__ + ) else: task_defaults = task_defaults.standard_handler_config.get( - self.__class__.__name__) + self.__class__.__name__ + ) except KeyError: task_defaults = {} diff --git a/adjutant/notifications/v1/email.py b/adjutant/notifications/v1/email.py index 82ca6d0..dfbc7ca 100644 --- a/adjutant/notifications/v1/email.py +++ b/adjutant/notifications/v1/email.py @@ -57,12 +57,11 @@ class EmailNotification(base.BaseNotificationHandler): fields.StrConfig( "template", help_text="Email template for this notification. " - "No template will cause the email not to send.", + "No template will cause the email not to send.", default="notification.txt", ), fields.StrConfig( - "html_template", - help_text="Email html template for this notification.", + "html_template", help_text="Email html template for this notification.", ), ] ) diff --git a/adjutant/notifications/v1/tests/test_notifications.py b/adjutant/notifications/v1/tests/test_notifications.py index 9aeeaff..49c5b69 100644 --- a/adjutant/notifications/v1/tests/test_notifications.py +++ b/adjutant/notifications/v1/tests/test_notifications.py @@ -22,39 +22,40 @@ from confspirator.tests import utils as conf_utils from adjutant.api.models import Notification from adjutant.tasks.models import Task -from adjutant.common.tests.fake_clients import ( - FakeManager, setup_identity_cache) +from adjutant.common.tests.fake_clients import FakeManager, setup_identity_cache from adjutant.common.tests.utils import AdjutantAPITestCase from adjutant.config import CONF from adjutant import exceptions -@mock.patch('adjutant.common.user_store.IdentityManager', - FakeManager) +@mock.patch("adjutant.common.user_store.IdentityManager", FakeManager) @conf_utils.modify_conf( CONF, operations={ "adjutant.workflow.tasks.create_project_and_user.notifications": [ - {'operation': 'override', 'value': { - "standard_handlers": ["EmailNotification"], - "error_handlers": ["EmailNotification"], - "standard_handler_config": { - "EmailNotification": { - 'emails': ['example_notification@example.com'], - 'reply': 'no-reply@example.com', - } + { + "operation": "override", + "value": { + "standard_handlers": ["EmailNotification"], + "error_handlers": ["EmailNotification"], + "standard_handler_config": { + "EmailNotification": { + "emails": ["example_notification@example.com"], + "reply": "no-reply@example.com", + } + }, + "error_handler_config": { + "EmailNotification": { + "emails": ["example_error_notification@example.com"], + "reply": "no-reply@example.com", + } + }, }, - "error_handler_config": { - "EmailNotification": { - 'emails': ['example_error_notification@example.com'], - 'reply': 'no-reply@example.com', - } - }, - }}, + }, ], - }) + }, +) class NotificationTests(AdjutantAPITestCase): - def test_new_project_sends_notification(self): """ Confirm that the email notification handler correctly acknowledges @@ -65,15 +66,15 @@ class NotificationTests(AdjutantAPITestCase): setup_identity_cache() url = "/v1/openstack/sign-up" - data = {'project_name': "test_project", 'email': "test@example.com"} - response = self.client.post(url, data, format='json') + data = {"project_name": "test_project", "email": "test@example.com"} + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) new_task = Task.objects.all()[0] self.assertEqual(Notification.objects.count(), 1) self.assertEqual(len(mail.outbox), 2) self.assertEqual(mail.outbox[1].subject, "create_project_and_user notification") - self.assertEqual(mail.outbox[1].to, ['example_notification@example.com']) + self.assertEqual(mail.outbox[1].to, ["example_notification@example.com"]) notif = Notification.objects.all()[0] self.assertEqual(notif.task.uuid, new_task.uuid) @@ -81,12 +82,12 @@ class NotificationTests(AdjutantAPITestCase): self.assertTrue(notif.acknowledged) headers = { - 'project_name': "test_project", - 'project_id': "test_project_id", - 'roles': "admin,member", - 'username': "test@example.com", - 'user_id': "test_user_id", - 'authenticated': True + "project_name": "test_project", + "project_id": "test_project_id", + "roles": "admin,member", + "username": "test@example.com", + "user_id": "test_user_id", + "authenticated": True, } url = "/v1/tasks/" + new_task.uuid with mock.patch( @@ -102,8 +103,10 @@ class NotificationTests(AdjutantAPITestCase): # should send token email, but no new notification self.assertEqual(Notification.objects.count(), 2) self.assertEqual(len(mail.outbox), 3) - self.assertEqual(mail.outbox[2].subject, "Error - create_project_and_user notification") - self.assertEqual(mail.outbox[2].to, ['example_error_notification@example.com']) + self.assertEqual( + mail.outbox[2].subject, "Error - create_project_and_user notification" + ) + self.assertEqual(mail.outbox[2].to, ["example_error_notification@example.com"]) notif = Notification.objects.all()[1] self.assertEqual(notif.task.uuid, new_task.uuid) diff --git a/adjutant/settings.py b/adjutant/settings.py index 5968e74..7e5c62c 100644 --- a/adjutant/settings.py +++ b/adjutant/settings.py @@ -33,41 +33,40 @@ BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # Application definition INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'rest_framework', - 'rest_framework_swagger', - - 'adjutant.commands', - 'adjutant.actions', - 'adjutant.api', - 'adjutant.notifications', - 'adjutant.tasks', - 'adjutant.startup', + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "rest_framework", + "rest_framework_swagger", + "adjutant.commands", + "adjutant.actions", + "adjutant.api", + "adjutant.notifications", + "adjutant.tasks", + "adjutant.startup", ) MIDDLEWARE = ( - 'django.middleware.common.CommonMiddleware', - 'adjutant.middleware.KeystoneHeaderUnwrapper', - 'adjutant.middleware.RequestLoggingMiddleware' + "django.middleware.common.CommonMiddleware", + "adjutant.middleware.KeystoneHeaderUnwrapper", + "adjutant.middleware.RequestLoggingMiddleware", ) -if 'test' in sys.argv: +if "test" in sys.argv: # modify MIDDLEWARE MIDDLEWARE = list(MIDDLEWARE) - MIDDLEWARE.remove('adjutant.middleware.KeystoneHeaderUnwrapper') - MIDDLEWARE.append('adjutant.middleware.TestingHeaderUnwrapper') + MIDDLEWARE.remove("adjutant.middleware.KeystoneHeaderUnwrapper") + MIDDLEWARE.append("adjutant.middleware.TestingHeaderUnwrapper") -ROOT_URLCONF = 'adjutant.urls' +ROOT_URLCONF = "adjutant.urls" -WSGI_APPLICATION = 'adjutant.wsgi.application' +WSGI_APPLICATION = "adjutant.wsgi.application" -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -75,33 +74,29 @@ USE_L10N = True USE_TZ = True -STATIC_URL = '/static/' +STATIC_URL = "/static/" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, - 'NAME': 'default', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "NAME": "default", }, { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, - 'DIRS': ['/etc/adjutant/templates/'], - 'NAME': 'include_etc_templates', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "DIRS": ["/etc/adjutant/templates/"], + "NAME": "include_etc_templates", }, ] AUTHENTICATION_BACKENDS = [] REST_FRAMEWORK = { - 'EXCEPTION_HANDLER': 'adjutant.api.exception_handler.exception_handler', - 'DEFAULT_RENDERER_CLASSES': [ - 'rest_framework.renderers.JSONRenderer', - ], - 'DEFAULT_PARSER_CLASSES': [ - 'rest_framework.parsers.JSONParser', - ], - 'DEFAULT_PERMISSION_CLASSES': [], + "EXCEPTION_HANDLER": "adjutant.api.exception_handler.exception_handler", + "DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer",], + "DEFAULT_PARSER_CLASSES": ["rest_framework.parsers.JSONParser",], + "DEFAULT_PERMISSION_CLASSES": [], } SECRET_KEY = adj_conf.django.secret_key @@ -109,14 +104,15 @@ SECRET_KEY = adj_conf.django.secret_key # SECURITY WARNING: don't run with debug turned on in production! DEBUG = adj_conf.django.debug if DEBUG: - REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'].append( - 'rest_framework.renderers.BrowsableAPIRenderer') + REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append( + "rest_framework.renderers.BrowsableAPIRenderer" + ) ALLOWED_HOSTS = adj_conf.django.allowed_hosts SECURE_PROXY_SSL_HEADER = ( adj_conf.django.secure_proxy_ssl_header, - adj_conf.django.secure_proxy_ssl_header_value + adj_conf.django.secure_proxy_ssl_header_value, ) DATABASES = adj_conf.django.databases @@ -125,30 +121,22 @@ if adj_conf.django.logging: LOGGING = adj_conf.django.logging else: LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'file': { - 'level': 'INFO', - 'class': 'logging.FileHandler', - 'filename': adj_conf.django.log_file, + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "file": { + "level": "INFO", + "class": "logging.FileHandler", + "filename": adj_conf.django.log_file, }, }, - 'loggers': { - 'adjutant': { - 'handlers': ['file'], - 'level': 'INFO', - 'propagate': False, - }, - 'django': { - 'handlers': ['file'], - 'level': 'INFO', - 'propagate': False, - }, - 'keystonemiddleware': { - 'handlers': ['file'], - 'level': 'INFO', - 'propagate': False, + "loggers": { + "adjutant": {"handlers": ["file"], "level": "INFO", "propagate": False,}, + "django": {"handlers": ["file"], "level": "INFO", "propagate": False,}, + "keystonemiddleware": { + "handlers": ["file"], + "level": "INFO", + "propagate": False, }, }, } diff --git a/adjutant/startup/__init__.py b/adjutant/startup/__init__.py index 4fc1981..6868ddd 100644 --- a/adjutant/startup/__init__.py +++ b/adjutant/startup/__init__.py @@ -1 +1 @@ -default_app_config = 'adjutant.startup.config.StartUpConfig' +default_app_config = "adjutant.startup.config.StartUpConfig" diff --git a/adjutant/startup/checks.py b/adjutant/startup/checks.py index 0f0b5b2..0147802 100644 --- a/adjutant/startup/checks.py +++ b/adjutant/startup/checks.py @@ -19,14 +19,15 @@ from adjutant.exceptions import ActionNotRegistered, DelegateAPINotRegistered def check_expected_delegate_apis(): missing_delegate_apis = list( - set(CONF.api.active_delegate_apis) - - set(api.DELEGATE_API_CLASSES.keys())) + set(CONF.api.active_delegate_apis) - set(api.DELEGATE_API_CLASSES.keys()) + ) if missing_delegate_apis: raise DelegateAPINotRegistered( message=( - "Expected DelegateAPIs are unregistered: %s" - % missing_delegate_apis)) + "Expected DelegateAPIs are unregistered: %s" % missing_delegate_apis + ) + ) def check_configured_actions(): @@ -38,11 +39,12 @@ def check_configured_actions(): configured_actions += task_class.default_actions configured_actions += CONF.workflow.tasks.get( - task_class.task_type).additional_actions + task_class.task_type + ).additional_actions - missing_actions = list( - set(configured_actions) - set(actions.ACTION_CLASSES.keys())) + missing_actions = list(set(configured_actions) - set(actions.ACTION_CLASSES.keys())) if missing_actions: raise ActionNotRegistered( - "Configured actions are unregistered: %s" % missing_actions) + "Configured actions are unregistered: %s" % missing_actions + ) diff --git a/adjutant/startup/loading.py b/adjutant/startup/loading.py index 096272d..0e603e9 100644 --- a/adjutant/startup/loading.py +++ b/adjutant/startup/loading.py @@ -16,6 +16,6 @@ import pkg_resources def load_feature_sets(): - for entry_point in pkg_resources.iter_entry_points('adjutant.feature_sets'): + for entry_point in pkg_resources.iter_entry_points("adjutant.feature_sets"): feature_set = entry_point.load() feature_set().load() diff --git a/adjutant/tasks/migrations/0001_initial.py b/adjutant/tasks/migrations/0001_initial.py index c70da0e..49e66f6 100644 --- a/adjutant/tasks/migrations/0001_initial.py +++ b/adjutant/tasks/migrations/0001_initial.py @@ -11,33 +11,51 @@ import jsonfield.fields class Migration(migrations.Migration): dependencies = [ - ('api', '0005_auto_20190610_0209'), + ("api", "0005_auto_20190610_0209"), ] operations = [ migrations.SeparateDatabaseAndState( state_operations=[ migrations.CreateModel( - name='Task', + name="Task", fields=[ - ('uuid', models.CharField(default=adjutant.tasks.models.hex_uuid, max_length=32, primary_key=True, serialize=False)), - ('hash_key', models.CharField(db_index=True, max_length=64)), - ('ip_address', models.GenericIPAddressField()), - ('keystone_user', jsonfield.fields.JSONField(default={})), - ('project_id', models.CharField(db_index=True, max_length=64, null=True)), - ('approved_by', jsonfield.fields.JSONField(default={})), - ('task_type', models.CharField(db_index=True, max_length=100)), - ('action_notes', jsonfield.fields.JSONField(default={})), - ('cancelled', models.BooleanField(db_index=True, default=False)), - ('approved', models.BooleanField(db_index=True, default=False)), - ('completed', models.BooleanField(db_index=True, default=False)), - ('created_on', models.DateTimeField(default=django.utils.timezone.now)), - ('approved_on', models.DateTimeField(null=True)), - ('completed_on', models.DateTimeField(null=True)), + ( + "uuid", + models.CharField( + default=adjutant.tasks.models.hex_uuid, + max_length=32, + primary_key=True, + serialize=False, + ), + ), + ("hash_key", models.CharField(db_index=True, max_length=64)), + ("ip_address", models.GenericIPAddressField()), + ("keystone_user", jsonfield.fields.JSONField(default={})), + ( + "project_id", + models.CharField(db_index=True, max_length=64, null=True), + ), + ("approved_by", jsonfield.fields.JSONField(default={})), + ("task_type", models.CharField(db_index=True, max_length=100)), + ("action_notes", jsonfield.fields.JSONField(default={})), + ( + "cancelled", + models.BooleanField(db_index=True, default=False), + ), + ("approved", models.BooleanField(db_index=True, default=False)), + ( + "completed", + models.BooleanField(db_index=True, default=False), + ), + ( + "created_on", + models.DateTimeField(default=django.utils.timezone.now), + ), + ("approved_on", models.DateTimeField(null=True)), + ("completed_on", models.DateTimeField(null=True)), ], - options={ - 'indexes': [], - }, + options={"indexes": [],}, ), ], ), diff --git a/adjutant/tasks/migrations/0002_auto_20190619_0613.py b/adjutant/tasks/migrations/0002_auto_20190619_0613.py index 354e8cd..813d168 100644 --- a/adjutant/tasks/migrations/0002_auto_20190619_0613.py +++ b/adjutant/tasks/migrations/0002_auto_20190619_0613.py @@ -9,71 +9,77 @@ import jsonfield.fields class Migration(migrations.Migration): dependencies = [ - ('tasks', '0001_initial'), + ("tasks", "0001_initial"), ] operations = [ - migrations.RemoveField( - model_name='task', - name='ip_address', - ), + migrations.RemoveField(model_name="task", name="ip_address",), migrations.AddField( - model_name='task', - name='task_notes', + model_name="task", + name="task_notes", field=jsonfield.fields.JSONField(default=[]), ), migrations.AlterField( - model_name='task', - name='approved', + model_name="task", + name="approved", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='task', - name='cancelled', + model_name="task", + name="cancelled", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='task', - name='completed', + model_name="task", + name="completed", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='task', - name='hash_key', - field=models.CharField(max_length=64), + model_name="task", name="hash_key", field=models.CharField(max_length=64), ), migrations.AlterField( - model_name='task', - name='project_id', + model_name="task", + name="project_id", field=models.CharField(max_length=64, null=True), ), migrations.AlterField( - model_name='task', - name='task_type', - field=models.CharField(max_length=100), + model_name="task", name="task_type", field=models.CharField(max_length=100), ), migrations.AddIndex( - model_name='task', - index=models.Index(fields=['completed'], name='completed_idx'), + model_name="task", + index=models.Index(fields=["completed"], name="completed_idx"), ), migrations.AddIndex( - model_name='task', - index=models.Index(fields=['project_id', 'uuid'], name='tasks_task_project_a1cfa7_idx'), + model_name="task", + index=models.Index( + fields=["project_id", "uuid"], name="tasks_task_project_a1cfa7_idx" + ), ), migrations.AddIndex( - model_name='task', - index=models.Index(fields=['project_id', 'task_type'], name='tasks_task_project_e86456_idx'), + model_name="task", + index=models.Index( + fields=["project_id", "task_type"], name="tasks_task_project_e86456_idx" + ), ), migrations.AddIndex( - model_name='task', - index=models.Index(fields=['project_id', 'task_type', 'cancelled'], name='tasks_task_project_f0ec0e_idx'), + model_name="task", + index=models.Index( + fields=["project_id", "task_type", "cancelled"], + name="tasks_task_project_f0ec0e_idx", + ), ), migrations.AddIndex( - model_name='task', - index=models.Index(fields=['project_id', 'task_type', 'completed', 'cancelled'], name='tasks_task_project_1cb2a8_idx'), + model_name="task", + index=models.Index( + fields=["project_id", "task_type", "completed", "cancelled"], + name="tasks_task_project_1cb2a8_idx", + ), ), migrations.AddIndex( - model_name='task', - index=models.Index(fields=['hash_key', 'completed', 'cancelled'], name='tasks_task_hash_ke_781b6a_idx'), + model_name="task", + index=models.Index( + fields=["hash_key", "completed", "cancelled"], + name="tasks_task_hash_ke_781b6a_idx", + ), ), ] diff --git a/adjutant/tasks/models.py b/adjutant/tasks/models.py index ead2ff9..f4ca74f 100644 --- a/adjutant/tasks/models.py +++ b/adjutant/tasks/models.py @@ -31,8 +31,8 @@ class Task(models.Model): Stores the state of the Task and a log for the action. """ - uuid = models.CharField(max_length=32, default=hex_uuid, - primary_key=True) + + uuid = models.CharField(max_length=32, default=hex_uuid, primary_key=True) hash_key = models.CharField(max_length=64) # who is this: @@ -61,13 +61,12 @@ class Task(models.Model): class Meta: indexes = [ - models.Index(fields=['completed'], name='completed_idx'), - models.Index(fields=['project_id', 'uuid']), - models.Index(fields=['project_id', 'task_type']), - models.Index(fields=['project_id', 'task_type', 'cancelled']), - models.Index(fields=[ - 'project_id', 'task_type', 'completed', 'cancelled']), - models.Index(fields=['hash_key', 'completed', 'cancelled']), + models.Index(fields=["completed"], name="completed_idx"), + models.Index(fields=["project_id", "uuid"]), + models.Index(fields=["project_id", "task_type"]), + models.Index(fields=["project_id", "task_type", "cancelled"]), + models.Index(fields=["project_id", "task_type", "completed", "cancelled"]), + models.Index(fields=["hash_key", "completed", "cancelled"]), ] def __init__(self, *args, **kwargs): @@ -89,7 +88,7 @@ class Task(models.Model): @property def actions(self): - return self.action_set.order_by('order') + return self.action_set.order_by("order") @property def tokens(self): @@ -102,11 +101,13 @@ class Task(models.Model): def to_dict(self): actions = [] for action in self.actions: - actions.append({ - "action_name": action.action_name, - "data": action.action_data, - "valid": action.valid - }) + actions.append( + { + "action_name": action.action_name, + "data": action.action_data, + "valid": action.valid, + } + ) return { "uuid": self.uuid, diff --git a/adjutant/tasks/v1/base.py b/adjutant/tasks/v1/base.py index f6a1331..f02110e 100644 --- a/adjutant/tasks/v1/base.py +++ b/adjutant/tasks/v1/base.py @@ -23,8 +23,7 @@ from adjutant.api.models import Task from adjutant.config import CONF from django.utils import timezone from adjutant.notifications.utils import create_notification -from adjutant.tasks.v1.utils import ( - send_stage_email, create_token, handle_task_error) +from adjutant.tasks.v1.utils import send_stage_email, create_token, handle_task_error from adjutant import exceptions @@ -35,7 +34,7 @@ def make_task_config(task_class): fields.BoolConfig( "allow_auto_approve", help_text="Override if this task allows auto_approval. " - "Otherwise uses task default.", + "Otherwise uses task default.", default=task_class.allow_auto_approve, ) ) @@ -43,7 +42,7 @@ def make_task_config(task_class): fields.ListConfig( "additional_actions", help_text="Additional actions to be run as part of the task " - "after default actions.", + "after default actions.", default=task_class.additional_actions or [], ) ) @@ -51,7 +50,7 @@ def make_task_config(task_class): fields.IntConfig( "token_expiry", help_text="Override for the task token expiry. " - "Otherwise uses task default.", + "Otherwise uses task default.", default=task_class.token_expiry, ) ) @@ -59,13 +58,11 @@ def make_task_config(task_class): fields.DictConfig( "actions", help_text="Action config overrides over the action defaults. " - "See 'adjutant.workflow.action_defaults'.", + "See 'adjutant.workflow.action_defaults'.", is_json=True, default=task_class.action_config or {}, sample_default={ - "SomeCustomAction": { - "some_action_setting": "" - } + "SomeCustomAction": {"some_action_setting": ""} }, ) ) @@ -73,14 +70,12 @@ def make_task_config(task_class): fields.DictConfig( "emails", help_text="Email config overrides for this task over task defaults." - "See 'adjutant.workflow.emails'.", + "See 'adjutant.workflow.emails'.", is_json=True, default=task_class.email_config or {}, sample_default={ "initial": None, - "token": { - "subject": "Some custom subject", - }, + "token": {"subject": "Some custom subject",}, }, ) ) @@ -88,7 +83,7 @@ def make_task_config(task_class): fields.DictConfig( "notifications", help_text="Notification config overrides for this task over task defaults." - "See 'adjutant.workflow.notifications'.", + "See 'adjutant.workflow.notifications'.", is_json=True, default=task_class.notification_config or {}, sample_default={ @@ -96,14 +91,14 @@ def make_task_config(task_class): "error_handlers": ["EmailNotification"], "standard_handler_config": { "EmailNotification": { - 'emails': ['example@example.com'], - 'reply': 'no-reply@example.com', + "emails": ["example@example.com"], + "reply": "no-reply@example.com", } }, "error_handler_config": { "EmailNotification": { - 'emails': ['example@example.com'], - 'reply': 'no-reply@example.com', + "emails": ["example@example.com"], + "reply": "no-reply@example.com", } }, }, @@ -141,50 +136,45 @@ class BaseTask(object): email_config = None notification_config = None - def __init__(self, - task_model=None, - task_data=None, - action_data=None): + def __init__(self, task_model=None, task_data=None, action_data=None): self._config = None - self.logger = getLogger('adjutant') + self.logger = getLogger("adjutant") if task_model: self.task = task_model self._refresh_actions() else: # raises 400 validation error - action_serializer_list = self._instantiate_action_serializers( - action_data) + action_serializer_list = self._instantiate_action_serializers(action_data) hash_key = self._create_task_hash(action_serializer_list) # raises duplicate error self._handle_duplicates(hash_key) - keystone_user = task_data.get('keystone_user', {}) + keystone_user = task_data.get("keystone_user", {}) self.task = Task.objects.create( keystone_user=keystone_user, - project_id=keystone_user.get('project_id'), + project_id=keystone_user.get("project_id"), task_type=self.task_type, - hash_key=hash_key) + hash_key=hash_key, + ) self.task.save() # Instantiate actions with serializers self.actions = [] for i, action in enumerate(action_serializer_list): - data = action['serializer'].validated_data + data = action["serializer"].validated_data # construct the action class - self.actions.append(action['action']( - data=data, - task=self.task, - order=i - )) + self.actions.append( + action["action"](data=data, task=self.task, order=i) + ) self.logger.info( "(%s) - '%s' task created (%s)." - % (timezone.now(), self.task_type, self.task.uuid)) + % (timezone.now(), self.task_type, self.task.uuid) + ) - def _instantiate_action_serializers(self, action_data, - use_existing_actions=False): + def _instantiate_action_serializers(self, action_data, use_existing_actions=False): action_serializer_list = [] if use_existing_actions: @@ -209,13 +199,13 @@ class BaseTask(object): # instantiate serializer class if not action_class.serializer: raise exceptions.SerializerMissingException( - "No serializer defined for action %s" % action_name) + "No serializer defined for action %s" % action_name + ) serializer = action_class.serializer(data=action_data) - action_serializer_list.append({ - 'name': action_name, - 'action': action_class, - 'serializer': serializer}) + action_serializer_list.append( + {"name": action_name, "action": action_class, "serializer": serializer} + ) if serializer and not serializer.is_valid(): valid = False @@ -223,52 +213,50 @@ class BaseTask(object): if not valid: errors = {} for action in action_serializer_list: - if action['serializer']: - errors.update(action['serializer'].errors) + if action["serializer"]: + errors.update(action["serializer"].errors) raise exceptions.TaskSerializersInvalid(errors) return action_serializer_list def _create_task_hash(self, action_list): - hashable_list = [self.task_type, ] + hashable_list = [ + self.task_type, + ] for action in action_list: - hashable_list.append(action['name']) - if not action['serializer']: + hashable_list.append(action["name"]) + if not action["serializer"]: continue # iterate like this to maintain consistent order for hash - fields = sorted(action['serializer'].validated_data.keys()) + fields = sorted(action["serializer"].validated_data.keys()) for field in fields: try: - hashable_list.append( - action['serializer'].validated_data[field]) + hashable_list.append(action["serializer"].validated_data[field]) except KeyError: if field == "username" and CONF.identity.username_is_email: continue else: raise - return hashlib.sha256(str(hashable_list).encode('utf-8')).hexdigest() + return hashlib.sha256(str(hashable_list).encode("utf-8")).hexdigest() def _handle_duplicates(self, hash_key): duplicate_tasks = Task.objects.filter( - hash_key=hash_key, - completed=0, - cancelled=0) + hash_key=hash_key, completed=0, cancelled=0 + ) if not duplicate_tasks: return if self.duplicate_policy == "cancel": now = timezone.now() - self.logger.info( - "(%s) - Task is a duplicate - Cancelling old tasks." % - now) + self.logger.info("(%s) - Task is a duplicate - Cancelling old tasks." % now) for task in duplicate_tasks: task.add_task_note( - "Task cancelled because was an old duplicate. - (%s)" - % now) + "Task cancelled because was an old duplicate. - (%s)" % now + ) task.get_task().cancel() return @@ -288,7 +276,7 @@ class BaseTask(object): email_conf = self.config.emails.token send_stage_email(self.task, email_conf, token) except KeyError as e: - handle_task_error(e, self.task, error_text='while sending token') + handle_task_error(e, self.task, error_text="while sending token") def add_note(self, note): """ @@ -296,7 +284,8 @@ class BaseTask(object): """ now = timezone.now() self.logger.info( - "(%s)(%s)(%s) - %s" % (now, self.task_type, self.task.uuid, note)) + "(%s)(%s)(%s) - %s" % (now, self.task_type, self.task.uuid, note) + ) note = "%s - (%s)" % (note, now) self.task.add_task_note(note) @@ -320,7 +309,8 @@ class BaseTask(object): if not valid: # TODO(amelia): get action invalidation reasons and raise those raise exceptions.TaskActionsInvalid( - self.task, 'actions invalid', internal_message) + self.task, "actions invalid", internal_message + ) @property def approved(self): @@ -342,40 +332,47 @@ class BaseTask(object): if completed is not None: if self.task.completed and not completed: raise exceptions.TaskStateInvalid( - self.task, "This task has already been completed.") + self.task, "This task has already been completed." + ) if not self.task.completed and completed: raise exceptions.TaskStateInvalid( - self.task, "This task hasn't been completed.") + self.task, "This task hasn't been completed." + ) if cancelled is not None: if self.task.cancelled and not cancelled: raise exceptions.TaskStateInvalid( - self.task, "This task has been cancelled.") + self.task, "This task has been cancelled." + ) if not self.task.cancelled and cancelled: raise exceptions.TaskStateInvalid( - self.task, "This task has not been cancelled.") + self.task, "This task has not been cancelled." + ) if approved is not None: if self.task.approved and not approved: raise exceptions.TaskStateInvalid( - self.task, "This task has already been approved.") + self.task, "This task has already been approved." + ) if not self.task.approved and approved: raise exceptions.TaskStateInvalid( - self.task, "This task has not been approved.") + self.task, "This task has not been approved." + ) def update(self, action_data): self.confirm_state(approved=False, completed=False, cancelled=False) action_serializer_list = self._instantiate_action_serializers( - action_data, use_existing_actions=True) + action_data, use_existing_actions=True + ) hash_key = self._create_task_hash(action_serializer_list) self._handle_duplicates(hash_key) for action in action_serializer_list: - data = action['serializer'].validated_data + data = action["serializer"].validated_data - action['action'].action.action_data = data - action['action'].action.save() + action["action"].action.action_data = data + action["action"].action.save() self._refresh_actions() self.prepare() @@ -392,8 +389,7 @@ class BaseTask(object): try: action.prepare() except Exception as e: - handle_task_error( - e, self.task, error_text='while setting up task') + handle_task_error(e, self.task, error_text="while setting up task") # send initial confirmation email: email_conf = self.config.emails.initial @@ -424,10 +420,7 @@ class BaseTask(object): return if self.send_approval_notification: - notes = { - 'notes': - ["'%s' task needs approval." % self.task_type] - } + notes = {"notes": ["'%s' task needs approval." % self.task_type]} create_notification(self.task, notes) def approve(self, approved_by="system"): @@ -451,8 +444,7 @@ class BaseTask(object): try: action.approve() except Exception as e: - handle_task_error( - e, self.task, error_text='while approving task') + handle_task_error(e, self.task, error_text="while approving task") self.is_valid("task invalid after approval") @@ -495,10 +487,11 @@ class BaseTask(object): try: data[field] = token_data[field] except KeyError: - errors[field] = ["This field is required.", ] + errors[field] = [ + "This field is required.", + ] except TypeError: - errors = ["Improperly formated json. " - "Should be a key-value object."] + errors = ["Improperly formated json. " "Should be a key-value object."] break if errors: @@ -510,8 +503,7 @@ class BaseTask(object): try: action.submit(data) except Exception as e: - handle_task_error( - e, self.task, "while submiting task") + handle_task_error(e, self.task, "while submiting task") self.is_valid("task invalid after submit") diff --git a/adjutant/tasks/v1/manager.py b/adjutant/tasks/v1/manager.py index fb4a925..70fb669 100644 --- a/adjutant/tasks/v1/manager.py +++ b/adjutant/tasks/v1/manager.py @@ -23,9 +23,8 @@ from adjutant.tasks.v1.base import BaseTask class TaskManager(object): - def __init__(self, message=None): - self.logger = getLogger('adjutant') + self.logger = getLogger("adjutant") def _get_task_class(self, task_type): """Get the task class from the given task_type @@ -38,8 +37,7 @@ class TaskManager(object): except KeyError: if task_type in tasks.TASK_CLASSES.values(): return task_type - raise exceptions.TaskNotRegistered( - "Unknown task type: '%s'" % task_type) + raise exceptions.TaskNotRegistered("Unknown task type: '%s'" % task_type) def create_from_request(self, task_type, request): task_class = self._get_task_class(task_type) @@ -65,7 +63,8 @@ class TaskManager(object): task = Task.objects.get(uuid=task) except Task.DoesNotExist: raise exceptions.TaskNotFound( - "Task not found with uuid of: '%s'" % task) + "Task not found with uuid of: '%s'" % task + ) if isinstance(task, Task): try: return tasks.TASK_CLASSES[task.task_type](task) @@ -74,11 +73,9 @@ class TaskManager(object): # for older deprecated tasks: raise exceptions.TaskNotRegistered( "Task type '%s' not registered, " - "and used for existing task." - % task.task_type + "and used for existing task." % task.task_type ) - raise exceptions.TaskNotFound( - "Task not found for value of: '%s'" % task) + raise exceptions.TaskNotFound("Task not found for value of: '%s'" % task) def update(self, task, action_data): task = self.get(task) diff --git a/adjutant/tasks/v1/projects.py b/adjutant/tasks/v1/projects.py index 5d560eb..9d7a632 100644 --- a/adjutant/tasks/v1/projects.py +++ b/adjutant/tasks/v1/projects.py @@ -18,22 +18,22 @@ from adjutant.tasks.v1.base import BaseTask class CreateProjectAndUser(BaseTask): duplicate_policy = "block" task_type = "create_project_and_user" - deprecated_task_types = ['create_project', 'signup'] + deprecated_task_types = ["create_project", "signup"] default_actions = [ "NewProjectWithUserAction", ] email_config = { - 'initial': { - 'template': 'create_project_and_user_initial.txt', - 'subject': 'signup received' + "initial": { + "template": "create_project_and_user_initial.txt", + "subject": "signup received", }, - 'token': { - 'template': 'create_project_and_user_token.txt', - 'subject': 'signup approved' + "token": { + "template": "create_project_and_user_token.txt", + "subject": "signup approved", + }, + "completed": { + "template": "create_project_and_user_completed.txt", + "subject": "signup completed", }, - 'completed': { - 'template': 'create_project_and_user_completed.txt', - 'subject': 'signup completed' - } } diff --git a/adjutant/tasks/v1/resources.py b/adjutant/tasks/v1/resources.py index e9fe78e..7e1e913 100644 --- a/adjutant/tasks/v1/resources.py +++ b/adjutant/tasks/v1/resources.py @@ -22,10 +22,10 @@ class UpdateProjectQuotas(BaseTask): ] email_config = { - 'initial': None, - 'token': None, - 'completed': { - 'template': 'create_project_and_user_completed.txt', - 'subject': 'signup completed' - } + "initial": None, + "token": None, + "completed": { + "template": "create_project_and_user_completed.txt", + "subject": "signup completed", + }, } diff --git a/adjutant/tasks/v1/users.py b/adjutant/tasks/v1/users.py index 69e8a0e..4067448 100644 --- a/adjutant/tasks/v1/users.py +++ b/adjutant/tasks/v1/users.py @@ -18,84 +18,80 @@ from adjutant.tasks.v1.base import BaseTask class InviteUser(BaseTask): duplicate_policy = "block" task_type = "invite_user_to_project" - deprecated_task_types = ['invite_user'] + deprecated_task_types = ["invite_user"] default_actions = [ "NewUserAction", ] email_config = { - 'initial': None, - 'token': { - 'template': 'invite_user_to_project_token.txt', - 'subject': 'invite_user_to_project' + "initial": None, + "token": { + "template": "invite_user_to_project_token.txt", + "subject": "invite_user_to_project", + }, + "completed": { + "template": "invite_user_to_project_completed.txt", + "subject": "invite_user_to_project", }, - 'completed': { - 'template': 'invite_user_to_project_completed.txt', - 'subject': 'invite_user_to_project' - } } class ResetUserPassword(BaseTask): task_type = "reset_user_password" - deprecated_task_types = ['reset_password'] + deprecated_task_types = ["reset_password"] default_actions = [ "ResetUserPasswordAction", ] email_config = { - 'initial': None, - 'token': { - 'template': 'reset_user_password_token.txt', - 'subject': 'Password Reset for OpenStack' + "initial": None, + "token": { + "template": "reset_user_password_token.txt", + "subject": "Password Reset for OpenStack", + }, + "completed": { + "template": "reset_user_password_completed.txt", + "subject": "Password Reset for OpenStack", }, - 'completed': { - 'template': 'reset_user_password_completed.txt', - 'subject': 'Password Reset for OpenStack' - } } class EditUserRoles(BaseTask): task_type = "edit_user_roles" - deprecated_task_types = ['edit_user'] + deprecated_task_types = ["edit_user"] default_actions = [ "EditUserRolesAction", ] - email_config = { - 'initial': None, - 'token': None, - 'completed': None - } + email_config = {"initial": None, "token": None, "completed": None} class UpdateUserEmail(BaseTask): task_type = "update_user_email" - deprecated_task_types = ['update_email'] + deprecated_task_types = ["update_email"] default_actions = [ "UpdateUserEmailAction", ] additional_actions = [ - 'SendAdditionalEmailAction', + "SendAdditionalEmailAction", ] action_config = { - 'SendAdditionalEmailAction': { - 'initial': { - 'subject': 'OpenStack Email Update Requested', - 'template': 'update_user_email_started.txt', - 'email_current_user': True, + "SendAdditionalEmailAction": { + "initial": { + "subject": "OpenStack Email Update Requested", + "template": "update_user_email_started.txt", + "email_current_user": True, }, }, } email_config = { - 'initial': None, - 'token': { - 'subject': 'update_user_email_token', - 'template': 'update_user_email_token.txt' + "initial": None, + "token": { + "subject": "update_user_email_token", + "template": "update_user_email_token.txt", + }, + "completed": { + "subject": "Email Update Complete", + "template": "update_user_email_completed.txt", }, - 'completed': { - 'subject': 'Email Update Complete', - 'template': 'update_user_email_completed.txt' - } } diff --git a/adjutant/tasks/v1/utils.py b/adjutant/tasks/v1/utils.py index f1f2806..03ea273 100644 --- a/adjutant/tasks/v1/utils.py +++ b/adjutant/tasks/v1/utils.py @@ -27,18 +27,21 @@ from adjutant.config import CONF from adjutant import exceptions -LOG = getLogger('adjutant') +LOG = getLogger("adjutant") def handle_task_error(e, task, error_text="while running task"): import traceback - trace = traceback.format_exc() - LOG.critical(( - "(%s) - Exception escaped! %s\nTrace: \n%s") % ( - timezone.now(), e, trace)) - notes = ["Error: %s(%s) %s. See task itself for details." - % (type(e).__name__, e, error_text)] + trace = traceback.format_exc() + LOG.critical( + ("(%s) - Exception escaped! %s\nTrace: \n%s") % (timezone.now(), e, trace) + ) + + notes = [ + "Error: %s(%s) %s. See task itself for details." + % (type(e).__name__, e, error_text) + ] raise exceptions.TaskActionsFailed(task, internal_message=notes) @@ -49,11 +52,7 @@ def create_token(task, expiry_time=None): expire = timezone.now() + timedelta(seconds=expiry_time) uuid = uuid4().hex - token = Token.objects.create( - task=task, - token=uuid, - expires=expire - ) + token = Token.objects.create(task=task, token=uuid, expires=expire) token.save() return token @@ -63,13 +62,13 @@ def send_stage_email(task, email_conf, token=None): return text_template = loader.get_template( - email_conf['template'], - using='include_etc_templates') - html_template = email_conf['html_template'] + email_conf["template"], using="include_etc_templates" + ) + html_template = email_conf["html_template"] if html_template: html_template = loader.get_template( - html_template, - using='include_etc_templates') + html_template, using="include_etc_templates" + ) emails = set() actions = {} @@ -86,74 +85,62 @@ def send_stage_email(task, email_conf, token=None): if len(emails) > 1: notes = { - 'errors': - ("Error: Unable to send update, more than one email for task: %s" - % task.uuid) + "errors": ( + "Error: Unable to send update, more than one email for task: %s" + % task.uuid + ) } create_notification(task, notes, error=True) return - context = { - 'task': task, - 'actions': actions - } + context = {"task": task, "actions": actions} if token: tokenurl = CONF.workflow.horizon_url - if not tokenurl.endswith('/'): - tokenurl += '/' - tokenurl += 'token/' - context.update({ - 'tokenurl': tokenurl, - 'token': token.token - }) + if not tokenurl.endswith("/"): + tokenurl += "/" + tokenurl += "token/" + context.update({"tokenurl": tokenurl, "token": token.token}) try: message = text_template.render(context) # from_email is the return-path and is distinct from the # message headers - from_email = email_conf['from'] + from_email = email_conf["from"] if not from_email: - from_email = email_conf['reply'] + from_email = email_conf["reply"] elif "%(task_uuid)s" in from_email: - from_email = from_email % {'task_uuid': task.uuid} + from_email = from_email % {"task_uuid": task.uuid} # these are the message headers which will be visible to # the email client. headers = { - 'X-Adjutant-Task-UUID': task.uuid, + "X-Adjutant-Task-UUID": task.uuid, # From needs to be set to be disctinct from return-path - 'From': email_conf['reply'], - 'Reply-To': email_conf['reply'], + "From": email_conf["reply"], + "Reply-To": email_conf["reply"], } email = EmailMultiAlternatives( - email_conf['subject'], - message, - from_email, - [emails.pop()], - headers=headers, + email_conf["subject"], message, from_email, [emails.pop()], headers=headers, ) if html_template: - email.attach_alternative( - html_template.render(context), "text/html") + email.attach_alternative(html_template.render(context), "text/html") email.send(fail_silently=False) except Exception as e: notes = { - 'errors': - ("Error: '%s' while emailing update for task: %s" % - (e, task.uuid)) + "errors": ( + "Error: '%s' while emailing update for task: %s" % (e, task.uuid) + ) } notif_conf = task.config.notifications if e.__class__.__name__ in notif_conf.safe_errors: - notification = create_notification( - task, notes, error=True, - handlers=False) + notification = create_notification(task, notes, error=True, handlers=False) notification.acknowledged = True notification.save() else: diff --git a/adjutant/urls.py b/adjutant/urls.py index f412a0e..4dbd978 100644 --- a/adjutant/urls.py +++ b/adjutant/urls.py @@ -15,5 +15,5 @@ from django.conf.urls import include, url urlpatterns = [ - url(r'^', include('adjutant.api.urls')), + url(r"^", include("adjutant.api.urls")), ] diff --git a/adjutant/wsgi.py b/adjutant/wsgi.py index f84d207..90f8c4c 100644 --- a/adjutant/wsgi.py +++ b/adjutant/wsgi.py @@ -38,14 +38,14 @@ application = get_wsgi_application() # the Keystone Auth Middleware. conf = { "auth_plugin": "password", - 'username': CONF.identity.auth.username, - 'password': CONF.identity.auth.password, - 'project_name': CONF.identity.auth.project_name, + "username": CONF.identity.auth.username, + "password": CONF.identity.auth.password, + "project_name": CONF.identity.auth.project_name, "project_domain_id": CONF.identity.auth.project_domain_id, "user_domain_id": CONF.identity.auth.user_domain_id, "auth_url": CONF.identity.auth.auth_url, - 'delay_auth_decision': True, - 'include_service_catalog': False, - 'token_cache_time': CONF.identity.token_cache_time, + "delay_auth_decision": True, + "include_service_catalog": False, + "token_cache_time": CONF.identity.token_cache_time, } application = AuthProtocol(application, conf) diff --git a/api-ref/source/conf.py b/api-ref/source/conf.py index e250f78..c909028 100644 --- a/api-ref/source/conf.py +++ b/api-ref/source/conf.py @@ -29,12 +29,9 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ - 'os_api_ref', - 'openstackdocstheme' -] +extensions = ["os_api_ref", "openstackdocstheme"] -html_theme = 'openstackdocstheme' +html_theme = "openstackdocstheme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -42,37 +39,37 @@ html_theme = 'openstackdocstheme' # html_theme_options = {} # openstackdocstheme settings -repository_name = 'openstack/adjutant' -html_theme = 'openstackdocs' +repository_name = "openstack/adjutant" +html_theme = "openstackdocs" use_storyboard = True # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Adjutant API Reference' -copyright = u'2017, Catalyst IT Ltd' +project = "Adjutant API Reference" +copyright = "2017, Catalyst IT Ltd" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '' +version = "" # The full version, including alpha/beta/rc tags. -release = '' +release = "" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -86,7 +83,7 @@ release = '' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -104,7 +101,7 @@ exclude_patterns = ['_build'] # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -195,7 +192,7 @@ pygments_style = 'sphinx' # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'AdjutantAPIReferencedoc' +htmlhelp_basename = "AdjutantAPIReferencedoc" # -- Options for LaTeX output --------------------------------------------- @@ -215,9 +212,13 @@ htmlhelp_basename = 'AdjutantAPIReferencedoc' # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'AdjutantAPIReference.tex', - u'Adjutant API Reference Documentation', - u'Catalyst IT Ltd', 'manual'), + ( + "index", + "AdjutantAPIReference.tex", + "Adjutant API Reference Documentation", + "Catalyst IT Ltd", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -246,8 +247,13 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'adjutantapireference', u'Adjutant API Reference Documentation', - [u'Catalyst IT Ltd'], 1) + ( + "index", + "adjutantapireference", + "Adjutant API Reference Documentation", + ["Catalyst IT Ltd"], + 1, + ) ] # If true, show URL addresses after external links. @@ -260,12 +266,17 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'AdjutantAPIReference', u'Adjutant API Reference Documentation', - u'Catalyst IT Ltd', 'AdjutantAPIReference', - 'A simple workflow framework to help automate admin and user tasks in ' - 'and around OpenStack via a pluggable API exposing tasks made up of ' - 'easily chainable actions.', - 'Miscellaneous'), + ( + "index", + "AdjutantAPIReference", + "Adjutant API Reference Documentation", + "Catalyst IT Ltd", + "AdjutantAPIReference", + "A simple workflow framework to help automate admin and user tasks in " + "and around OpenStack via a pluggable API exposing tasks made up of " + "easily chainable actions.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. @@ -282,4 +293,4 @@ texinfo_documents = [ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} +intersphinx_mapping = {"http://docs.python.org/": None} diff --git a/doc/source/conf.py b/doc/source/conf.py index d457cf0..e300f00 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -30,30 +30,28 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ - 'openstackdocstheme' -] +extensions = ["openstackdocstheme"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # openstackdocstheme settings -repository_name = 'openstack/adjutant' -html_theme = 'openstackdocs' +repository_name = "openstack/adjutant" +html_theme = "openstackdocs" use_storyboard = True # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Adjutant' -copyright = u'2017, Catalyst IT Ltd' +project = "Adjutant" +copyright = "2017, Catalyst IT Ltd" # List of patterns, relative to source directory, that match files and @@ -62,7 +60,7 @@ copyright = u'2017, Catalyst IT Ltd' exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -93,7 +91,7 @@ todo_include_todos = False # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = 'Adjutantdoc' +htmlhelp_basename = "Adjutantdoc" # -- Options for LaTeX output --------------------------------------------- @@ -102,8 +100,7 @@ htmlhelp_basename = 'Adjutantdoc' # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'Adjutant.tex', u'Adjutant Documentation', - u'Catalyst IT Ltd', 'manual'), + (master_doc, "Adjutant.tex", "Adjutant Documentation", "Catalyst IT Ltd", "manual"), ] @@ -111,10 +108,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'adjutant', u'Adjutant Documentation', - ['Catalyst IT Ltd'], 1) -] +man_pages = [(master_doc, "adjutant", "Adjutant Documentation", ["Catalyst IT Ltd"], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -123,7 +117,13 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'Adjutant', u'Adjutant Documentation', - 'Catalyst IT Ltd', 'Adjutant', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "Adjutant", + "Adjutant Documentation", + "Catalyst IT Ltd", + "Adjutant", + "One line description of project.", + "Miscellaneous", + ), ] diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index dd6e055..911e2a7 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -34,37 +34,37 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'openstackdocstheme', - 'reno.sphinxext', + "openstackdocstheme", + "reno.sphinxext", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Adjutant Release Notes' -copyright = u'2019, Adjutant Developers' +project = "Adjutant Release Notes" +copyright = "2019, Adjutant Developers" # openstackdocstheme settings -repository_name = 'openstack/adjutant' +repository_name = "openstack/adjutant" use_storyboard = True # Release notes are version independent # The full version, including alpha/beta/rc tags. -release = '' +release = "" # The short X.Y version. -version = '' +version = "" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -96,7 +96,7 @@ exclude_patterns = [] # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -109,7 +109,7 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'openstackdocs' +html_theme = "openstackdocs" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -188,7 +188,7 @@ html_theme = 'openstackdocs' # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'AdjutantReleaseNotesdoc' +htmlhelp_basename = "AdjutantReleaseNotesdoc" # -- Options for LaTeX output --------------------------------------------- @@ -197,9 +197,13 @@ htmlhelp_basename = 'AdjutantReleaseNotesdoc' # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'AdjutantReleaseNotes.tex', - u'Adjutant Release Notes Documentation', - u'Adjutant Developers', 'manual'), + ( + "index", + "AdjutantReleaseNotes.tex", + "Adjutant Release Notes Documentation", + "Adjutant Developers", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -228,8 +232,13 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'adjutantreleasenotes', u'Adjutant Release Notes Documentation', - [u'Adjutant Developers'], 1) + ( + "index", + "adjutantreleasenotes", + "Adjutant Release Notes Documentation", + ["Adjutant Developers"], + 1, + ) ] # If true, show URL addresses after external links. @@ -242,10 +251,15 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'AdjutantReleaseNotes', u'Adjutant Release Notes Documentation', - u'Adjutant Developers', 'AdjutantReleaseNotes', - 'An extensible API framework for admin logic in OpenStack.', - 'Miscellaneous'), + ( + "index", + "AdjutantReleaseNotes", + "Adjutant Release Notes Documentation", + "Adjutant Developers", + "AdjutantReleaseNotes", + "An extensible API framework for admin logic in OpenStack.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. @@ -261,4 +275,4 @@ texinfo_documents = [ # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ -locale_dirs = ['locale/'] +locale_dirs = ["locale/"] diff --git a/setup.py b/setup.py index 14c18b8..6e30c43 100644 --- a/setup.py +++ b/setup.py @@ -15,6 +15,5 @@ from setuptools import setup setup( - setup_requires=['pbr'], - pbr=True, + setup_requires=["pbr"], pbr=True, ) diff --git a/tox.ini b/tox.ini index 51275a9..e5a78d4 100644 --- a/tox.ini +++ b/tox.ini @@ -52,7 +52,7 @@ commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenote [flake8] max-line-length = 88 select = C,E,F,W,B,B950 -ignore = D100,D101,D102,D103,D104,D105,D200,D203,D202,D204,D205,D208,D400,D401,W503,E501 +ignore = D100,D101,D102,D103,D104,D105,D200,D203,D202,D204,D205,D208,D400,D401,W503,E231,E501 show-source = true builtins = _ exclude=.venv,venv,.env,env,.git,.tox,dist,doc,*lib/python*,*egg,releasenotes,adjutant/api/migrations/*,adjutant/actions/migrations,adjutant/tasks/migrations