Esempio n. 1
0
class RoutineWorkload(Workload):
    """
    A routine workload generates accesses according to the following params:

        - A normal distribution time between accesses
        - A probability of reads (vs. writes)
        - A probability of switching objects (vs. continuing with current)

    This is the simplest workload that is completely implemented.
    """
    def __init__(self, sim, **kwargs):
        """
        Initialize workload probabilities and distributions before passing
        all optional keyword arguments to the super class.
        """

        # Distribution for whether or not to change objects
        self.do_object = Bernoulli(
            kwargs.pop('object_prob', settings.simulation.object_prob))
        self.do_read = Bernoulli(
            kwargs.pop('read_prob', settings.simulation.read_prob))

        # Interval distribution for the wait (in ms) to the next access.
        self.next_access = BoundedNormal(
            kwargs.pop('access_mean', settings.simulation.access_mean),
            kwargs.pop('access_stddev', settings.simulation.access_stddev),
            floor=1.0,
        )

        # Initialize the Workload
        super(RoutineWorkload, self).__init__(sim, **kwargs)

        # If current is None, update the state of the workload:
        if self.current is None: self.update()

    def update(self, **kwargs):
        """
        Uses the do_object distribution to determine whether or not to change
        the currently accessed object to a new object.
        """

        # Do we switch the current object?
        if self.current is None or self.do_object.get():

            if len(self.objects) == 1:
                # There is only one choice, no switching!
                self.current = self.objects[0]

            else:
                # Randomly select an object that is not the current object.
                self.current = Discrete([
                    obj for obj in self.objects if obj != self.current
                ]).get()

        # Call to the super update method
        super(RoutineWorkload, self).update(**kwargs)

    def wait(self):
        """
        Utilizes the bounded normal distribution to return the wait (in
        milliseconds) until the next access.
        """
        return self.next_access.get()

    def access(self):
        """
        Utilizes the do_read distribution to determine whether or not to
        issue a write or a read access, and calls the device method for it.
        """
        # Make sure that there is a device to write to!
        if not self.device:
            raise WorkloadException(
                "No device specified to trigger the access on!")

        # Make sure that there is a current object to write!
        if not self.current:
            raise WorkloadException(
                "No object specified as currently open on the workload!")

        # Determine if we are reading or writing.
        access = READ if self.do_read.get() else WRITE

        # Log the results on the timeseries for the access.
        self.sim.results.update(
            access,
            (self.device.id, self.location, self.current, self.env.now))

        if access == READ:
            # Read the latest version of the current object
            return self.device.read(self.current)

        if access == WRITE:
            # Write to the current version (e.g. call nextv)
            return self.device.write(self.current)
Esempio n. 2
0
class RoutineWorkload(Workload):
    """
    A routine workload generates accesses according to the following params:

        - A normal distribution time between accesses
        - A probability of reads (vs. writes)
        - A probability of switching objects (vs. continuing with current)

    This is the simplest workload that is completely implemented.
    """

    def __init__(self, sim, **kwargs):
        """
        Initialize workload probabilities and distributions before passing
        all optional keyword arguments to the super class.
        """

        # Distribution for whether or not to change objects
        self.do_object = Bernoulli(kwargs.pop("object_prob", settings.simulation.object_prob))
        self.do_read = Bernoulli(kwargs.pop("read_prob", settings.simulation.read_prob))

        # Interval distribution for the wait (in ms) to the next access.
        self.next_access = BoundedNormal(
            kwargs.pop("access_mean", settings.simulation.access_mean),
            kwargs.pop("access_stddev", settings.simulation.access_stddev),
            floor=1.0,
        )

        # Initialize the Workload
        super(RoutineWorkload, self).__init__(sim, **kwargs)

        # If current is None, update the state of the workload:
        if self.current is None:
            self.update()

    def update(self, **kwargs):
        """
        Uses the do_object distribution to determine whether or not to change
        the currently accessed object to a new object.
        """

        # Do we switch the current object?
        if self.current is None or self.do_object.get():

            if len(self.objects) == 1:
                # There is only one choice, no switching!
                self.current = self.objects[0]

            else:
                # Randomly select an object that is not the current object.
                self.current = Discrete([obj for obj in self.objects if obj != self.current]).get()

        # Call to the super update method
        super(RoutineWorkload, self).update(**kwargs)

    def wait(self):
        """
        Utilizes the bounded normal distribution to return the wait (in
        milliseconds) until the next access.
        """
        return self.next_access.get()

    def access(self):
        """
        Utilizes the do_read distribution to determine whether or not to
        issue a write or a read access, and calls the device method for it.
        """
        # Make sure that there is a device to write to!
        if not self.device:
            raise WorkloadException("No device specified to trigger the access on!")

        # Make sure that there is a current object to write!
        if not self.current:
            raise WorkloadException("No object specified as currently open on the workload!")

        # Determine if we are reading or writing.
        access = READ if self.do_read.get() else WRITE

        # Log the results on the timeseries for the access.
        self.sim.results.update(access, (self.device.id, self.location, self.current, self.env.now))

        if access == READ:
            # Read the latest version of the current object
            return self.device.read(self.current)

        if access == WRITE:
            # Write to the current version (e.g. call nextv)
            return self.device.write(self.current)
