Exemplo n.º 1
0
    def _handle_task_error(self,
                           e,
                           task,
                           error_text="while running task",
                           return_response=False):
        import traceback
        trace = traceback.format_exc()
        self.logger.critical(("(%s) - Exception escaped! %s\nTrace: \n%s") %
                             (timezone.now(), e, trace))
        notes = {
            'errors': [
                "Error: %s(%s) %s. See task itself for details." %
                (type(e).__name__, e, error_text)
            ]
        }
        create_notification(task, notes, error=True)

        response_dict = {
            'errors': [
                "Error: Something went wrong on the server. "
                "It will be looked into shortly."
            ]
        }
        if return_response:
            return Response(response_dict, status=500)
        return response_dict, 500
Exemplo n.º 2
0
    def post(self, request, format=None):
        """
        Unauthenticated endpoint bound primarily to NewProjectWithUser.

        This process requires approval, so this will validate
        incoming data and create a task to be approved
        later.
        """
        self.logger.info("(%s) - Starting new project task." % timezone.now())

        class_conf = settings.TASK_SETTINGS.get(self.task_type, {})

        # we need to set the region the resources will be created in:
        request.data['region'] = class_conf.get('default_region')

        # parent_id for new project, if null defaults to domain:
        request.data['parent_id'] = class_conf.get('default_parent_id')

        processed, status = self.process_actions(request)

        errors = processed.get('errors', None)
        if errors:
            self.logger.info("(%s) - Validation errors with task." %
                             timezone.now())
            return Response(errors, status=status)

        notes = {'notes': ['New task for CreateProject.']}
        create_notification(processed['task'], notes)
        self.logger.info("(%s) - Task created." % timezone.now())

        response_dict = {'notes': ['task created']}

        add_task_id_for_roles(request, processed, response_dict, ['admin'])

        return Response(response_dict, status=status)
Exemplo n.º 3
0
    def _create_token(self, task):
        token = create_token(task)
        try:
            class_conf = settings.TASK_SETTINGS.get(
                self.task_type, settings.DEFAULT_TASK_SETTINGS)

            # will throw a key error if the token template has not
            # been specified
            email_conf = class_conf['emails']['token']
            send_stage_email(task, email_conf, token)
            return {'notes': ['created token']}, 200
        except KeyError as e:
            import traceback
            trace = traceback.format_exc()
            self.logger.critical(
                ("(%s) - Exception escaped! %s\nTrace: \n%s") %
                (timezone.now(), e, trace))
            notes = {
                'errors': [("Error: '%s' while sending " + "token. See task " +
                            "itself for details.") % e]
            }
            create_notification(task, notes, error=True)

            response_dict = {
                'errors': [
                    "Error: Something went wrong on the " +
                    "server. It will be looked into shortly."
                ]
            }
            return response_dict, 500
Exemplo n.º 4
0
    def post(self, request):

        request.data['project_id'] = request.keystone_user['project_id']
        self.project_id = request.keystone_user['project_id']

        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

        self.logger.info("(%s) - New UpdateProjectQuotas request." %
                         timezone.now())

        processed, status = self.process_actions(request)

        # check the status
        errors = processed.get('errors', None)
        if errors:
            self.logger.info("(%s) - Validation errors with task." %
                             timezone.now())
            return Response({'errors': errors}, status=status)

        if processed.get('auto_approved', False):
            response_dict = {'notes': processed['notes']}
            return Response(response_dict, status=status)

        task = processed['task']
        action_models = task.actions
        valid = all([act.valid for act in action_models])
        if not valid:
            return Response(
                {
                    'errors': [
                        'Actions invalid. You may have usage '
                        'above the new quota level.'
                    ]
                }, 400)

        # Action needs to be manually approved
        notes = {'notes': ['New task for UpdateProjectQuotas.']}

        create_notification(processed['task'], notes)
        self.logger.info("(%s) - Task processed. Awaiting Aprroval" %
                         timezone.now())

        response_dict = {'notes': ['Task processed. Awaiting Aprroval.']}

        return Response(response_dict, status=202)
