Пример #1
0
class DjangoResource:
    """
    Same as SimpleResource but django-backed
    """
    state = ModelResourceStateDescriptor(
        state_classes=(State1, State2, State3),
        default_state=State1,
        model_field_name='backing_field',
    )

    backing_field = models.CharField(max_length=100, choices=state.model_field_choices)

    # Define some transitions:
    done_one = state.transition(from_states=State1, to_state=State2)
    done_two = state.transition(from_states=State2, to_state=State3)
    reset_to_one = state.transition(from_states=(State2, State3), to_state=State1)
    reset_to_one_alt = state.transition(from_states=BaseState, to_state=State1)

    return_value = True  # Change this to change the expected return value of most of these methods.

    @state.only_for(State1)
    def method_one(self):
        """ A method that only can be called in state 1 """
        return self.return_value

    @state.only_for(State1)
    def method_one_with_args(self, a, b, c):  # pylint: disable=no-self-use,invalid-name
        """ A method that only can be called in state 1 """
        return (a * 1) + (b * 2) + (c * 3)

    @state.only_for(State2)
    def method_two(self):
        """ A method that only can be called in state 2 """
        return self.return_value

    @state.only_for(State1, State3)
    def method_odd(self):
        """ A method that only can be called in states 1 or 3 """
        return self.return_value

    @property
    @state.only_for(State1)
    def prop_one(self):
        """ A property whose value is only available in state 1 """
        return self.return_value

    @property
    @state.only_for(State2, State3)
    def prop_two(self):
        """ A property whose value is only available in state 2 or 3 """
        return self.return_value

    @state.only_for(State1, State2)
    def increment_state(self):
        """ Increment the state """
        if isinstance(self.state, State1):
            self.done_one()
        else:
            self.done_two()
Пример #2
0
class Server(ValidateModelMixin, TimeStampedModel):
    """
    A single server VM
    """
    name_prefix = models.SlugField(max_length=20, blank=False)

    Status = Status
    status = ModelResourceStateDescriptor(state_classes=Status.states,
                                          default_state=Status.Pending,
                                          model_field_name='_status')
    _status = models.CharField(
        max_length=20,
        default=status.default_state_class.state_id,
        choices=status.model_field_choices,
        db_index=True,
        db_column='status',
    )
    # State transitions:
    _status_to_building = status.transition(from_states=(Status.Pending,
                                                         Status.Unknown),
                                            to_state=Status.Building)
    _status_to_build_failed = status.transition(from_states=(Status.Building,
                                                             Status.Unknown),
                                                to_state=Status.BuildFailed)
    _status_to_booting = status.transition(from_states=(Status.Building,
                                                        Status.Ready,
                                                        Status.Unknown),
                                           to_state=Status.Booting)
    _status_to_ready = status.transition(from_states=(Status.Booting,
                                                      Status.Unknown),
                                         to_state=Status.Ready)
    _status_to_terminated = status.transition(to_state=Status.Terminated)
    _status_to_unknown = status.transition(from_states=(Status.Building,
                                                        Status.Booting,
                                                        Status.Ready),
                                           to_state=Status.Unknown)

    objects = ServerQuerySet().as_manager()

    class Meta:
        abstract = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.logger = ModelLoggerAdapter(logger, {'obj': self})

    @property
    def name(self):
        """ Get a name for this server (slug-friendly) """
        assert self.id is not None
        return "{prefix}-{num}".format(prefix=self.name_prefix, num=self.id)

    @property
    def event_context(self):
        """
        Context dictionary to include in events
        """
        return {'server_id': self.pk}

    def get_log_message_annotation(self):
        """
        Format a log line annotation for this server.
        """
        return 'server={} ({!s:.20})'.format(self.pk, self.name)

    def sleep_until(self, condition, timeout=3600):
        """
        Sleep in a loop until condition related to server status is fulfilled,
        or until timeout (provided in seconds) is reached.

        Raises an exception if the desired condition can not be fulfilled.
        This can happen if the server is in a steady state (i.e., a state that is not expected to change)
        that does not fulfill the desired condition.

        The default timeout is 1h.

        Use as follows:

            server.sleep_until(lambda: server.status.is_steady_state)
            server.sleep_until(lambda: server.status.accepts_ssh_commands)
        """
        # Check if we received a valid timeout value
        # to avoid the possibility of entering an infinite loop (if timeout is negative)
        # or reaching the timeout right away (if timeout is zero)
        assert timeout > 0, "Timeout must be greater than 0 to be able to do anything useful"

        self.logger.info(
            'Waiting to reach status from which we can proceed...')

        while timeout > 0:
            self.update_status()
            if condition():
                self.logger.info(
                    'Reached appropriate status ({name}). Proceeding.'.format(
                        name=self.status.name))
                return
            else:
                if self.status.is_steady_state:
                    raise SteadyStateException(
                        "The current status ({name}) does not fulfill the desired condition "
                        "and is not expected to change.".format(
                            name=self.status.name))
            time.sleep(1)
            timeout -= 1

        # If we get here, this means we've reached the timeout
        raise TimeoutError(
            "Waited {minutes:.2f} to reach appropriate status, and got nowhere. "
            "Aborting with a status of {status}.".format(
                minutes=timeout / 60, status=self.status.name))

    def save(self, *args, **kwargs):
        """
        Save this Server
        """
        super().save(*args, **kwargs)
        publish_data('notification', {
            'type': 'server_update',
            'server_pk': self.pk,
        })

    def update_status(self):
        """
        Check the current status and update it if it has changed
        """
        raise NotImplementedError