def setUp(self): '''Setup for logging, an event and an timer to use for testing ''' self.teal = teal.Teal('data/common/configurationtest.conf', 'stderr', msgLevel=self.msglevel, data_only=True, commit_alerts=False, commit_checkpoints=False) self.timeout_occurred = False self.my_event = multiprocessing.Event() self.timer = ExtendableTimer(5, self.setTimeoutOccurred)
def start(self, start_time): '''Start or restart the time for the pool ''' self.msg_target.debug('Starting while in state {0} {1} {2}' \ .format(POOL_STATE_AS_STRING[self.state], str(start_time), \ str(self.duration))) if self.state == POOL_STATE_NEW: self.state = POOL_STATE_RUNNING self.start_time = start_time self.planned_close_time = self.start_time + timedelta(seconds=self.duration) if self.use_timer == True: self.timer = ExtendableTimer(self.duration, self.timer_expired) self.timer.start() else: raise IncidentPoolStateTransitionError('Start when in {0} state'.format(POOL_STATE_AS_STRING[self.state])) return
def timer_expired(self): '''Timer has expired so close the pool''' if self.state != POOL_STATE_RUNNING: # Nobody cares since either the callback has already been called or we failed return # Check arrival rate to see if we should wait to close extension = min(self.get_arrival_extension(self.duration), self.max_duration - self.duration) if extension == 0: # Now close it try: self.close(self.planned_close_time, POOL_CLOSE_REASON_TIMER) except ThreadKilled: raise except: self.msg_target.exception('Failing pool because the timer expired failed') self.failed() else: # Need to restart the timer with the extension time self.duration += extension self.planned_close_time += timedelta(seconds=extension) self.msg_target.debug('Starting new timer for extension of {0}'.format(str(extension))) self.timer = ExtendableTimer(extension, self.timer_expired) self.timer.start() return
class TestBasicTimer(TealTestCase): def setUp(self): '''Setup for logging, an event and an timer to use for testing ''' self.teal = teal.Teal('data/common/configurationtest.conf', 'stderr', msgLevel=self.msglevel, data_only=True, commit_alerts=False, commit_checkpoints=False) self.timeout_occurred = False self.my_event = multiprocessing.Event() self.timer = ExtendableTimer(5, self.setTimeoutOccurred) def tearDown(self): '''Clear the event and cancel the timer ''' self.my_event.clear() self.timer.cancel() self.teal.shutdown() def setTimeoutOccurred(self): '''Callback when timer completes Set a flag and the event ''' # print 'In setTimeoutOccurred' self.timeout_occurred = True self.my_event.set() return def testTimeoutCancel(self): '''Make sure the timeout cancels Start timer, wait a couple of seconds, cancel, then wait beyond when timeout would have occurred ''' self.timer.start() self.my_event.wait(2) self.timer.cancel() self.my_event.wait(5) self.assertFalse(self.timeout_occurred) return def testTimeoutFail(self): '''Make sure the timeout doesn't occur right away Start timer, wait a couple of seconds and then check ''' self.timer.start() self.my_event.wait(2) self.assertFalse(self.timeout_occurred) return def testTimeoutPass(self): '''Make sure the timeout does occur Start timer and wait on the event past when it should occur ''' self.timer.start() self.my_event.wait(10) self.assertTrue(self.timeout_occurred) # The next test isn't 100% true ... timers don't have to be exact self.assertEqual(self.timer.get_delta().seconds,5) return def testTimeoutExtendFail(self): '''Make sure that the time does extend Start timer, extend, wait more than the initial value but less than the extension and make sure we timeout before that. ''' self.timer.start() self.timer.add_time(5) # Now 10 self.my_event.wait(8) self.assertFalse(self.timeout_occurred) return def testTimeoutExtendPass(self): '''Make sure that the time does extend and times out Start timer, extend, wait more than the the new timeout and make sure the timeout occurs ''' self.timer.start() self.timer.add_time(5) # Now 10 self.my_event.wait(20) self.assertTrue(self.timeout_occurred) # The next test isn't 100% true ... timers don't have to be exact self.assertEqual(self.timer.get_delta().seconds, 10) return def testTimeoutStartLess(self): '''Make sure the time start value overrides Start timer with shorter value, wait less than original value, make sure timeout occurs ''' self.timer.start(2) self.my_event.wait(3) self.assertTrue(self.timeout_occurred) # The next test isn't 100% true ... timers don't have to be exact self.assertEqual(self.timer.get_delta().seconds,2) return def testTimeoutExtendPass1(self): '''Make sure extensions accumulate Start timer, wait 2, extend timer, extend timer, ensure timeout ''' self.timer.start() self.my_event.wait(2) self.timer.add_time(5) # Now 10 self.timer.add_time(2) # Now 12 self.my_event.wait(20) self.assertTrue(self.timeout_occurred) # The next test isn't 100% true ... timers don't have to be exact self.assertEqual(self.timer.get_delta().seconds,timedelta(seconds=12).seconds) return def testTimeoutExtendPass2(self): '''Make sure subsequent extensions accumulate Start timer, wait 2, extend timer, wait into extension, extend timer, ensure timeout ''' self.timer.start() self.my_event.wait(2) self.timer.add_time(5) # Now 10 self.my_event.wait(6) self.timer.add_time(5) # Now 15 self.my_event.wait(20) self.assertTrue(self.timeout_occurred) # The next test isn't 100% true ... timers don't have to be exact self.assertEqual(15, self.timer.get_delta().seconds) return
class IncidentPool(object): '''Base pool to work with incidents, which are typically events and alerts This should not be created directly but should be created via the class methods to make sure the appropriate processing is done Internal variables of interest min_time_incidents is a dictionary: key of incident; value of (min_left, added_at_sec, dur_ext) where min_left is the number of seconds left from the min time in pool added_at_sec is the number of seconds into the pool the incident was added dur_ext is the duration extension (to remove need to re-lookup when moving forward) suppressed is a dict keyed by incident id to a list of the incidents in the pool that have been suppressed. This allows quicker suppression processing. suppressions is a dictionary: key of incident; value of a list of incidents suppressed The dict nature is a dict keyed by incident id to a list of incidents in the pool that have not been suppressed ... yet. This allows quicker suppression processing. The total set of all events in the pool is the combination of the lists from the nature and the contained suppressed dict's lists. ''' @classmethod def new_pool(cls, mode, init_duration, max_duration, close_callback=None, msg_target=None, use_timer=False, arrival_check_ctl=None): ''' Create the next pool using the current one Input pool should be closed ''' # create the instance to return new_obj = cls(mode) # setup the message target information if msg_target is None: # This only happens when testing new_obj.msg_target = MsgTargetLogger(prefix='TESTING: ') else: new_obj.msg_target = msg_target new_obj.msg_target.debug('Creating NEW Incident Pool init={0} max={1}'.format(str(init_duration), str(max_duration))) # Input validation if init_duration < 0: raise IncidentPoolConfigError('Negative initial duration specified on creation') if max_duration is not None: if max_duration < 0: raise IncidentPoolConfigError('Negative maximum duration specified on creation') else: max_duration = POOL_DEFAULT_DURATION # 5 minutes is the default if init_duration > max_duration: raise IncidentPoolConfigError('Initial duration {0} is larger than maximum duration {1}'.format(str(init_duration), str(max_duration))) if mode < 0: raise IncidentPoolConfigError('Invalid mode specified {0}'.format(mode)) try: POOL_MODE_AS_STRING[mode] except: raise IncidentPoolConfigError('Invalid mode specified {0}'.format(mode)) # Set duration information new_obj.duration = init_duration new_obj.init_duration = init_duration new_obj.max_duration = max_duration # Set close callback new_obj.close_callback = close_callback # Indicate if timer should be used new_obj.use_timer = use_timer # Validate arrival check values new_obj.arrival_check_ctl = arrival_check_ctl if arrival_check_ctl is not None: if arrival_check_ctl.window_max is None or \ arrival_check_ctl.arrival_rate is None or \ arrival_check_ctl.extension is None or \ arrival_check_ctl.window_min is None: raise IncidentPoolConfigError('If arrival rate control is specified, then all three values must be specified') new_obj.arrival_window_values = [-1] * arrival_check_ctl.window_max new_obj.add_arrival_window_value = new_obj.add_arrival_window_value_CHECKED return new_obj @classmethod def next_pool(cls, cur_pool): ''' Create the next pool using the current one ''' # Create new instance new_obj = cls(cur_pool.mode) # Copy over msg target values new_obj.msg_target = cur_pool.msg_target new_obj.msg_target.debug('Creating NEXT Incident Pool init={0} max={1}'.format(str(cur_pool.init_duration), str(cur_pool.max_duration))) # Copy over duration values new_obj.duration = cur_pool.init_duration new_obj.init_duration = cur_pool.init_duration new_obj.max_duration = cur_pool.max_duration # Copy over close callback new_obj.close_callback = cur_pool.close_callback new_obj.arrival_check_ctl = cur_pool.arrival_check_ctl if cur_pool.arrival_check_ctl is not None: new_obj.arrival_window_values = [-1] * cur_pool.arrival_check_ctl.window_max new_obj.add_arrival_window_value = new_obj.add_arrival_window_value_CHECKED new_obj.use_timer = cur_pool.use_timer # Check for incidents moving forward # Note: Pool is closed so min_time_incidents only contains incidents to move forward if len(cur_pool.min_time_incidents) != 0: # Process the incidents moving forward # Need to determine start time from current pool # close times may not be filled in so use start times and duration start_time = cur_pool.start_time + timedelta(seconds=cur_pool.duration) new_obj.start_time = start_time for incident in cur_pool.min_time_incidents.keys(): # Put in new pool new_obj.moved_forward.append(incident) # If suppressed in current pool, make suppressed in new tid = incident.get_incident_id() if tid in cur_pool.suppressed.keys() and incident in cur_pool.suppressed[tid]: new_obj.suppressed[incident.get_incident_id()].append(incident) else: new_obj.incidents[incident.get_incident_id()].append(incident) # Move things it suppresses key = (incident.get_type(), incident.get_rec_id()) if key in cur_pool.suppressions: new_obj.suppressions[key] = cur_pool.get_suppressed(incident) # For events moving forward: update duration, add to moving forward list min_left, added_at_sec, dur_ext = cur_pool.min_time_incidents[incident] # Update duration new_obj.duration = min(new_obj.duration + dur_ext, new_obj.max_duration) # Calculate new min time in pool (note: we know positive because if it wasn't, it # would not have moved forward -- cleanup in close) new_min = min_left - (cur_pool.duration - added_at_sec) # Add to moving forward list in new pool new_obj.min_time_incidents[incident] = (new_min, 0, dur_ext) # Process next as a subsequent new_obj._add_incident = new_obj._add_incident_ACTIVE_SUBSEQUENT # Start the pool # Note: sets the state, the start times, the planned close times, and, if needed, starts the timer new_obj.start(start_time) #p rint cur_pool.dump() #p rint new_obj.dump() return new_obj def __init__(self, mode): '''Constructor This should not be created directly. It should be created using the class methods above. mode -- indicate if the pool should use time logged or time occurred ''' self.state = POOL_STATE_NEW # Mode controls which time to use self.mode = mode if self.mode == POOL_MODE_LOGGED: self.get_time = self.get_time_LOGGED else: self.get_time = self.get_time_OCCURRED # New so first incident starts the pool self._add_incident = self._add_incident_ACTIVE_FIRST # Lock because timer thread may be accessing asynchronously self.lock = threading.RLock() # Setup incidents self.incidents = defaultdict(list) self.last_incident = None # Start with no suppressions self.suppressions = defaultdict(set) self.suppressed = defaultdict(list) # set start and close times self.start_time = None self.planned_close_time = None self.close_time = None # Initialize timer info self.use_timer = False self.timer = None # Nothing moving forward self.moved_forward = [] self.min_time_incidents = {} # Arrival Rate variables self.arrival_check_ctl = None self.arrival_window_values = [-1] if os.environ.get(TEAL_TEST_POOL_LOG_ON_CLOSE, 'NO') == 'YES': self.log_on_close = True else: self.log_on_close = False return def get_time_OCCURRED(self, incident): ''' Get the incident time when in occurred mode ''' return incident.get_time_occurred() def get_time_LOGGED(self, incident): ''' Get the incident time when in occurred mode ''' return incident.get_time_logged() def start(self, start_time): '''Start or restart the time for the pool ''' self.msg_target.debug('Starting while in state {0} {1} {2}' \ .format(POOL_STATE_AS_STRING[self.state], str(start_time), \ str(self.duration))) if self.state == POOL_STATE_NEW: self.state = POOL_STATE_RUNNING self.start_time = start_time self.planned_close_time = self.start_time + timedelta(seconds=self.duration) if self.use_timer == True: self.timer = ExtendableTimer(self.duration, self.timer_expired) self.timer.start() else: raise IncidentPoolStateTransitionError('Start when in {0} state'.format(POOL_STATE_AS_STRING[self.state])) return def flush(self, flush_time): ''' Flush the pool ... close it now instead of at duration ''' if self.state != POOL_STATE_RUNNING: return self.duration = abs(flush_time - self.start_time).seconds self.close(flush_time, POOL_CLOSE_REASON_FLUSH) return def close(self, close_time, reason): '''Close the pool ''' with self.lock: if self.state == POOL_STATE_RUNNING: # Pool has stuff in it if (close_time is not None and close_time < self.start_time): self.msg_target.debug(str(self)) raise IncidentPoolStateTransitionError('End time was before start time') if self.timer is not None: self.timer.cancel() # turn off ability to add incidents self._add_incident = self._add_incident_CLOSED self.state = POOL_STATE_CLOSED self.close_time = close_time if self.log_on_close == False: self.msg_target.debug('Closing due to {0} {1}({2})' \ .format(POOL_CLOSE_REASON_AS_STRING[reason], \ str(self.close_time), str(self.planned_close_time))) else: self.msg_target.warning('Closing due to {0}' \ .format(POOL_CLOSE_REASON_AS_STRING[reason])) self.msg_target.warning('{0}'.format(str(self))) # check/clean-up min_time entries del_list = [] for incident in self.min_time_incidents.keys(): min_left, added_at_sec = self.min_time_incidents[incident][0:2] if min_left <= self.duration - added_at_sec: del_list.append(incident) for incident in del_list: del self.min_time_incidents[incident] # Callback if self.close_callback is not None: try: tmp_rec_id = self.last_incident.rec_id except ThreadKilled: raise except: tmp_rec_id = None self.close_callback(reason, tmp_rec_id) elif self.state == POOL_STATE_NEW: # Pool doesn't have anything in it (created but no incidents added) # Set all the times as close time self.close_time = close_time # Stop timer (could be an incoming shutdown on a timed pool with no elements yet) if self.timer is not None: self.timer.cancel() # turn off ability to add incidents self._add_incident = self._add_incident_CLOSED self.state = POOL_STATE_CLOSED self.msg_target.debug('Closing {0}' \ .format(POOL_CLOSE_REASON_AS_STRING[reason])) if self.close_callback is not None: self.close_callback(reason, None) else: #self.lock.release() raise IncidentPoolStateTransitionError('Closed when in {0} state'.format(POOL_STATE_AS_STRING[self.state])) return def shutdown(self): '''Shutdown the pool. This includes closing it with shutdown=True''' with self.lock: if self.state == POOL_STATE_CLOSED: # Nobody cares since callback was already called return elif self.state == POOL_STATE_NEW: # start it so we can close it now = datetime.now() self.start(now) # Now close it with shutdown indicator self.close(self.planned_close_time, POOL_CLOSE_REASON_SHUTDOWN) return def failed(self): ''' Mark the pool as failed ''' # turn off ability to add incidents self._add_incident = self._add_incident_FAILED self.state = POOL_STATE_FAILED self.msg_target.error('Pool failed:') try: self.msg_target.error('{0}'.format(self.dump())) except ThreadKilled: raise except: pass return def add_incident(self, incident, ext_dur, min_time): '''Add an incident to the pool. Using dynamic method pattern Note that the ext_dur and min_time must be positive values If this does not raise an exception the a lock will be held. Caller is responsible for calling add_incident_completed to release it ''' self.lock.acquire() try: self._add_incident(incident, ext_dur, min_time) except ThreadKilled: raise except: self.lock.release() raise return def add_incident_completed(self): '''Any additional processing to be done when adding an incident has been completed. This will release the pool lock ''' self.lock.release() return def _add_incident_CLOSED(self, incident, ext_dur, min_time): '''Add an incident to the pool when the pool is closed Pool is closed, so can't add anymore so raise exception ''' raise IncidentPoolClosedError('Attempted to add incident to closed pool', error=False) def _add_incident_FAILED(self, incident, ext_dur, min_time): ''' Add an incident to a failed pool ''' raise IncidentPoolFailedError('Attempt to add incident to a failed pool') def _add_incident_ACTIVE_FIRST(self, incident, ext_dur, min_time): '''Add an incident to the pool when it is the first incident to go into the pool''' self.duration = min(self.duration + ext_dur, self.max_duration) if min_time > 0: self.min_time_incidents[incident] = (min_time, 0, ext_dur) self.start(self.get_time(incident)) self.incidents[incident.get_incident_id()].append(incident) self.msg_target.debug('Started and {0} added'.format(incident)) self._add_incident = self._add_incident_ACTIVE_SUBSEQUENT self.last_incident = incident self.add_arrival_window_value(0) return def _add_incident_ACTIVE_SUBSEQUENT(self, incident, ext_dur, min_time): '''Add an incident to the pool, but not the first ''' # Check if pool should be closed point_in_pool = abs(self.get_time(incident) - self.start_time).seconds if self.get_time(incident) > self.planned_close_time: extension = min(self.get_arrival_extension(point_in_pool), self.max_duration - self.duration) if extension == 0: #close the pool and throw exception self.close(self.planned_close_time, POOL_CLOSE_REASON_INCIDENT_TIME) raise IncidentPoolClosedError('Pool closed by incident past planned close time', error=False) # Arrival rate extension occurred self.msg_target.debug('Arrival rate caused extension of {0}'.format(str(extension))) self.duration += extension self.planned_close_time += timedelta(seconds=extension) if self.use_timer is True: self.timer.add_time(extension) # Add the incident self.incidents[incident.get_incident_id()].append(incident) self.msg_target.debug('{0} added'.format(incident)) # Duration is extended after check increment = min(ext_dur, self.max_duration - self.duration) self.msg_target.debug('Extending pool duration from {0} by {1}'.format(str(self.duration), str(increment))) self.duration += increment self.planned_close_time += timedelta(seconds=increment) # Record min time info if might need at close self.add_arrival_window_value(point_in_pool) if min_time != 0: self.min_time_incidents[incident] = (min_time, point_in_pool, ext_dur) if self.use_timer is True: self.timer.add_time(increment) self.last_incident = incident return def suppresses(self, suppressor, suppressees): '''Suppressor supresses set of suppressees Suppressor can be either an event or an alert ''' if not suppressees: return key = (suppressor.get_type(), suppressor.get_rec_id()) self.suppressions[key].update(suppressees) for suppressed in suppressees: tid = suppressed.get_incident_id() if tid in self.incidents.keys() and suppressed in self.incidents[tid]: self.incidents[tid].remove(suppressed) if len(self.incidents[tid]) == 0: del self.incidents[tid] self.suppressed[tid].append(suppressed) return def get_suppressed(self, suppressor, result=None): ''' Get the items that this suppressor suppresses This is transitive Suppressor can be either an event or an alert ''' key = (suppressor.get_type(), suppressor.get_rec_id()) if result is None: result = set() if key not in self.suppressions: return result # only drill down for ones we haven't already found drill_down = self.suppressions[key].copy() drill_down.difference_update(result) drill_down.difference_update([suppressor]) # Add the suppressor's suppressed to the result result.update(self.suppressions[key]) # drill down to get transitive closure for i in drill_down: result.update(self.get_suppressed(i, result)) return result def is_suppressed(self, incident): ''' check if the incident is suppressed in this pool ''' tid = incident.get_incident_id() return (tid in self.suppressed.keys() and incident in self.suppressed[tid]) def force_suppressed(self, incident): ''' force the incident to be suppressed in this pool ''' tid = incident.get_incident_id() if tid in self.incidents.keys() and incident in self.incidents[tid]: self.incidents[tid].remove(incident) if len(self.incidents[tid]) == 0: del self.incidents[tid] self.suppressed[tid].append(incident) return def get_incidents(self, incident_id=None): ''' Get all incidents in the pool ''' result = [] if incident_id is not None: if incident_id in self.incidents.keys(): result.extend(self.incidents[incident_id]) if incident_id in self.suppressed.keys(): result.extend(self.suppressed[incident_id]) else: result = [] for til in self.incidents.values(): result.extend(til) for tsl in self.suppressed.values(): result.extend(tsl) return result def get_suppressed_incidents(self, incident_id=None): ''' Get all incidents in the pool ''' if incident_id is not None: result = self.suppressed[incident_id] else: result = [] for tsl in self.suppressed.values(): result.extend(tsl) return result def contains_incident(self, incident_type, incident_id): '''Determine if the pool contains at least one incident of the specified incident_type and incident id''' # Check unsuppressed return ((incident_id in self.incidents.keys() and len(self.incidents[incident_id]))or (incident_id in self.suppressed.keys() and len(self.suppressed[incident_id]))) def timer_expired(self): '''Timer has expired so close the pool''' if self.state != POOL_STATE_RUNNING: # Nobody cares since either the callback has already been called or we failed return # Check arrival rate to see if we should wait to close extension = min(self.get_arrival_extension(self.duration), self.max_duration - self.duration) if extension == 0: # Now close it try: self.close(self.planned_close_time, POOL_CLOSE_REASON_TIMER) except ThreadKilled: raise except: self.msg_target.exception('Failing pool because the timer expired failed') self.failed() else: # Need to restart the timer with the extension time self.duration += extension self.planned_close_time += timedelta(seconds=extension) self.msg_target.debug('Starting new timer for extension of {0}'.format(str(extension))) self.timer = ExtendableTimer(extension, self.timer_expired) self.timer.start() return def add_arrival_window_value_CHECKED(self, value): ''' Add a rate value for arrival checking When checking is done ''' del self.arrival_window_values[0] self.arrival_window_values.append(value) return def add_arrival_window_value(self, value): ''' Nothing to do if not checking arrival rate Default behavior ''' return def get_arrival_rate(self, current_arrival): ''' Calculate the arrival rate ''' # Check if using or if not enough if self.arrival_check_ctl is None: return None first = self.arrival_window_values[0] if first == -1: if self.arrival_window_values[-1] == -1: return None tmp_values = [v for v in self.arrival_window_values if v != -1] first = tmp_values[0] tmp_size = len(tmp_values) if tmp_size < self.arrival_check_ctl.window_min: return None else: tmp_size = self.arrival_check_ctl.window_max time_delta = current_arrival - first if time_delta == 0: # All happened at the same time return sys.maxint return (tmp_size * 1000) / (time_delta) def get_arrival_extension(self, current_arrival): ''' Check the arrival rate and return True if the time should be extended because of the arrival rate ''' rate = self.get_arrival_rate(current_arrival) if rate is None or rate < (1000 * self.arrival_check_ctl.arrival_rate): return 0 self.msg_target.debug('Arrival threshold met: {0} < {1}'.format(str(rate), str(self.arrival_check_ctl.arrival_rate))) # Originally did the next line, but it doesn't make sense because we # should just keep calculating the arrival rate #self.arrival_window_values = [-1] * self.arrival_check_ctl.window_max return self.arrival_check_ctl.extension def __str__(self): ''' Dump out all of the information about the pool as a string ''' out_str = '\nIncidentPool {0} state = {1}; last = '.format(str(self.msg_target.prefix), POOL_STATE_AS_STRING[self.state]) if self.last_incident is not None: out_str += '{0}'.format(str(self.last_incident.rec_id)) else: out_str += 'None' out_str += '\n duration: {0} >{1}< {2}'.format(self.init_duration, self.duration, self.max_duration) out_str += ' timer = {0}'.format(str(self.use_timer)) out_str += '\n times: S: {0}'.format(self.start_time) if self.planned_close_time: out_str += ' PC: {0}({1:5d})'.format(self.planned_close_time, abs(self.planned_close_time - self.start_time).seconds) if self.close_time: out_str += ' C: {0}({1:5d})'.format(self.close_time, abs(self.close_time - self.start_time).seconds) out_str += '\n active incidents:' if len(self.incidents) == 0: out_str += '\n <none>' else: for til in self.incidents.values(): for incident in til: out_str += '\n {0:5d}: {1:8s} {2}({3:5d})'.format(incident.get_rec_id(), incident.get_incident_id(), self.get_time(incident), abs(self.get_time(incident) - self.start_time).seconds) out_str += '\n suppressed incidents:' if len(self.suppressed) == 0: out_str += '\n <none>' else: for tsl in self.suppressed.values(): for incident in tsl: out_str += '\n {0:5d}: {1:8s} {2}({3:5d})'.format(incident.get_rec_id(), incident.get_incident_id(), self.get_time(incident), abs(self.get_time(incident) - self.start_time).seconds) out_str += '\n suppression relationships:' if len(self.suppressions) == 0: out_str += '\n <none>' else: for key in self.suppressions.keys(): sup_list = self.suppressions[key] out_str += '\n {0}({1:5d}) -> '.format(key[0], key[1]) if len(sup_list) == 0: out_str += ' <none>' else: out_str += ','.join(['{0:8s}({1:5d})'.format(sup.get_incident_id(), sup.get_rec_id())for sup in sup_list]) out_str += '\n priming incidents:' if len(self.moved_forward) == 0: out_str += '\n <none>' else: out_str += '\n ' out_str += ','.join(['{0:5d}: {1:8s}'.format(mf.get_rec_id(), mf.get_incident_id())for mf in self.moved_forward]) out_str += '\n min time in pool monitoring list:' if len(self.min_time_incidents) == 0: out_str += '\n <none>' else: for incident in self.min_time_incidents.keys(): min_left, added_at_sec, dur_ext = self.min_time_incidents[incident] #print min_left, added_at_sec, dur_ext, incident.get_rec_id(), incident.get_incident_id() out_str += '\n {0:5d}: {1:8s} left = {2:5d} added = {3:5d} ext = {4:5d}' \ .format(incident.get_rec_id(), incident.get_incident_id(), min_left, added_at_sec, dur_ext) if self.arrival_check_ctl is not None: out_str += '\n arrival rate: {0} per sec {1} sec\n ['.format( str(self.arrival_check_ctl.arrival_rate), str(self.arrival_check_ctl.extension)) out_str += ','.join(['{0}'.format(str(tv)) for tv in self.arrival_window_values]) out_str += '] rate = {0}/1000 per sec'.format(str(self.get_arrival_rate(self.duration))) else: out_str += '\n arrival rate: --not checked--' return out_str