Esempio n. 3
0
class OutageGenerator(NamedProcess):
    """
    A process that causes outages to occur across the wide or local area or
    across both areas, or to occur for leaders only. Outages are generated on
    a collection of connections, usually collected together based on the
    connection type. The outages script is run as follows in the simulation:

        - determine the probability of online vs. outage
        - use the normal distribution of online/outage to determine duration
        - cause outage if outage, wait until duration is over.
        - repeat from the first step.

    Outages can also do more than cut the connection, they can also vary the
    latency for that connection by changing the network parameters.
    """

    def __init__(self, sim, connections, **kwargs):
        """
        Initialize the workload with the simulation (containing both the
        environment and the topology for work), the set of connections to
        cause outages for as a group, and any additional arguments.
        """

        self.sim = sim
        self.connections = connections
        self.do_outage = Bernoulli(kwargs.pop('outage_prob', settings.simulation.outage_prob))

        # NOTE: This will not call any methods on the connections (on purpose)
        self._state = ONLINE

        # Distribution of outage duration
        self.outage_duration = BoundedNormal(
            kwargs.pop('outage_mean', settings.simulation.outage_mean),
            kwargs.pop('outage_stddev', settings.simulation.outage_stddev),
            floor = 10.0,
        )

        # Distribution of online duration
        self.online_duration = BoundedNormal(
            kwargs.pop('online_mean', settings.simulation.online_mean),
            kwargs.pop('online_stddev', settings.simulation.online_stddev),
            floor = 10.0,
        )

        # Initialize the Process
        super(OutageGenerator, self).__init__(sim.env)

    @setter
    def connections(self, value):
        """
        Allows passing a single connection instance or multiple.
        """
        if not isinstance(value, (tuple, list)):
            value = (value,)
        return tuple(value)

    @setter
    def state(self, state):
        """
        When the state is set on the outage generator, update connections.
        """
        if state == ONLINE:
            self.update_online_state()

        elif state == OUTAGE:
            self.update_outage_state()

        else:
            raise OutagesException(
                "Unknown state: '{}' set either {} or {}".format(
                    state, ONLINE, OUTAGE
                )
            )

        return state

    def update_online_state(self):
        """
        Sets the state of the generator to online.
        NOTE - should not be called by clients but can be subclassed!
        """
        # If we were previously offline:
        if self.state == OUTAGE:
            for conn in self.connections:
                conn.up()
                self.sim.logger.debug(
                    "{} is now online".format(conn)
                )

    def update_outage_state(self):
        """
        Sets the state of the generator to outage.
        NOTE - should not be called by clients but can be subclassed!
        """
        # If we were previously online:
        if self.state == ONLINE:
            for conn in self.connections:
                conn.down()
                self.sim.logger.debug(
                    "{} is now offline".format(conn)
                )

    def duration(self):
        """
        Returns the duration of the current state in milliseconds.
        """
        if self.state == ONLINE:
            return self.online_duration.get()

        if self.state == OUTAGE:
            return self.outage_duration.get()

    def update(self):
        """
        Updates the state of the connections according to the outage
        probability. This method should be called routinely according to the
        outage and online duration distributions.
        """

        if self.do_outage.get():
            self.state = OUTAGE
        else:
            self.state = ONLINE

    def run(self):
        """
        The action that generates outages on the passed in set of connections.
        """
        while True:

            # Get the duration of the current state
            duration = self.duration()

            # Log (info) the outage/online state and duration
            self.sim.logger.info(
                "{} connections {} for {}".format(
                    len(self.connections), self.state,
                    humanizedelta(milliseconds=duration)
                )
            )

            # Wait for the duration
            yield self.env.timeout(duration)

            # Update the state of the outage
            self.update()

    def __str__(self):
        """
        String representation of the outage generator.
        """
        return "{}: {} connections {}".format(
            self.name, len(self.connections), self.state
        )