Exemplo n.º 5
0
    def post(self, request):
        """ Update Account Details """
        self.logger.info("(%s) - Starting new AccountDetailsManageTask task." %
                         timezone.now())

        request.data['project_id'] = request.keystone_user['project_id']

        processed, status = self.process_actions(request)
        errors = processed.get('errors', None)
        if errors:
            self.logger.info("(%s) - Validation errors with task." %
                             timezone.now())
            return Response(errors, status=status)

        # cancel any old tasks
        project_tasks = Task.objects.filter(
            project_id=request.keystone_user['project_id'],
            task_type=self.task_type,
            completed=0,
            cancelled=0).exclude(uuid=processed['task'].uuid)

        for task in project_tasks:
            task.cancelled = True
            task.save()

        if processed.get('auto_approved', False):
            response_dict = {'notes': ['Updated Account Address']}
            return Response(response_dict, status=status)

        task = processed['task']
        action_models = task.actions
        valid = all([act.valid for act in action_models])
        if not valid:
            return Response({'errors': ['Actions invalid.']}, 400)

        # Action needs to be manually approved
        notes = {'notes': ['New task for AddressManagement.']}

        create_notification(processed['task'], notes)
        self.logger.info("(%s) - Task processed. Awaiting Aprroval" %
                         timezone.now())

        response_dict = {'notes': ['Task submitted.', 'Awaiting Approval.']}

        return Response(response_dict, status=202)
Exemplo n.º 6
0
    def post(self, request, format=None):
        """
        Unauthenticated endpoint bound primarily to NewClientSignUp
        and NewProjectSignUp.

        This task requires approval, so this will validate
        incoming data and create a task to be approved
        later.
        """
        self.logger.info("(%s) - Starting new OpenStackSignUp task." %
                         timezone.now())

        class_conf = settings.TASK_SETTINGS.get(self.task_type, {})

        # we need to set the region the resources will be created in:
        request.data['region'] = class_conf.get('default_region')
        # Will a default network be setup:
        request.data['setup_network'] = class_conf.get('setup_network', False)
        # domain_id for new project:
        request.data['domain_id'] = class_conf.get(
            'default_domain_id', 'default')
        # parent_id for new project, if null defaults to domain:
        request.data['parent_id'] = class_conf.get('default_parent_id')

        processed, status = self.process_actions(request)

        errors = processed.get('errors', None)
        if errors:
            self.logger.info("(%s) - Validation errors with task." %
                             timezone.now())
            return Response(errors, status=status)

        notes = {
            'notes':
                ['New OpenStackSignUp task.']
        }
        create_notification(processed['task'], notes)
        self.logger.info("(%s) - Task created." % timezone.now())

        response_dict = {'notes': ['Sign-up submitted.']}

        add_task_id_for_roles(request, processed, response_dict, ['admin'])

        return Response(response_dict, status=status)
Exemplo n.º 7
0
    def _get_parent_id(self):
        """Get ID of the owner (company) for the project

        Expects:
            - self.project_id
            - self.odoo_project_id
        Sets:
            - self.odoo_owner
        """
        if getattr(self, 'odoo_owner', None):
            return self.odoo_owner.id

        odooclient = odoo_client.get_odoo_client()
        search = [['cloud_tenant', '=', self.odoo_project_id],
                  ['contact_type', '=', 'owner']]
        all_relations = odooclient.project_relationships.list(search)
        if len(all_relations) > 1:
            note = ("WARNING! More than one owner found for '%s'" %
                    self.project_id)
            self.add_note(note)
            if not self.get_cache('multi_owner_error'):
                create_notification(self.action.task, {'errors': [note]},
                                    error=True)
                self.set_cache('multi_owner_error', True)

        if len(all_relations) < 1:
            note = ("WARNING! No owner found for '%s'" % self.project_id)
            self.add_note(note)
            if not self.get_cache('no_owner_error'):
                create_notification(self.action.task, {'errors': [note]},
                                    error=True)
                self.set_cache('no_owner_error', True)
            return None

        self.odoo_owner = all_relations[0].partner_id
        self.add_note("Found owner: %s" % self.odoo_owner.name)
        return self.odoo_owner.id
