def schedule_transition():
    def decorator(view_fn):
        @functools.wraps(view_fn)
        def wrapped(self, request, *args, **kwargs):
            supported_operations = {
                # code: (scheduled_celery_task, instance_marker_state)
                'start': ('schedule_starting', tasks.schedule_starting),
                'stop': ('schedule_stopping', tasks.schedule_stopping),
                'restart': ('schedule_restarting', tasks.schedule_restarting),
                'destroy': ('schedule_deletion', tasks.schedule_deleting),
                'flavor change': ('schedule_resizing', tasks.resize_flavor),
                'disk extension': ('schedule_resizing', tasks.extend_disk),
            }

            # Define them in inner scope but call when transaction complete
            response, processing_task, logger_info = None, None, None

            try:
                with transaction.atomic():
                    instance = self.get_object()

                    membership = instance.cloud_project_membership
                    is_admin = membership.project.has_user(request.user, ProjectRole.ADMINISTRATOR)

                    if not is_admin and not request.user.is_staff:
                        raise exceptions.PermissionDenied()

                    # Important! We are passing back the instance from current transaction to a view
                    options = view_fn(self, request, instance, *args, **kwargs)

                    if isinstance(options, tuple):
                        # Expecting operation, logger_info and optional celery_kwargs from a view
                        operation, logger_info = options[:2]
                        celery_kwargs = options[2] if len(options) >= 3 else {}
                        change_instance_state, processing_task = supported_operations[operation]

                        transition = getattr(instance, change_instance_state)
                        transition()

                        instance.save(update_fields=['state'])
                    else:
                        # Break execution by return from a view
                        response = options
                        raise RuntimeError

            except TransitionNotAllowed:
                message = "Performing %s operation from instance state '%s' is not allowed"
                return Response({'status': message % (operation, instance.get_state_display())},
                                status=status.HTTP_409_CONFLICT)

            except IntegrityError:
                return Response({'status': '%s was not scheduled' % operation},
                                status=status.HTTP_400_BAD_REQUEST)

            except RuntimeError:
                assert isinstance(response, Response)
                return response

            else:
                # Call celery task AFTER transaction has been commited
                processing_task.delay(instance.uuid.hex, **celery_kwargs)
                if logger_info is not None:
                    event_logger.instance.info(
                        logger_info['message'],
                        event_type=logger_info['event_type'],
                        event_context=logger_info['event_context'])

            return Response({'status': operation % '%s was scheduled'},
                            status=status.HTTP_202_ACCEPTED)

        return wrapped
    return decorator
dataset/ETHPy150Open opennode/nodeconductor/nodeconductor/iaas/views.py/schedule_transition/swapped operands of `%`