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)
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)
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 )
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)