Exemplo n.º 8
0
    def post(self, request, id, format=None):
        """
        Ensures the required fields are present,
        will then pass those to the actions via the submit
        function.
        """
        try:
            token = Token.objects.get(token=id)
            if token.expires < timezone.now():
                token.delete()
                token = Token.objects.get(token=id)
        except Token.DoesNotExist:
            return Response(
                {'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)

        if token.task.cancelled:
            return Response({'errors': ['This task has been cancelled.']},
                            status=400)

        if token.expires < timezone.now():
            token.delete()
            return Response({'errors': ['This token has expired.']},
                            status=400)

        required_fields = set()
        actions = []
        for action in token.task.actions:
            a = action.get_action()
            actions.append(a)
            for field in a.token_fields:
                required_fields.add(field)

        errors = {}
        data = {}

        for field in required_fields:
            try:
                data[field] = request.data[field]
            except KeyError:
                errors[field] = [
                    "This field is required.",
                ]
            except TypeError:
                errors = [
                    "Improperly formated json. " +
                    "Should be a key-value object.",
                ]
                break

        if errors:
            return Response({"errors": errors}, status=400)

        for action in actions:
            try:
                action.submit(data)
            except Exception as e:
                notes = {
                    'errors': [("Error: '%s' while submitting task. " +
                                "See task itself for details.") % e],
                    'task':
                    token.task.uuid
                }
                create_notification(token.task, notes)

                import traceback
                trace = traceback.format_exc()
                self.logger.critical(
                    ("(%s) - Exception escaped! %s\n" + "Trace: \n%s") %
                    (timezone.now(), e, trace))

                response_dict = {
                    'errors': [
                        "Error: Something went wrong on the server. " +
                        "It will be looked into shortly."
                    ]
                }
                return Response(response_dict, status=500)

        token.task.completed = True
        token.task.completed_on = timezone.now()
        token.task.save()
        token.delete()

        # Sending confirmation email:
        class_conf = settings.TASK_SETTINGS.get(token.task.task_type,
                                                settings.DEFAULT_TASK_SETTINGS)
        email_conf = class_conf.get('emails', {}).get('completed', None)
        send_stage_email(token.task, email_conf)

        return Response({'notes': ["Token submitted successfully."]},
                        status=200)
Exemplo n.º 9
0
    def post(self, request, format=None):
        """
        Reissue a token for an approved task.

        Clears other tokens for it.
        """
        uuid = request.data.get('task', None)
        if uuid is None:
            return Response({'task': [
                "This field is required.",
            ]},
                            status=400)
        try:
            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'])
        except Task.DoesNotExist:
            return Response({'errors': ['No task with this id.']}, status=404)

        if task.completed:
            return Response(
                {'errors': ['This task has already been completed.']},
                status=400)

        if task.cancelled:
            return Response({'errors': ['This task has been cancelled.']},
                            status=400)

        if not task.approved:
            return Response({'errors': ['This task has not been approved.']},
                            status=400)

        for token in task.tokens:
            token.delete()

        token = create_token(task)
        try:
            class_conf = settings.TASK_SETTINGS.get(
                task.task_type, settings.DEFAULT_TASK_SETTINGS)

            # will throw a key error if the token template has not
            # been specified
            email_conf = class_conf['emails']['token']
            send_stage_email(task, email_conf, token)
        except KeyError as e:
            notes = {
                'errors': [("Error: '%(error)s' while sending token. " +
                            "See registration itself for details.") % {
                                'error': e
                            }],
                'task':
                task.uuid
            }
            create_notification(task, notes)

            import traceback
            trace = traceback.format_exc()
            self.logger.critical(
                ("(%s) - Exception escaped!" + " %s\n Trace: \n%s") %
                (timezone.now(), e, trace))

            response_dict = {
                'errors': [
                    "Error: Something went wrong on the " +
                    "server. It will be looked into shortly."
                ]
            }
            return Response(response_dict, status=500)
        return Response({'notes': ['Token reissued.']}, status=200)
Exemplo n.º 10
0
    def post(self, request, uuid, format=None):
        """
        Will approve the Task specified,
        followed by running the post_approve actions
        and if valid will setup and create a related token.
        """
        try:
            task = Task.objects.get(uuid=uuid)
        except Task.DoesNotExist:
            return Response({'errors': ['No task with this id.']}, status=404)

        try:
            if request.data.get('approved') is not True:
                return Response(
                    {'approved': ["this is a required boolean field."]},
                    status=400)
        except ParseError:
            return Response(
                {'approved': ["this is a required boolean field."]},
                status=400)

        if task.completed:
            return Response(
                {'errors': ['This task has already been completed.']},
                status=400)

        if task.cancelled:
            return Response({'errors': ['This task has been cancelled.']},
                            status=400)

        # we check that the task is valid before approving it:
        valid = True
        for action in task.actions:
            if not action.valid:
                valid = False

        if not valid:
            return Response(
                {
                    'errors': [
                        'Cannot approve an invalid task. ' +
                        'Update data and rerun pre_approve.'
                    ]
                },
                status=400)

        # We approve the task before running actions,
        # that way if something goes wrong we know if it was approved,
        # when it was approved, and who approved it last. Subsequent
        # reapproval attempts overwrite previous approved_by/on.
        task.approved = True
        task.approved_by = request.keystone_user
        task.approved_on = timezone.now()
        task.save()

        need_token = False
        valid = True

        actions = []

        for action in task.actions:
            act_model = action.get_action()
            actions.append(act_model)
            try:
                act_model.post_approve()
            except Exception as e:
                notes = {
                    'errors': [("Error: '%s' while approving task. " +
                                "See task itself for details.") % e],
                    'task':
                    task.uuid
                }
                create_notification(task, notes)

                import traceback
                trace = traceback.format_exc()
                self.logger.critical(
                    ("(%s) - Exception escaped! %s\n" + "Trace: \n%s") %
                    (timezone.now(), e, trace))

                return Response(notes, status=500)

            if not action.valid:
                valid = False
            if action.need_token:
                need_token = True

        if valid:
            if need_token:
                token = create_token(task)
                try:
                    class_conf = settings.TASK_SETTINGS.get(
                        task.task_type, settings.DEFAULT_TASK_SETTINGS)

                    # will throw a key error if the token template has not
                    # been specified
                    email_conf = class_conf['emails']['token']
                    send_stage_email(task, email_conf, token)
                    return Response({'notes': ['created token']}, status=200)
                except KeyError as e:
                    notes = {
                        'errors':
                        [("Error: '%s' while sending " + "token. See task " +
                          "itself for details.") % e],
                        'task':
                        task.uuid
                    }
                    create_notification(task, notes)

                    import traceback
                    trace = traceback.format_exc()
                    self.logger.critical(
                        ("(%s) - Exception escaped!" + " %s\n Trace: \n%s") %
                        (timezone.now(), e, trace))

                    response_dict = {
                        'errors': [
                            "Error: Something went wrong on the " +
                            "server. It will be looked into shortly."
                        ]
                    }
                    return Response(response_dict, status=500)
            else:
                for action in actions:
                    try:
                        action.submit({})
                    except Exception as e:
                        notes = {
                            'errors':
                            [("Error: '%s' while submitting " +
                              "task. See task " + "itself for details.") % e],
                            'task':
                            task.uuid
                        }
                        create_notification(task, notes)

                        import traceback
                        trace = traceback.format_exc()
                        self.logger.critical(
                            ("(%s) - Exception escaped!" + " %s\n Trace: \n%s")
                            % (timezone.now(), e, trace))

                        return Response(notes, status=500)

                task.completed = True
                task.completed_on = timezone.now()
                task.save()

                # Sending confirmation email:
                class_conf = settings.TASK_SETTINGS.get(
                    task.task_type, settings.DEFAULT_TASK_SETTINGS)
                email_conf = class_conf.get('emails',
                                            {}).get('completed', None)
                send_stage_email(task, email_conf)

                return Response({'notes': ["Task completed successfully."]},
                                status=200)
        return Response({'errors': ['actions invalid']}, status=400)
Exemplo n.º 11
0
    def put(self, request, uuid, format=None):
        """
        Allows the updating of action data and retriggering
        of the pre_approve step.
        """
        try:
            task = Task.objects.get(uuid=uuid)
        except Task.DoesNotExist:
            return Response({'errors': ['No task with this id.']}, status=404)

        if task.completed:
            return Response(
                {'errors': ['This task has already been completed.']},
                status=400)

        if task.cancelled:
            # NOTE(adriant): If we can uncancel a task, that should happen
            # at this endpoint.
            return Response({'errors': ['This task has been cancelled.']},
                            status=400)

        if task.approved:
            return Response(
                {'errors': ['This task has already been approved.']},
                status=400)

        act_list = []

        valid = True
        for action in task.actions:
            action_serializer = settings.ACTION_CLASSES[action.action_name][1]

            if action_serializer is not None:
                serializer = action_serializer(data=request.data)
            else:
                serializer = None

            act_list.append({
                'name': action.action_name,
                'action': action,
                'serializer': serializer
            })

            if serializer is not None and not serializer.is_valid():
                valid = False

        if valid:
            for act in act_list:
                if act['serializer'] is not None:
                    data = act['serializer'].validated_data
                else:
                    data = {}
                act['action'].action_data = data
                act['action'].save()

                try:
                    act['action'].get_action().pre_approve()
                except Exception as e:
                    notes = {
                        'errors': [("Error: '%s' while updating task. " +
                                    "See task itself for details.") % e],
                        'task':
                        task.uuid
                    }
                    create_notification(task, notes)

                    import traceback
                    trace = traceback.format_exc()
                    self.logger.critical(
                        ("(%s) - Exception escaped! %s\n" + "Trace: \n%s") %
                        (timezone.now(), e, trace))

                    response_dict = {
                        'errors': [
                            "Error: Something went wrong on the server. " +
                            "It will be looked into shortly."
                        ]
                    }
                    return Response(response_dict, status=500)

            return Response({'notes': ["Task successfully updated."]},
                            status=200)
        else:
            errors = {}
            for act in act_list:
                if act['serializer'] is not None:
                    errors.update(act['serializer'].errors)
            return Response({'errors': errors}, status=400)
Exemplo n.º 12
0
    def approve(self, request, task):
        """
        Approves the task and runs the post_approve steps.
        Will create a token if required, otherwise will run the
        submit steps.
        """

        # We approve the task before running actions,
        # that way if something goes wrong we know if it was approved,
        # when it was approved, and who approved it.
        task.approved = True
        task.approved_on = timezone.now()
        task.approved_by = request.keystone_user
        task.save()

        action_models = task.actions
        actions = [act.get_action() for act in action_models]
        need_token = False

        valid = all([act.valid for act in actions])
        if not valid:
            return {'errors': ['actions invalid']}, 400

        # post_approve all actions
        for action in actions:
            try:
                action.post_approve()
            except Exception as e:
                import traceback
                trace = traceback.format_exc()
                self.logger.critical(
                    ("(%s) - Exception escaped! %s\nTrace: \n%s") %
                    (timezone.now(), e, trace))
                notes = {
                    'errors': [("Error: '%s' while approving task. " +
                                "See task itself for details.") % e]
                }
                create_notification(task, notes, error=True)

                response_dict = {
                    'errors': [
                        "Error: Something went wrong on the server. " +
                        "It will be looked into shortly."
                    ]
                }
                return response_dict, 500

        valid = all([act.valid for act in actions])
        if not valid:
            return {'errors': ['actions invalid']}, 400

        need_token = any([act.need_token for act in actions])
        if need_token:
            return self._create_token(task)

        # submit all actions
        for action in actions:
            try:
                action.submit({})
            except Exception as e:
                import traceback
                trace = traceback.format_exc()
                self.logger.critical(
                    ("(%s) - Exception escaped! %s\nTrace: \n%s") %
                    (timezone.now(), e, trace))
                notes = {
                    'errors': [("Error: '%s' while submitting " +
                                "task. See task " + "itself for details.") % e]
                }
                create_notification(task, notes, error=True)

                response_dict = {
                    'errors': [
                        "Error: Something went wrong on the " +
                        "server. It will be looked into shortly."
                    ]
                }
                return response_dict, 500

        task.completed = True
        task.completed_on = timezone.now()
        task.save()

        # Sending confirmation email:
        class_conf = settings.TASK_SETTINGS.get(self.task_type,
                                                settings.DEFAULT_TASK_SETTINGS)
        email_conf = class_conf.get('emails', {}).get('completed', None)
        send_stage_email(task, email_conf)
        return {'notes': ["Task completed successfully."]}, 200
Exemplo n.º 13
0
    def process_actions(self, request):
        """
        Will ensure the request data contains the required data
        based on the action serializer, and if present will create
        a Task and the linked actions, attaching notes
        based on running of the the pre_approve validation
        function on all the actions.

        If during the pre_approve step at least one of the actions
        sets auto_approve to True, and none of them set it to False
        the approval steps will also be run.
        """
        class_conf = settings.TASK_SETTINGS.get(self.task_type,
                                                settings.DEFAULT_TASK_SETTINGS)

        # Action serializers
        action_serializer_list = self._instantiate_action_serializers(
            request, class_conf)

        if isinstance(action_serializer_list, tuple):
            return action_serializer_list

        hash_key = create_task_hash(self.task_type, action_serializer_list)

        # Handle duplicates
        duplicate_error = self._handle_duplicates(class_conf, hash_key)
        if duplicate_error:
            return duplicate_error

        # Instantiate Task
        ip_address = request.META['REMOTE_ADDR']
        keystone_user = request.keystone_user
        try:
            task = Task.objects.create(ip_address=ip_address,
                                       keystone_user=keystone_user,
                                       project_id=keystone_user['project_id'],
                                       task_type=self.task_type,
                                       hash_key=hash_key)
        except KeyError:
            task = Task.objects.create(ip_address=ip_address,
                                       keystone_user=keystone_user,
                                       task_type=self.task_type,
                                       hash_key=hash_key)
        task.save()

        # Instantiate actions with serializers
        for i, action in enumerate(action_serializer_list):
            data = action['serializer'].validated_data

            # construct the action class
            action_instance = action['action'](data=data, task=task, order=i)

            try:
                action_instance.pre_approve()
            except Exception as e:
                import traceback
                trace = traceback.format_exc()
                self.logger.critical(
                    ("(%s) - Exception escaped! %s\nTrace: \n%s") %
                    (timezone.now(), e, trace))
                notes = {
                    'errors': [("Error: '%s' while setting up task. " +
                                "See task itself for details.") % e]
                }
                create_notification(task, notes, error=True)

                response_dict = {
                    'errors': [
                        "Error: Something went wrong on the server. " +
                        "It will be looked into shortly."
                    ]
                }
                return response_dict, 200

        # send initial confirmation email:
        email_conf = class_conf.get('emails', {}).get('initial', None)
        send_stage_email(task, email_conf)

        action_models = task.actions
        approve_list = [act.get_action().auto_approve for act in action_models]

        # TODO(amelia): It would be nice to explicitly test this, however
        #               currently we don't have the right combinations of
        #               actions to allow for it.
        if False in approve_list:
            can_auto_approve = False
        elif True in approve_list:
            can_auto_approve = True
        else:
            can_auto_approve = False

        if can_auto_approve:
            task_name = self.__class__.__name__
            self.logger.info("(%s) - AutoApproving %s request." %
                             (timezone.now(), task_name))
            approval_data, status = self.approve(request, task)
            # Additional information that would be otherwise expected
            approval_data['task'] = task
            approval_data['auto_approved'] = True
            return approval_data, status

        return {'task': task}, 200
Exemplo n.º 14
0
def send_email(to_addresses, context, conf, task):
    """
    Function for sending emails from actions
    """

    if not conf.get('template'):
        return

    if not to_addresses:
        return
    if isinstance(to_addresses, six.string_types):
        to_addresses = [to_addresses]
    elif isinstance(to_addresses, set):
        to_addresses = list(to_addresses)

    text_template = loader.get_template(conf['template'],
                                        using='include_etc_templates')

    html_template = conf.get('html_template', None)
    if html_template:
        html_template = loader.get_template(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')
        if not from_email:
            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}

        reply_email = conf['reply']
        # these are the message headers which will be visible to
        # the email client.
        headers = {
            'X-Adjutant-Task-UUID': task.uuid,
            # From needs to be set to be distinct from return-path
            'From': reply_email,
            'Reply-To': reply_email,
        }

        email = EmailMultiAlternatives(
            conf['subject'],
            message,
            from_email,
            to_addresses,
            headers=headers,
        )

        if html_template:
            email.attach_alternative(html_template.render(context),
                                     "text/html")

        email.send(fail_silently=False)
        return True

    except SMTPException as e:
        notes = {
            'errors':
            ("Error: '%s' while sending additional email for task: %s" %
             (e, task.uuid))
        }

        errors_conf = settings.TASK_SETTINGS.get(
            task.task_type,
            settings.DEFAULT_TASK_SETTINGS).get('errors',
                                                {}).get("SMTPException", {})

        if errors_conf:
            notification = create_notification(task,
                                               notes,
                                               error=True,
                                               engines=errors_conf.get(
                                                   'engines', True))

            if errors_conf.get('notification') == "acknowledge":
                notification.acknowledged = True
                notification.save()
        else:
            create_notification(task, notes, error=True)

        return False