Esempio n. 4
0
class OutageGenerator(NamedProcess):
    """
    A process that causes outages to occur across the wide or local area or
    across both areas, or to occur for leaders only. Outages are generated on
    a collection of connections, usually collected together based on the
    connection type. The outages script is run as follows in the simulation:

        - determine the probability of online vs. outage
        - use the normal distribution of online/outage to determine duration
        - cause outage if outage, wait until duration is over.
        - repeat from the first step.

    Outages can also do more than cut the connection, they can also vary the
    latency for that connection by changing the network parameters.
    """
    def __init__(self, sim, connections, **kwargs):
        """
        Initialize the workload with the simulation (containing both the
        environment and the topology for work), the set of connections to
        cause outages for as a group, and any additional arguments.
        """

        self.sim = sim
        self.connections = connections
        self.do_outage = Bernoulli(
            kwargs.pop('outage_prob', settings.simulation.outage_prob))

        # NOTE: This will not call any methods on the connections (on purpose)
        self._state = ONLINE

        # Distribution of outage duration
        self.outage_duration = BoundedNormal(
            kwargs.pop('outage_mean', settings.simulation.outage_mean),
            kwargs.pop('outage_stddev', settings.simulation.outage_stddev),
            floor=10.0,
        )

        # Distribution of online duration
        self.online_duration = BoundedNormal(
            kwargs.pop('online_mean', settings.simulation.online_mean),
            kwargs.pop('online_stddev', settings.simulation.online_stddev),
            floor=10.0,
        )

        # Initialize the Process
        super(OutageGenerator, self).__init__(sim.env)

    @setter
    def connections(self, value):
        """
        Allows passing a single connection instance or multiple.
        """
        if not isinstance(value, (tuple, list)):
            value = (value, )
        return tuple(value)

    @setter
    def state(self, state):
        """
        When the state is set on the outage generator, update connections.
        """
        if state == ONLINE:
            self.update_online_state()

        elif state == OUTAGE:
            self.update_outage_state()

        else:
            raise OutagesException(
                "Unknown state: '{}' set either {} or {}".format(
                    state, ONLINE, OUTAGE))

        return state

    def update_online_state(self):
        """
        Sets the state of the generator to online.
        NOTE - should not be called by clients but can be subclassed!
        """
        # If we were previously offline:
        if self.state == OUTAGE:
            for conn in self.connections:
                conn.up()
                self.sim.logger.debug("{} is now online".format(conn))

    def update_outage_state(self):
        """
        Sets the state of the generator to outage.
        NOTE - should not be called by clients but can be subclassed!
        """
        # If we were previously online:
        if self.state == ONLINE:
            for conn in self.connections:
                conn.down()
                self.sim.logger.debug("{} is now offline".format(conn))

    def duration(self):
        """
        Returns the duration of the current state in milliseconds.
        """
        if self.state == ONLINE:
            return self.online_duration.get()

        if self.state == OUTAGE:
            return self.outage_duration.get()

    def update(self):
        """
        Updates the state of the connections according to the outage
        probability. This method should be called routinely according to the
        outage and online duration distributions.
        """

        if self.do_outage.get():
            self.state = OUTAGE
        else:
            self.state = ONLINE

    def run(self):
        """
        The action that generates outages on the passed in set of connections.
        """
        while True:

            # Get the duration of the current state
            duration = self.duration()

            # Log (info) the outage/online state and duration
            self.sim.logger.info("{} connections {} for {}".format(
                len(self.connections), self.state,
                humanizedelta(milliseconds=duration)))

            # Wait for the duration
            yield self.env.timeout(duration)

            # Update the state of the outage
            self.update()

    def __str__(self):
        """
        String representation of the outage generator.
        """
        return "{}: {} connections {}".format(self.name, len(self.connections),
                                              self.state)