class Servicedependency(Item): id = 0 my_type = "servicedependency" # F is dep of D # host_name Host B # service_description Service D # dependent_host_name Host C # dependent_service_description Service F # execution_failure_criteria o # notification_failure_criteria w,u # inherits_parent 1 # dependency_period 24x7 properties = Item.properties.copy() properties.update({ 'dependent_host_name': StringProp(), 'dependent_hostgroup_name': StringProp(default=''), 'dependent_service_description': StringProp(), 'host_name': StringProp(), 'hostgroup_name': StringProp(default=''), 'service_description': StringProp(), 'inherits_parent': BoolProp(default='0'), 'execution_failure_criteria': ListProp(default='n'), 'notification_failure_criteria': ListProp(default='n'), 'dependency_period': StringProp(default=''), 'explode_hostgroup': BoolProp(default='0') }) # Give a nice name output, for debbuging purpose # (Yes, debbuging CAN happen...) def get_name(self): return getattr(self, 'dependent_host_name', '') + '/' + getattr(self, 'dependent_service_description', '') + '..' + getattr(self, 'host_name', '') + '/' + getattr(self, 'service_description', '')
class Hostdependency(Item): id = 0 #F is dep of D #host_name Host B # service_description Service D # dependent_host_name Host C # dependent_service_description Service F # execution_failure_criteria o # notification_failure_criteria w,u # inherits_parent 1 # dependency_period 24x7 properties = { 'dependent_host_name': StringProp(), 'dependent_hostgroup_name': StringProp(default=''), 'host_name': StringProp(), 'hostgroup_name': StringProp(default=''), 'inherits_parent': BoolProp(default='0'), 'execution_failure_criteria': ListProp(default='n'), 'notification_failure_criteria': ListProp(default='n'), 'dependency_period': StringProp(default='') } running_properties = { 'configuration_errors': ListProp(default=[]), } # Give a nice name output, for debbuging purpose # (debugging happens more often than expected...) def get_name(self): return self.dependent_host_name+'/'+self.host_name
class Resultmodulation(Item): id = 1#0 is always special in database, so we do not take risk here my_type = 'resultmodulation' properties = { 'resultmodulation_name': StringProp(), 'exit_codes_match': ListProp (default=''), 'exit_code_modulation': StringProp(default=None), 'modulation_period': StringProp(default=None), } running_properties = { 'configuration_errors': ListProp(default=[]), } macros = {} #For debugging purpose only (nice name) def get_name(self): return self.resultmodulation_name def clean(self): pass #Make the return code modulation if need def module_return(self, return_code): #Only if in modulation_period of modulation_period == None if self.modulation_period is None or self.modulation_period.is_time_valid(time.time()): #Try to change the exit code only if a new one is defined if self.exit_code_modulation is not None: #First with the exit_code_match if return_code in self.exit_codes_match: return_code = self.exit_code_modulation return return_code #We override the pythonize because we have special cases that we do not want #to be do at running def pythonize(self): #First apply Item pythonize super(self.__class__, self).pythonize() #Then very special cases # Intify the exit_codes_match, and make list self.exit_codes_match = [ int(ec) for ec in getattr(self, 'exit_codes_match', []) ] if hasattr(self, 'exit_code_modulation'): self.exit_code_modulation = int(self.exit_code_modulation) else: self.exit_code_modulation = None
class PollerLink(SatelliteLink): """This class is the link between Arbiter and Poller. With it, arbiter can see if a poller is alive, and can send it new configuration """ id = 0 my_type = 'poller' # To_send: send or not to satellite conf properties = SatelliteLink.properties.copy() properties.update({ 'poller_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7771, fill_brok=['full_status']), 'passive': BoolProp(default='0', fill_brok=['full_status'], to_send=True), 'min_workers': IntegerProp(default='0', fill_brok=['full_status'], to_send=True), 'max_workers': IntegerProp(default='30', fill_brok=['full_status'], to_send=True), 'processes_by_worker': IntegerProp(default='256', fill_brok=['full_status'], to_send=True), 'poller_tags': ListProp(default='None', to_send=True), }) def get_name(self): return self.poller_name def register_to_my_realm(self): self.realm.pollers.append(self)
class ReactionnerLink(SatelliteLink): id = 0 my_type = 'reactionner' properties = SatelliteLink.properties.copy() properties.update({ 'reactionner_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default='7769', fill_brok=['full_status']), 'passive': BoolProp(default='0', fill_brok=['full_status'], to_send=True), 'min_workers': IntegerProp(default='1', fill_brok=['full_status'], to_send=True), 'max_workers': IntegerProp(default='30', fill_brok=['full_status'], to_send=True), 'processes_by_worker': IntegerProp(default='256', fill_brok=['full_status'], to_send=True), 'reactionner_tags': ListProp(default='None', to_send=True), }) def get_name(self): return self.reactionner_name def register_to_my_realm(self): self.realm.reactionners.append(self)
class ReactionnerLink(SatelliteLink): """Please Add a Docstring to describe the class here""" id = 0 my_type = 'reactionner' properties = SatelliteLink.properties.copy() properties.update({ 'reactionner_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7769, fill_brok=['full_status']), 'min_workers': IntegerProp(default=1, fill_brok=['full_status'], to_send=True), 'max_workers': IntegerProp(default=30, fill_brok=['full_status'], to_send=True), 'processes_by_worker': IntegerProp(default=256, fill_brok=['full_status'], to_send=True), 'reactionner_tags': ListProp(default=['None'], to_send=True), }) def get_name(self): return self.reactionner_name def register_to_my_realm(self): self.realm.reactionners.append(self)
class Serviceescalation(Item): id = 1 # zero is always special in database, so we do not take risk here my_type = 'serviceescalation' properties = Item.properties.copy() properties.update({ 'host_name': StringProp(), 'hostgroup_name': StringProp(), 'service_description': StringProp(), 'first_notification': IntegerProp(), 'last_notification': IntegerProp(), 'notification_interval': IntegerProp(default=30), # like Nagios value 'escalation_period': StringProp(default=''), 'escalation_options': ListProp(default=['d', 'u', 'r', 'w', 'c'], split_on_coma=True), 'contacts': StringProp(), 'contact_groups': StringProp(), 'first_notification_time': IntegerProp(), 'last_notification_time': IntegerProp(), }) # For debugging purpose only (nice name) def get_name(self): return ''
class Hostdependency(Item): id = 0 my_type = 'hostdependency' # F is dep of D # host_name Host B # service_description Service D # dependent_host_name Host C # dependent_service_description Service F # execution_failure_criteria o # notification_failure_criteria w,u # inherits_parent 1 # dependency_period 24x7 properties = Item.properties.copy() properties.update({ 'dependent_host_name': StringProp(), 'dependent_hostgroup_name': StringProp(default=''), 'host_name': StringProp(), 'hostgroup_name': StringProp(default=''), 'inherits_parent': BoolProp(default=False), 'execution_failure_criteria': ListProp(default=['n'], split_on_coma=True), 'notification_failure_criteria': ListProp(default=['n'], split_on_coma=True), 'dependency_period': StringProp(default='') }) # Give a nice name output, for debugging purpose # (debugging happens more often than expected...) def get_name(self): dependent_host_name = 'unknown' if getattr(self, 'dependent_host_name', None): dependent_host_name = getattr(getattr(self, 'dependent_host_name'), 'host_name', 'unknown') host_name = 'unknown' if getattr(self, 'host_name', None): host_name = getattr(getattr(self, 'host_name'), 'host_name', 'unknown') return dependent_host_name + '/' + host_name
class Module(Item): id = 1 # zero is always special in database, so we do not take risk here my_type = 'module' properties = Item.properties.copy() properties.update({ 'module_name': StringProp(), 'module_type': StringProp(), 'modules': ListProp(default=''), }) macros = {} # For debugging purpose only (nice name) def get_name(self): return self.module_name
class Discoveryrun(Item): id = 1 #0 is always special in database, so we do not take risk here my_type = 'discoveryrun' properties = { 'discoveryrun_name': StringProp(), 'discoveryrun_command': StringProp(), } running_properties = { 'current_launch': StringProp(default=None), 'configuration_errors': ListProp(default=[]), } macros = {} # Output name def get_name(self): return self.discoveryrun_name # Get an eventhandler object and launch it def launch(self): m = MacroResolver() data = [] cmd = m.resolve_command(self.discoveryrun_command, data) self.current_launch = EventHandler(cmd, timeout=300) self.current_launch.execute() def check_finished(self): max_output = 10**9 #print "Max output", max_output self.current_launch.check_finished(max_output) # Look if the current launch is done or not def is_finished(self): if self.current_launch == None: return True if self.current_launch.status in ('done', 'timeout'): return True return False # we use an EventHandler object, so we have output with a single line # and longoutput with the rest. We just need to return all def get_output(self): return '\n'.join( [self.current_launch.output, self.current_launch.long_output])
class Module(Item): id = 1 # zero is always special in database, so we do not take risk here my_type = 'module' properties = Item.properties.copy() properties.update({ 'module_name': StringProp(), 'module_type': StringProp(), 'modules': ListProp(default=[''], split_on_coma=True), }) macros = {} # For debugging purpose only (nice name) def get_name(self): return self.module_name def __repr__(self): return '<module name=%s />' % self.get_name()
class Hostescalation(Item): id = 1 # zero is always special in database, so we do not take risk here my_type = 'hostescalation' properties = Item.properties.copy() properties.update({ 'host_name': StringProp(), 'hostgroup_name': StringProp(), 'first_notification': IntegerProp(), 'last_notification': IntegerProp(), 'notification_interval': IntegerProp(default='30'), # like Nagios value 'escalation_period': StringProp(default=''), 'escalation_options': ListProp(default='d,u,r,w,c'), 'contacts': StringProp(), 'contact_groups': StringProp(), }) # For debugging purpose only (nice name) def get_name(self): return ''
class PollerLink(SatelliteLink): """This class is the link between Arbiter and Poller. With it, arbiter can see if a poller is alive, and can send it new configuration """ id = 0 my_type = 'poller' # To_send: send or not to satellite conf properties = SatelliteLink.properties.copy() properties.update({ 'poller_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7771, fill_brok=['full_status']), 'min_workers': IntegerProp(default=0, fill_brok=['full_status'], to_send=True), 'max_workers': IntegerProp(default=30, fill_brok=['full_status'], to_send=True), 'processes_by_worker': IntegerProp(default=256, fill_brok=['full_status'], to_send=True), 'max_q_size': IntegerProp(default=0, fill_brok=['full_status'], to_send=True), 'q_factor': IntegerProp(default=0, fill_brok=['full_status'], to_send=True), 'results_batch': IntegerProp(default=0, fill_brok=['full_status'], to_send=True), 'poller_tags': ListProp(default=['None'], to_send=True), 'harakiri_threshold': StringProp(default=None, fill_brok=['full_status'], to_send=True), }) def get_name(self): return getattr(self, 'poller_name', 'UNNAMED-POLLER') def register_to_my_realm(self): self.realm.pollers.append(self)
class Hostescalation(Item): id = 1 #0 is always special in database, so we do not take risk here my_type = 'hostescalation' properties = { 'host_name': StringProp(), 'hostgroup_name': StringProp(), 'first_notification': IntegerProp(), 'last_notification': IntegerProp(), 'notification_interval': IntegerProp(), 'escalation_period': StringProp(default=''), 'escalation_options': ListProp(default='d,u,r,w,c'), 'contacts': StringProp(), 'contact_groups': StringProp(), } running_properties = {} macros = {} #For debugging purpose only (nice name) def get_name(self): return ''
class PollerLink(SatelliteLink): id = 0 my_type = 'poller' #To_send : send or not to satellite conf properties = SatelliteLink.properties.copy() properties.update({ 'poller_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7771, fill_brok=['full_status']), 'passive' : BoolProp(default='0', fill_brok=['full_status'], to_send=True), 'min_workers': IntegerProp(default='1', fill_brok=['full_status'], to_send=True), 'max_workers': IntegerProp(default='30', fill_brok=['full_status'], to_send=True), 'processes_by_worker': IntegerProp(default='256', fill_brok=['full_status'], to_send=True), 'poller_tags': ListProp(default='None', to_send=True), }) def get_name(self): return self.poller_name def register_to_my_realm(self): self.realm.pollers.append(self) if self.poller_tags != []: print "I %s manage tags : %s " % (self.get_name(), self.poller_tags)
class Contact(Item): id = 1 # zero is always special in database, so we do not take risk here my_type = 'contact' properties = Item.properties.copy() properties.update({ 'contact_name': StringProp(fill_brok=['full_status']), 'alias': StringProp(default='none', fill_brok=['full_status']), 'contactgroups': ListProp(default=[], fill_brok=['full_status']), 'host_notifications_enabled': BoolProp(default=True, fill_brok=['full_status']), 'service_notifications_enabled': BoolProp(default=True, fill_brok=['full_status']), 'host_notification_period': StringProp(fill_brok=['full_status']), 'service_notification_period': StringProp(fill_brok=['full_status']), 'host_notification_options': ListProp(default=[''], fill_brok=['full_status'], split_on_coma=True), 'service_notification_options': ListProp(default=[''], fill_brok=['full_status'], split_on_coma=True), # To be consistent with notificationway object attributes 'host_notification_commands': ListProp(fill_brok=['full_status']), 'service_notification_commands': ListProp(fill_brok=['full_status']), 'min_business_impact': IntegerProp(default=0, fill_brok=['full_status']), 'email': StringProp(default='none', fill_brok=['full_status']), 'pager': StringProp(default='none', fill_brok=['full_status']), 'address1': StringProp(default='none', fill_brok=['full_status']), 'address2': StringProp(default='none', fill_brok=['full_status']), 'address3': StringProp(default='none', fill_brok=['full_status']), 'address4': StringProp(default='none', fill_brok=['full_status']), 'address5': StringProp(default='none', fill_brok=['full_status']), 'address6': StringProp(default='none', fill_brok=['full_status']), 'can_submit_commands': BoolProp(default=False, fill_brok=['full_status']), 'is_admin': BoolProp(default=False, fill_brok=['full_status']), 'expert': BoolProp(default=False, fill_brok=['full_status']), 'retain_status_information': BoolProp(default=True, fill_brok=['full_status']), 'notificationways': ListProp(default=[], fill_brok=['full_status']), 'password': StringProp(default='NOPASSWORDSET', fill_brok=['full_status']), }) running_properties = Item.running_properties.copy() running_properties.update({ 'modified_attributes': IntegerProp(default=0L, fill_brok=['full_status'], retention=True), 'downtimes': StringProp(default=[], fill_brok=['full_status'], retention=True), })
class Timeperiod(Item): id = 1 my_type = 'timeperiod' properties = Item.properties.copy() properties.update({ 'timeperiod_name': StringProp(fill_brok=['full_status']), 'alias': StringProp(default='', fill_brok=['full_status']), 'use': StringProp(default=''), 'register': IntegerProp(default='1'), # These are needed if a broker module calls methods on timeperiod objects 'dateranges': ListProp(fill_brok=['full_status'], default=[]), 'exclude': ListProp(fill_brok=['full_status'], default=[]), 'is_active': BoolProp(default='0') }) running_properties = Item.running_properties.copy() def __init__(self, params={}): self.id = Timeperiod.id Timeperiod.id = Timeperiod.id + 1 self.unresolved = [] self.dateranges = [] self.exclude = '' self.customs = {} self.plus = {} self.invalid_entries = [] for key in params: if key in [ 'name', 'alias', 'timeperiod_name', 'exclude', 'use', 'register', 'imported_from', 'is_active', 'dateranges' ]: setattr(self, key, params[key]) else: self.unresolved.append(key + ' ' + params[key]) self.cache = {} # For tunning purpose only self.invalid_cache = {} # same but for invalid search self.configuration_errors = [] self.configuration_warnings = [] # By default the tp is None so we know we just start self.is_active = None self.tags = set() def get_name(self): return getattr(self, 'timeperiod_name', 'unknown_timeperiod') # We fillfull properties with template ones if need # for the unresolved values (like sunday ETCETC) def get_unresolved_properties_by_inheritance(self, items): # Ok, I do not have prop, Maybe my templates do? # Same story for plus for i in self.templates: self.unresolved.extend(i.unresolved) # Ok timeperiods are a bit different from classic items, because we do not have a real list # of our raw properties, like if we got february 1 - 15 / 3 for example def get_raw_import_values(self): properties = ['timeperiod_name', 'alias', 'use', 'register'] r = {} for prop in properties: if hasattr(self, prop): v = getattr(self, prop) print prop, ":", v r[prop] = v # Now the unresolved one. The only way to get ride of same key things is to put # directly the full value as the key for other in self.unresolved: r[other] = '' return r def is_time_valid(self, t): if self.has('exclude'): for dr in self.exclude: if dr.is_time_valid(t): return False for dr in self.dateranges: if dr.is_time_valid(t): return True return False # will give the first time > t which is valid def get_min_from_t(self, t): mins_incl = [] for dr in self.dateranges: mins_incl.append(dr.get_min_from_t(t)) return min(mins_incl) # will give the first time > t which is not valid def get_not_in_min_from_t(self, f): pass def find_next_valid_time_from_cache(self, t): try: return self.cache[t] except KeyError: return None def find_next_invalid_time_from_cache(self, t): try: return self.invalid_cache[t] except KeyError: return None # will look for active/un-active change. And log it # [1327392000] TIMEPERIOD TRANSITION: <name>;<from>;<to> # from is -1 on startup. to is 1 if the timeperiod starts # and 0 if it ends. def check_and_log_activation_change(self): now = int(time.time()) was_active = self.is_active self.is_active = self.is_time_valid(now) # If we got a change, log it! if self.is_active != was_active: _from = 0 _to = 0 # If it's the start, get a special value for was if was_active is None: _from = -1 if was_active: _from = 1 if self.is_active: _to = 1 # Now raise the log console_logger.info('TIMEPERIOD TRANSITION: %s;%d;%d' % (self.get_name(), _from, _to)) # clean the get_next_valid_time_from_t cache # The entries are a dict on t. t < now are useless # Because we do not care about past anymore. # If not, it's not important, it's just a cache after all :) def clean_cache(self): now = int(time.time()) t_to_del = [] for t in self.cache: if t < now: t_to_del.append(t) for t in t_to_del: del self.cache[t] # same for the invalid cache t_to_del = [] for t in self.invalid_cache: if t < now: t_to_del.append(t) for t in t_to_del: del self.invalid_cache[t] def get_next_valid_time_from_t(self, t): # first find from cache t = int(t) original_t = t #logger.debug("[%s] Check valid time for %s" % ( self.get_name(), time.asctime(time.localtime(t))) res_from_cache = self.find_next_valid_time_from_cache(t) if res_from_cache is not None: return res_from_cache still_loop = True # Loop for all minutes... while still_loop: local_min = None # Ok, not in cache... dr_mins = [] s_dr_mins = [] for dr in self.dateranges: dr_mins.append(dr.get_next_valid_time_from_t(t)) s_dr_mins = sorted([d for d in dr_mins if d is not None]) for t1 in s_dr_mins: if not self.exclude and still_loop is True: # No Exclude so we are good local_min = t1 still_loop = False else: for tp in self.exclude: if not tp.is_time_valid(t1) and still_loop is True: # OK we found a date that is not valid in any exclude timeperiod local_min = t1 still_loop = False if local_min is None: # print "Looking for next valid date" exc_mins = [] if s_dr_mins != []: for tp in self.exclude: exc_mins.append( tp.get_next_invalid_time_from_t(s_dr_mins[0])) s_exc_mins = sorted([d for d in exc_mins if d is not None]) if s_exc_mins != []: local_min = s_exc_mins[0] if local_min is None: still_loop = False else: t = local_min # No loop more than one year if t > original_t + 3600 * 24 * 366 + 1: still_loop = False local_min = None # Ok, we update the cache... self.cache[original_t] = local_min return local_min def get_next_invalid_time_from_t(self, t): #print '\n\n', self.get_name(), 'Search for next invalid from', time.asctime(time.localtime(t)), t t = int(t) original_t = t still_loop = True # First try to find in cache res_from_cache = self.find_next_invalid_time_from_cache(t) if res_from_cache is not None: return res_from_cache # Then look, maybe t is already invalid if not self.is_time_valid(t): return t local_min = t res = None # Loop for all minutes... while still_loop: #print "Invalid loop with", time.asctime(time.localtime(local_min)) dr_mins = [] #val_valids = [] #val_inval = [] # But maybe we can find a better solution with next invalid of standard dateranges #print self.get_name(), "After valid of exclude, local_min =", time.asctime(time.localtime(local_min)) for dr in self.dateranges: #print self.get_name(), "Search a next invalid from DR", time.asctime(time.localtime(local_min)) #print dr.__dict__ m = dr.get_next_invalid_time_from_t(local_min) #print self.get_name(), "Dr", dr.__dict__, "give me next invalid", time.asctime(time.localtime(m)) if m is not None: # But maybe it's invalid for this dr, but valid for other ones. #if not self.is_time_valid(m): # print "Final: Got a next invalid at", time.asctime(time.localtime(m)) dr_mins.append(m) #if not self.is_time_valid(m): # val_inval.append(m) #else: # val_valids.append(m) # print "Add a m", time.asctime(time.localtime(m)) #else: # print dr.__dict__ # print "F**K bad result\n\n\n" #print "Inval" #for v in val_inval: # print "\t", time.asctime(time.localtime(v)) #print "Valid" #for v in val_valids: # print "\t", time.asctime(time.localtime(v)) if dr_mins != []: local_min = min(dr_mins) # Take the minimum valid as lower for next search #local_min_valid = 0 #if val_valids != []: # local_min_valid = min(val_valids) #if local_min_valid != 0: # local_min = local_min_valid #else: # local_min = min(dr_mins) #print "UPDATE After dr: found invalid local min:", time.asctime(time.localtime(local_min)), "is valid", self.is_time_valid(local_min) #print self.get_name(), 'Invalid: local min', local_min #time.asctime(time.localtime(local_min)) # We do not loop unless the local_min is not valid if not self.is_time_valid(local_min): still_loop = False else: # continue until we reach too far..., in one minute # After one month, go quicker... if local_min > original_t + 3600 * 24 * 30: local_min += 3600 else: # else search for 1min precision local_min += 60 # after one year, stop. if local_min > original_t + 3600 * 24 * 366 + 1: # 60*24*366 + 1: still_loop = False #print "Loop?", still_loop # if we've got a real value, we check it with the exclude if local_min is not None: # Now check if local_min is not valid for tp in self.exclude: #print self.get_name(),"we check for invalid", time.asctime(time.localtime(local_min)), 'with tp', tp.name if tp.is_time_valid(local_min): still_loop = True # local_min + 60 local_min = tp.get_next_invalid_time_from_t(local_min + 60) # No loop more than one year if local_min > original_t + 60 * 24 * 366 + 1: still_loop = False res = None if not still_loop: # We find a possible value # We take the result the minimal possible if res is None or local_min < res: res = local_min #print "Finished Return the next invalid", time.asctime(time.localtime(local_min)) # Ok, we update the cache... self.invalid_cache[original_t] = local_min return local_min def has(self, prop): return hasattr(self, prop) # We are correct only if our daterange are # and if we have no unmatch entries def is_correct(self): b = True for dr in self.dateranges: b &= dr.is_correct() # Even one invalid is non correct for e in self.invalid_entries: b = False logger.error("[timeperiod::%s] invalid entry '%s'" % (self.get_name(), e)) return b def __str__(self): s = '' s += str(self.__dict__) + '\n' for elt in self.dateranges: s += str(elt) (start, end) = elt.get_start_and_end_time() start = time.asctime(time.localtime(start)) end = time.asctime(time.localtime(end)) s += "\nStart and end:" + str((start, end)) s += '\nExclude' for elt in self.exclude: s += str(elt) return s def resolve_daterange(self, dateranges, entry): #print "Trying to resolve ", entry res = re.search( '(\d{4})-(\d{2})-(\d{2}) - (\d{4})-(\d{2})-(\d{2}) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 1" (syear, smon, smday, eyear, emon, emday, skip_interval, other) = res.groups() dateranges.append( CalendarDaterange(syear, smon, smday, 0, 0, eyear, emon, emday, 0, 0, skip_interval, other)) return res = re.search('(\d{4})-(\d{2})-(\d{2}) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 2" (syear, smon, smday, skip_interval, other) = res.groups() eyear = syear emon = smon emday = smday dateranges.append( CalendarDaterange(syear, smon, smday, 0, 0, eyear, emon, emday, 0, 0, skip_interval, other)) return res = re.search( '(\d{4})-(\d{2})-(\d{2}) - (\d{4})-(\d{2})-(\d{2})[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 3" (syear, smon, smday, eyear, emon, emday, other) = res.groups() dateranges.append( CalendarDaterange(syear, smon, smday, 0, 0, eyear, emon, emday, 0, 0, 0, other)) return res = re.search('(\d{4})-(\d{2})-(\d{2})[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 4" (syear, smon, smday, other) = res.groups() eyear = syear emon = smon emday = smday dateranges.append( CalendarDaterange(syear, smon, smday, 0, 0, eyear, emon, emday, 0, 0, 0, other)) return res = re.search( '([a-z]*) ([\d-]+) ([a-z]*) - ([a-z]*) ([\d-]+) ([a-z]*) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 5" (swday, swday_offset, smon, ewday, ewday_offset, emon, skip_interval, other) = res.groups() dateranges.append( MonthWeekDayDaterange(0, smon, 0, swday, swday_offset, 0, emon, 0, ewday, ewday_offset, skip_interval, other)) return res = re.search( '([a-z]*) ([\d-]+) - ([a-z]*) ([\d-]+) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 6" (t0, smday, t1, emday, skip_interval, other) = res.groups() if t0 in Daterange.weekdays and t1 in Daterange.weekdays: swday = t0 ewday = t1 swday_offset = smday ewday_offset = emday dateranges.append( WeekDayDaterange(0, 0, 0, swday, swday_offset, 0, 0, 0, ewday, ewday_offset, skip_interval, other)) return elif t0 in Daterange.months and t1 in Daterange.months: smon = t0 emon = t1 dateranges.append( MonthDateDaterange(0, smon, smday, 0, 0, 0, emon, emday, 0, 0, skip_interval, other)) return elif t0 == 'day' and t1 == 'day': dateranges.append( MonthDayDaterange(0, 0, smday, 0, 0, 0, 0, emday, 0, 0, skip_interval, other)) return res = re.search( '([a-z]*) ([\d-]+) - ([\d-]+) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 7" (t0, smday, emday, skip_interval, other) = res.groups() if t0 in Daterange.weekdays: swday = t0 swday_offset = smday ewday = swday ewday_offset = emday dateranges.append( WeekDayDaterange(0, 0, 0, swday, swday_offset, 0, 0, 0, ewday, ewday_offset, skip_interval, other)) return elif t0 in Daterange.months: smon = t0 emon = smon dateranges.append( MonthDateDaterange(0, smon, smday, 0, 0, 0, emon, emday, 0, 0, skip_interval, other)) return elif t0 == 'day': dateranges.append( MonthDayDaterange(0, 0, smday, 0, 0, 0, 0, emday, 0, 0, skip_interval, other)) return res = re.search( '([a-z]*) ([\d-]+) ([a-z]*) - ([a-z]*) ([\d-]+) ([a-z]*) [\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 8" (swday, swday_offset, smon, ewday, ewday_offset, emon, other) = res.groups() #print "Debug:", (swday, swday_offset, smon, ewday, ewday_offset, emon, other) dateranges.append( MonthWeekDayDaterange(0, smon, 0, swday, swday_offset, 0, emon, 0, ewday, ewday_offset, 0, other)) return res = re.search('([a-z]*) ([\d-]+) - ([\d-]+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 9" (t0, smday, emday, other) = res.groups() if t0 in Daterange.weekdays: swday = t0 swday_offset = smday ewday = swday ewday_offset = emday dateranges.append( WeekDayDaterange(0, 0, 0, swday, swday_offset, 0, 0, 0, ewday, ewday_offset, 0, other)) return elif t0 in Daterange.months: smon = t0 emon = smon dateranges.append( MonthDateDaterange(0, smon, smday, 0, 0, 0, emon, emday, 0, 0, 0, other)) return elif t0 == 'day': dateranges.append( MonthDayDaterange(0, 0, smday, 0, 0, 0, 0, emday, 0, 0, 0, other)) return res = re.search( '([a-z]*) ([\d-]+) - ([a-z]*) ([\d-]+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 10" (t0, smday, t1, emday, other) = res.groups() if t0 in Daterange.weekdays and t1 in Daterange.weekdays: swday = t0 ewday = t1 swday_offset = smday ewday_offset = emday dateranges.append( WeekDayDaterange(0, 0, 0, swday, swday_offset, 0, 0, 0, ewday, ewday_offset, 0, other)) return elif t0 in Daterange.months and t1 in Daterange.months: smon = t0 emon = t1 dateranges.append( MonthDateDaterange(0, smon, smday, 0, 0, 0, emon, emday, 0, 0, 0, other)) return elif t0 == 'day' and t1 == 'day': dateranges.append( MonthDayDaterange(0, 0, smday, 0, 0, 0, 0, emday, 0, 0, 0, other)) return res = re.search('([a-z]*) ([\d-]+) ([a-z]*)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 11" (t0, swday_offset, t1, other) = res.groups() if t0 in Daterange.weekdays and t1 in Daterange.months: swday = t0 smon = t1 emon = smon ewday = swday ewday_offset = swday_offset dateranges.append( MonthWeekDayDaterange(0, smon, 0, swday, swday_offset, 0, emon, 0, ewday, ewday_offset, 0, other)) return res = re.search('([a-z]*) ([\d-]+)[\s\t]+([0-9:, -]+)', entry) if res is not None: #print "Good catch 12" (t0, smday, other) = res.groups() if t0 in Daterange.weekdays: swday = t0 swday_offset = smday ewday = swday ewday_offset = swday_offset dateranges.append( WeekDayDaterange(0, 0, 0, swday, swday_offset, 0, 0, 0, ewday, ewday_offset, 0, other)) return if t0 in Daterange.months: smon = t0 emon = smon emday = smday dateranges.append( MonthDateDaterange(0, smon, smday, 0, 0, 0, emon, emday, 0, 0, 0, other)) return if t0 == 'day': emday = smday dateranges.append( MonthDayDaterange(0, 0, smday, 0, 0, 0, 0, emday, 0, 0, 0, other)) return res = re.search('([a-z]*)[\s\t]+([0-9:, -]+)', entry) if res is not None: #print "Good catch 13" (t0, other) = res.groups() if t0 in Daterange.weekdays: day = t0 dateranges.append(StandardDaterange(day, other)) return logger.info("[timeentry::%s] no match for %s" % (self.get_name(), entry)) self.invalid_entries.append(entry) def apply_inheritance(self): pass # create daterange from unresolved param def explode(self, timeperiods): for entry in self.unresolved: #print "Revolving entry", entry self.resolve_daterange(self.dateranges, entry) self.unresolved = [] # Will make tp in exclude with id of the timeperiods def linkify(self, timeperiods): new_exclude = [] if self.has('exclude') and self.exclude != '': logger.debug("[timeentry::%s] have excluded %s" % (self.get_name(), self.exclude)) excluded_tps = self.exclude.split(',') #print "I will exclude from:", excluded_tps for tp_name in excluded_tps: tp = timeperiods.find_by_name(tp_name.strip()) if tp is not None: new_exclude.append(tp) else: logger.error("[timeentry::%s] unknown %s timeperiod" % (self.get_name(), tp_name)) self.exclude = new_exclude def check_exclude_rec(self): if self.rec_tag: logger.error("[timeentry::%s] is in a loop in exclude parameter" % self.get_name()) return False self.rec_tag = True for tp in self.exclude: tp.check_exclude_rec() return True def fill_data_brok_from(self, data, brok_type): cls = self.__class__ # Now config properties for prop, entry in cls.properties.items(): # Is this property intended for broking? #if 'fill_brok' in entry: if brok_type in entry.fill_brok: if hasattr(self, prop): data[prop] = getattr(self, prop) elif entry.has_default: data[prop] = entry.default # Get a brok with initial status def get_initial_status_brok(self): cls = self.__class__ my_type = cls.my_type data = {'id': self.id} self.fill_data_brok_from(data, 'full_status') b = Brok('initial_' + my_type + '_status', data) return b
class Discoveryrule(MatchingItem): id = 1 # zero is always special in database, so we do not take risk here my_type = 'discoveryrule' properties = Item.properties.copy() properties.update({ 'discoveryrule_name': StringProp(), 'creation_type': StringProp(default='service'), 'discoveryrule_order': IntegerProp(default='0'), ## 'check_command': StringProp (), ## 'service_description': StringProp (), ## 'use': StringProp(), }) running_properties = { 'configuration_errors': ListProp(default=[]), } macros = {} # The init of a discovery will set the property of # Discoveryrule.properties as in setattr, but all others # will be in a list because we need to have all names # and not lost all in __dict__ def __init__(self, params={}): cls = self.__class__ # We have our own id of My Class type :) # use set attr for going into the slots # instead of __dict__ :) setattr(self, 'id', cls.id) cls.id += 1 self.matches = {} # for matching rules self.not_matches = {} # for rules that should NOT match self.writing_properties = {} for key in params: # delistify attributes if there is only one value params[key] = self.compact_unique_attr_value(params[key]) # Get the properties of the Class we want if not 'creation_type' in params: params['creation_type'] = 'service' map = {'service': Service, 'host': Host} t = params['creation_type'] if not t in map: return tcls = map[t] # In my own property: # -> in __dict__ # In the properties of the 'creation_type' Class: # -> in self.writing_properties # if not, in matches or not match (if key starts # with a !, it's a not rule) # -> in self.matches or self.not_matches # in writing properties if start with + (means 'add this') # in writing properties if start with - (means 'del this') for key in params: # Some key are quite special if key in cls.properties: setattr(self, key, params[key]) elif key in ['use'] or key.startswith('+') or key.startswith( '-') or key in tcls.properties or key.startswith('_'): self.writing_properties[key] = params[key] else: if key.startswith('!'): key = key.split('!')[1] self.not_matches[key] = params['!' + key] else: self.matches[key] = params[key] # Then running prop :) cls = self.__class__ # adding running properties like latency, dependency list, etc for prop, entry in cls.running_properties.items(): # Copy is slow, so we check type # Type with __iter__ are list or dict, or tuple. # Item need it's own list, so qe copy val = entry.default if hasattr(val, '__iter__'): setattr(self, prop, copy(val)) else: setattr(self, prop, val) # each instance to have his own running prop! # Output name def get_name(self): try: return self.discoveryrule_name except AttributeError: return "UnnamedDiscoveryRule"
class Itemgroup(Item): id = 0 properties = Item.properties.copy() properties.update({ 'members': ListProp(fill_brok=['full_status'], default=None, split_on_coma=True), # Shinken specific 'unknown_members': ListProp(default=None), }) def __init__(self, params={}): self.id = self.__class__.id self.__class__.id += 1 cls = self.__class__ self.init_running_properties() for key in params: if key in self.properties: val = self.properties[key].pythonize(params[key]) elif key in self.running_properties: warning = "using a the running property %s in a config file" % key self.configuration_warnings.append(warning) val = self.running_properties[key].pythonize(params[key]) else: warning = "Guessing the property %s type because it is not in %s object properties" % \ (key, cls.__name__) self.configuration_warnings.append(warning) val = ToGuessProp.pythonize(params[key]) setattr(self, key, val) # Copy the groups properties EXCEPT the members # members need to be fill after manually def copy_shell(self): cls = self.__class__ old_id = cls.id new_i = cls() # create a new group new_i.id = self.id # with the same id cls.id = old_id # Reset the Class counter # Copy all properties for prop in cls.properties: if prop is not 'members': if self.has(prop): val = getattr(self, prop) setattr(new_i, prop, val) # but no members new_i.members = [] return new_i def replace_members(self, members): self.members = members # If a prop is absent and is not required, put the default value def fill_default(self): cls = self.__class__ for prop, entry in cls.properties.items(): if not hasattr(self, prop) and not entry.required: value = entry.default setattr(self, prop, value) def add_string_member(self, member): add_fun = list.extend if isinstance(member, list) else list.append if not hasattr(self, "members"): self.members = [] add_fun(self.members, member) def add_string_unknown_member(self, member): add_fun = list.extend if isinstance(member, list) else list.append if not self.unknown_members: self.unknown_members = [] add_fun(self.unknown_members, member) def __str__(self): return str(self.__dict__) + '\n' def __iter__(self): return self.members.__iter__() def __delitem__(self, i): try: self.members.remove(i) except ValueError: pass # a item group is correct if all members actually exists, # so if unknown_members is still [] def is_correct(self): res = True if self.unknown_members: for m in self.unknown_members: logger.error("[itemgroup::%s] as %s, got unknown member %s", self.get_name(), self.__class__.my_type, m) res = False if self.configuration_errors != []: for err in self.configuration_errors: logger.error("[itemgroup] %s", err) res = False return res def has(self, prop): return hasattr(self, prop) # Get a brok with hostgroup info (like id, name) # members is special: list of (id, host_name) for database info def get_initial_status_brok(self): cls = self.__class__ data = {} # Now config properties for prop, entry in cls.properties.items(): if entry.fill_brok != []: if self.has(prop): data[prop] = getattr(self, prop) # Here members is just a bunch of host, I need name in place data['members'] = [] for i in self.members: # it look like lisp! ((( ..))), sorry.... data['members'].append((i.id, i.get_name())) b = Brok('initial_' + cls.my_type + '_status', data) return b
class NotificationWay(Item): id = 1 # zero is always special in database, so we do not take risk here my_type = 'notificationway' properties = Item.properties.copy() properties.update({ 'notificationway_name': StringProp(fill_brok=['full_status']), 'host_notifications_enabled': BoolProp(default=True, fill_brok=['full_status']), 'service_notifications_enabled': BoolProp(default=True, fill_brok=['full_status']), 'host_notification_period': StringProp(fill_brok=['full_status']), 'service_notification_period': StringProp(fill_brok=['full_status']), 'host_notification_options': ListProp(default=[''], fill_brok=['full_status'], split_on_coma=True), 'service_notification_options': ListProp(default=[''], fill_brok=['full_status'], split_on_coma=True), 'host_notification_commands': StringProp(fill_brok=['full_status']), 'service_notification_commands': StringProp(fill_brok=['full_status']), 'min_business_impact': IntegerProp(default=0, fill_brok=['full_status']), }) running_properties = Item.running_properties.copy() # This tab is used to transform old parameters name into new ones # so from Nagios2 format, to Nagios3 ones. # Or Shinken deprecated names like criticity old_properties = { 'min_criticity': 'min_business_impact', } macros = {} # For debugging purpose only (nice name) def get_name(self): return self.notificationway_name # Search for notification_options with state and if t is # in service_notification_period def want_service_notification(self, t, state, type, business_impact, cmd=None): if not self.service_notifications_enabled: return False # Maybe the command we ask for are not for us, but for another notification ways # on the same contact. If so, bail out if cmd and not cmd in self.service_notification_commands: return False # If the business_impact is not high enough, we bail out if business_impact < self.min_business_impact: return False b = self.service_notification_period.is_time_valid(t) if 'n' in self.service_notification_options: return False t = { 'WARNING': 'w', 'UNKNOWN': 'u', 'CRITICAL': 'c', 'RECOVERY': 'r', 'FLAPPING': 'f', 'DOWNTIME': 's' } if type == 'PROBLEM': if state in t: return b and t[state] in self.service_notification_options elif type == 'RECOVERY': if type in t: return b and t[type] in self.service_notification_options elif type == 'ACKNOWLEDGEMENT': return b elif type in ('FLAPPINGSTART', 'FLAPPINGSTOP', 'FLAPPINGDISABLED'): return b and 'f' in self.service_notification_options elif type in ('DOWNTIMESTART', 'DOWNTIMEEND', 'DOWNTIMECANCELLED'): # No notification when a downtime was cancelled. Is that true?? # According to the documentation we need to look at _host_ options return b and 's' in self.host_notification_options return False # Search for notification_options with state and if t is in # host_notification_period def want_host_notification(self, t, state, type, business_impact, cmd=None): if not self.host_notifications_enabled: return False # If the business_impact is not high enough, we bail out if business_impact < self.min_business_impact: return False # Maybe the command we ask for are not for us, but for another notification ways # on the same contact. If so, bail out if cmd and not cmd in self.host_notification_commands: return False b = self.host_notification_period.is_time_valid(t) if 'n' in self.host_notification_options: return False t = { 'DOWN': 'd', 'UNREACHABLE': 'u', 'RECOVERY': 'r', 'FLAPPING': 'f', 'DOWNTIME': 's' } if type == 'PROBLEM': if state in t: return b and t[state] in self.host_notification_options elif type == 'RECOVERY': if type in t: return b and t[type] in self.host_notification_options elif type == 'ACKNOWLEDGEMENT': return b elif type in ('FLAPPINGSTART', 'FLAPPINGSTOP', 'FLAPPINGDISABLED'): return b and 'f' in self.host_notification_options elif type in ('DOWNTIMESTART', 'DOWNTIMEEND', 'DOWNTIMECANCELLED'): return b and 's' in self.host_notification_options return False # Call to get our commands to launch a Notification def get_notification_commands(self, type): # service_notification_commands for service notif_commands_prop = type + '_notification_commands' notif_commands = getattr(self, notif_commands_prop) return notif_commands # Check is required prop are set: # contacts OR contactgroups is need def is_correct(self): state = True cls = self.__class__ # Raised all previously saw errors like unknown commands or timeperiods if self.configuration_errors != []: state = False for err in self.configuration_errors: logger.error("[item::%s] %s", self.get_name(), err) # A null notif way is a notif way that will do nothing (service = n, hot =n) is_null_notifway = False if hasattr(self, 'service_notification_options' ) and self.service_notification_options == ['n']: if hasattr(self, 'host_notification_options' ) and self.host_notification_options == ['n']: is_null_notifway = True return True for prop, entry in cls.properties.items(): if prop not in _special_properties: if not hasattr(self, prop) and entry.required: logger.warning("[notificationway::%s] %s property not set", self.get_name(), prop) state = False # Bad boy... # Ok now we manage special cases... # Service part if not hasattr(self, 'service_notification_commands'): logger.warning( "[notificationway::%s] do not have any service_notification_commands defined", self.get_name()) state = False else: for cmd in self.service_notification_commands: if cmd is None: logger.warning( "[notificationway::%s] a service_notification_command is missing", self.get_name()) state = False if not cmd.is_valid(): logger.warning( "[notificationway::%s] a service_notification_command is invalid", self.get_name()) state = False if getattr(self, 'service_notification_period', None) is None: logger.warning( "[notificationway::%s] the service_notification_period is invalid", self.get_name()) state = False # Now host part if not hasattr(self, 'host_notification_commands'): logger.warning( "[notificationway::%s] do not have any host_notification_commands defined", self.get_name()) state = False else: for cmd in self.host_notification_commands: if cmd is None: logger.warning( "[notificationway::%s] a host_notification_command is missing", self.get_name()) state = False if not cmd.is_valid(): logger.warning( "[notificationway::%s] a host_notification_command is invalid (%s)", cmd.get_name(), str(cmd.__dict__)) state = False if getattr(self, 'host_notification_period', None) is None: logger.warning( "[notificationway::%s] the host_notification_period is invalid", self.get_name()) state = False return state # In the scheduler we need to relink the commandCall with # the real commands def late_linkify_nw_by_commands(self, commands): props = ['service_notification_commands', 'host_notification_commands'] for prop in props: for cc in getattr(self, prop, []): cc.late_linkify_with_command(commands)
class Item(object): properties = { 'imported_from': StringProp(default='unknown'), 'use': ListProp(default=''), 'name': StringProp(default=''), 'definition_order': IntegerProp(default='100'), # TODO: find why we can't uncomment this line below. #'register': BoolProp(default='1'), } running_properties = { # All errors and warning raised during the configuration parsing # and that will raised real warning/errors during the is_correct 'configuration_warnings': ListProp(default=[]), 'configuration_errors': ListProp(default=[]), 'hash': StringProp(default=''), # We save all template we asked us to load from 'tags': ListProp(default=set(), fill_brok=['full_status']), } macros = { } def __init__(self, params={}): # We have our own id of My Class type :) # use set attr for going into the slots # instead of __dict__ :) cls = self.__class__ self.id = cls.id cls.id += 1 self.customs = {} # for custom variables self.plus = {} # for value with a + self.init_running_properties() # [0] = + -> new key-plus # [0] = _ -> new custom entry in UPPER case for key in params: # delistify attributes if there is only one value params[key] = self.compact_unique_attr_value(params[key]) # checks for attribute value special syntax (+ or _) if not isinstance(params[key], list) and \ len(params[key]) >= 1 and params[key][0] == '+': # Special case: a _MACRO can be a plus. so add to plus # but upper the key for the macro name if key[0] == "_": self.plus[key.upper()] = params[key][1:] # we remove the + else: self.plus[key] = params[key][1:] # we remove the + elif key[0] == "_": if isinstance(params[key], list): err = "no support for _ syntax in multiple valued attributes" self.configuration_errors.append(err) continue custom_name = key.upper() self.customs[custom_name] = params[key] else: setattr(self, key, params[key]) # When values to set on attributes are unique (single element list), # return the value directly rather than setting list element. def compact_unique_attr_value(self, val): if isinstance(val, list): if len(val) > 1: return val elif len(val) == 0: return '' else: return val[0] else: return val def init_running_properties(self): for prop, entry in self.__class__.running_properties.items(): # Copy is slow, so we check type # Type with __iter__ are list or dict, or tuple. # Item need it's own list, so we copy val = entry.default if hasattr(val, '__iter__'): setattr(self, prop, copy(val)) else: setattr(self, prop, val) # each instance to have his own running prop! def copy(self): """ Return a copy of the item, but give him a new id """ cls = self.__class__ i = cls({}) # Dummy item but with it's own running properties for prop in cls.properties: if hasattr(self, prop): val = getattr(self, prop) setattr(i, prop, val) # Also copy the customs tab i.customs = copy(self.customs) return i def clean(self): """ Clean useless things not requested once item has been fully initialized&configured. Like temporary attributes such as "imported_from", etc.. """ for name in ('imported_from', 'use', 'plus', 'templates',): try: delattr(self, name) except AttributeError: pass def __str__(self): return str(self.__dict__) + '\n' def is_tpl(self): """ Return if the elements is a template """ try: return self.register == '0' except Exception, exp: return False
class SatelliteLink(Item): #id = 0 each Class will have it's own id properties = { 'address': StringProp(fill_brok=['full_status']), 'timeout': IntegerProp(default='3', fill_brok=['full_status']), 'data_timeout': IntegerProp(default='120', fill_brok=['full_status']), 'check_interval': IntegerProp(default='60', fill_brok=['full_status']), 'max_check_attempts': IntegerProp(default='3', fill_brok=['full_status']), 'spare': BoolProp(default='0', fill_brok=['full_status']), 'manage_sub_realms': BoolProp(default='1', fill_brok=['full_status']), 'manage_arbiters': BoolProp(default='0', fill_brok=['full_status'], to_send=True), 'modules': ListProp(default='', to_send=True), 'polling_interval': IntegerProp(default='1', fill_brok=['full_status'], to_send=True), 'use_timezone': StringProp(default='NOTSET', to_send=True), 'realm': StringProp(default=''), } running_properties = { 'con': StringProp(default=None), 'alive': StringProp(default=True, fill_brok=['full_status']), 'broks': StringProp(default=[]), 'attempt': StringProp(default=0, fill_brok=['full_status']), # the number of failed attempt 'reachable': StringProp(default=False, fill_brok=[ 'full_status' ]), # can be network ask or not (dead or check in timeout or error) 'configuration_errors': StringProp(default=[]), 'last_check': IntegerProp(default=0, fill_brok=['full_status']), } macros = {} #Clean? Really? def clean(self): pass #Check is required prop are set: #contacts OR contactgroups is need def is_correct(self): state = True #guilty or not? :) cls = self.__class__ special_properties = ['realm'] for prop, entry in cls.properties.items(): if prop not in special_properties: if not hasattr(self, prop) and entry['required']: print self.get_name(), " : I do not have", prop state = False #Bad boy... # Ok now we manage special cases... if getattr(self, 'realm', None) is None: print self.get_name(), " : I do not have a valid realm" state = False return state def create_connexion(self): self.uri = pyro.create_uri(self.address, self.port, "ForArbiter", self.__class__.use_ssl) self.con = pyro.getProxy(self.uri) pyro.set_timeout(self.con, self.timeout) def put_conf(self, conf): if self.con is None: self.create_connexion() #print "Connexion is OK, now we put conf", conf #print "Try to put conf:", conf try: pyro.set_timeout(self.con, self.data_timeout) print "DBG: put conf to", self.con.__dict__ self.con.put_conf(conf) pyro.set_timeout(self.con, self.timeout) return True except Pyro_exp_pack, exp: self.con = None #print ''.join(Pyro.util.getPyroTraceback(exp)) return False
class SatelliteLink(Item): """SatelliteLink is a common Class for link to satellite for Arbiter with Conf Dispatcher. """ # id = 0 each Class will have it's own id properties = Item.properties.copy() properties.update({ 'address': StringProp(fill_brok=['full_status']), 'timeout': IntegerProp(default='3', fill_brok=['full_status']), 'data_timeout': IntegerProp(default='120', fill_brok=['full_status']), 'check_interval': IntegerProp(default='60', fill_brok=['full_status']), 'max_check_attempts': IntegerProp(default='3', fill_brok=['full_status']), 'spare': BoolProp(default='0', fill_brok=['full_status']), 'manage_sub_realms': BoolProp(default='1', fill_brok=['full_status']), 'manage_arbiters': BoolProp(default='0', fill_brok=['full_status'], to_send=True), 'modules': ListProp(default='', to_send=True), 'polling_interval': IntegerProp(default='1', fill_brok=['full_status'], to_send=True), 'use_timezone': StringProp(default='NOTSET', to_send=True), 'realm': StringProp(default='', fill_brok=['full_status'], brok_transformation=get_obj_name_two_args_and_void), 'satellitemap': DictProp(default=None, elts_prop=AddrProp, to_send=True, override=True), 'use_ssl': BoolProp(default='0', fill_brok=['full_status']), }) running_properties = Item.running_properties.copy() running_properties.update({ 'con': StringProp(default=None), 'alive': StringProp(default=True, fill_brok=['full_status']), 'broks': StringProp(default=[]), 'attempt': StringProp(default=0, fill_brok=['full_status']), # the number of failed attempt 'reachable': StringProp(default=False, fill_brok=[ 'full_status' ]), # can be network ask or not (dead or check in timeout or error) 'last_check': IntegerProp(default=0, fill_brok=['full_status']), 'managed_confs': StringProp(default={}), }) def __init__(self, *args, **kwargs): super(SatelliteLink, self).__init__(*args, **kwargs) self.arb_satmap = {'address': '0.0.0.0', 'port': 0} if hasattr(self, 'address'): self.arb_satmap['address'] = self.address if hasattr(self, 'port'): try: self.arb_satmap['port'] = int(self.port) except: pass def set_arbiter_satellitemap(self, satellitemap): """ arb_satmap is the satellitemap in current context: - A SatelliteLink is owned by an Arbiter - satellitemap attribute of SatelliteLink is the map defined IN THE satellite configuration but for creating connections, we need the have the satellitemap of the Arbiter """ self.arb_satmap = {'address': self.address, 'port': self.port} self.arb_satmap.update(satellitemap) def create_connection(self): self.con = HTTPClient(address=self.arb_satmap['address'], port=self.arb_satmap['port'], timeout=self.timeout, data_timeout=self.data_timeout, use_ssl=self.use_ssl) self.uri = self.con.uri def put_conf(self, conf): if self.con is None: self.create_connection() # Maybe the connexion was not ok, bail out if not self.con: return False try: #pyro.set_timeout(self.con, self.data_timeout) self.con.post('put_conf', {'conf': conf}, wait='long') #pyro.set_timeout(self.con, self.timeout) print "PUT CONF SUCESS", self.get_name() return True except HTTPExceptions, exp: self.con = None logger.error("Failed sending configuration for %s: %s" % (self.get_name(), str(exp))) return False
class NotificationWay(Item): id = 1#0 is always special in database, so we do not take risk here my_type = 'notificationway' properties = { 'notificationway_name': StringProp (fill_brok=['full_status']), 'host_notifications_enabled': BoolProp (default='1', fill_brok=['full_status']), 'service_notifications_enabled':BoolProp (default='1', fill_brok=['full_status']), 'host_notification_period': StringProp (fill_brok=['full_status']), 'service_notification_period': StringProp (fill_brok=['full_status']), 'host_notification_options': ListProp (fill_brok=['full_status']), 'service_notification_options': ListProp (fill_brok=['full_status']), 'host_notification_commands': StringProp (fill_brok=['full_status']), 'service_notification_commands':StringProp (fill_brok=['full_status']), 'min_criticity': IntegerProp(default = '0', fill_brok=['full_status']), } running_properties = {} macros = {} #For debugging purpose only (nice name) def get_name(self): return self.notificationway_name #Search for notification_options with state and if t is #in service_notification_period def want_service_notification(self, t, state, type, criticity): if not self.service_notifications_enabled: return False # If the criticity is not high enough, we bail out if criticity < self.min_criticity: return False b = self.service_notification_period.is_time_valid(t) if 'n' in self.service_notification_options: return False t = {'WARNING' : 'w', 'UNKNOWN' : 'u', 'CRITICAL' : 'c', 'RECOVERY' : 'r', 'FLAPPING' : 'f', 'DOWNTIME' : 's'} if type == 'PROBLEM': if state in t: return b and t[state] in self.service_notification_options elif type == 'RECOVERY': if type in t: return b and t[type] in self.service_notification_options elif type == 'ACKNOWLEDGEMENT': return b elif type in ('FLAPPINGSTART', 'FLAPPINGSTOP', 'FLAPPINGDISABLED'): return b and 'f' in self.service_notification_options elif type in ('DOWNTIMESTART', 'DOWNTIMEEND', 'DOWNTIMECANCELLED'): #No notification when a downtime was cancelled. Is that true?? # According to the documentation we need to look at _host_ options return b and 's' in self.host_notification_options return False #Search for notification_options with state and if t is in #host_notification_period def want_host_notification(self, t, state, type, criticity): if not self.host_notifications_enabled: return False # If the criticity is not high enough, we bail out if criticity < self.min_criticity: return False b = self.host_notification_period.is_time_valid(t) if 'n' in self.host_notification_options: return False t = {'DOWN' : 'd', 'UNREACHABLE' : 'u', 'RECOVERY' : 'r', 'FLAPPING' : 'f', 'DOWNTIME' : 's'} if type == 'PROBLEM': if state in t: return b and t[state] in self.host_notification_options elif type == 'RECOVERY': if type in t: return b and t[type] in self.host_notification_options elif type == 'ACKNOWLEDGEMENT': return b elif type in ('FLAPPINGSTART', 'FLAPPINGSTOP', 'FLAPPINGDISABLED'): return b and 'f' in self.host_notification_options elif type in ('DOWNTIMESTART', 'DOWNTIMEEND', 'DOWNTIMECANCELLED'): return b and 's' in self.host_notification_options return False def clean(self): pass #Call to get our commands to launch a Notification def get_notification_commands(self, type): #service_notification_commands for service notif_commands_prop = type+'_notification_commands' notif_commands = getattr(self, notif_commands_prop) return notif_commands #Check is required prop are set: #contacts OR contactgroups is need def is_correct(self): state = True #guilty or not? :) cls = self.__class__ #A null notif way is a notif way that will do nothing (service = n, hot =n) is_null_notifway = False if hasattr(self, 'service_notification_options') and self.service_notification_options==['n']: if hasattr(self, 'host_notification_options') and self.host_notification_options==['n']: is_null_notifway = True return True for prop, entry in cls.properties.items(): if prop not in _special_properties: if not hasattr(self, prop) and entry.required: print self.get_name(), " : I do not have", prop state = False #Bad boy... #Ok now we manage special cases... #Service part if not hasattr(self, 'service_notification_commands') : print self.get_name()," : do not have any service_notification_commands defined" state = False else: for cmd in self.service_notification_commands: if cmd is None: print self.get_name()," : a service_notification_command is missing" state = False if not cmd.is_valid(): print self.get_name()," : a service_notification_command is invalid", cmd.get_name() state = False if getattr(self, 'service_notification_period', None) is None: print self.get_name()," : the service_notification_period is invalid" state = False #Now host part if not hasattr(self, 'host_notification_commands') : print self.get_name()," : do not have any host_notification_commands defined" state = False else: for cmd in self.host_notification_commands: if cmd is None : print self.get_name()," : a host_notification_command is missing" state = False if not cmd.is_valid(): print self.get_name()," : a host_notification_command is invalid", cmd.get_name(), cmd.__dict__ state = False if getattr(self, 'host_notification_period', None) is None: print self.get_name()," : the host_notification_period is invalid" state = False return state
class Escalation(Item): id = 1 # zero is always special in database, so we do not take risk here my_type = 'escalation' properties = Item.properties.copy() properties.update({ 'escalation_name': StringProp(), 'first_notification': IntegerProp(), 'last_notification': IntegerProp(), 'first_notification_time': IntegerProp(), 'last_notification_time': IntegerProp(), # by default don't use the notification_interval defined in # the escalation, but the one defined by the object 'notification_interval': IntegerProp(default='-1'), 'escalation_period': StringProp(default=''), 'escalation_options': ListProp(default='d,u,r,w,c'), 'contacts': StringProp(), 'contact_groups': StringProp(), }) running_properties = Item.running_properties.copy() running_properties.update({ 'time_based': BoolProp(default=False), }) # For debugging purpose only (nice name) def get_name(self): return self.escalation_name # Return True if: # *time in in escalation_period or we do not have escalation_period # *status is in escalation_options # *the notification number is in our interval [[first_notification .. last_notification]] # if we are a classic escalation. # *If we are time based, we check if the time that we were in notification # is in our time interval def is_eligible(self, t, status, notif_number, in_notif_time, interval): small_states = { 'WARNING': 'w', 'UNKNOWN': 'u', 'CRITICAL': 'c', 'RECOVERY': 'r', 'FLAPPING': 'f', 'DOWNTIME': 's', 'DOWN': 'd', 'UNREACHABLE': 'u', 'OK': 'o', 'UP': 'o' } # If we are not time based, we check notification numbers: if not self.time_based: # Begin with the easy cases if notif_number < self.first_notification: return False #self.last_notification = 0 mean no end if self.last_notification != 0 and notif_number > self.last_notification: return False # Else we are time based, we must check for the good value else: # Begin with the easy cases if in_notif_time < self.first_notification_time * interval: return False # self.last_notification = 0 mean no end if self.last_notification_time != 0 and in_notif_time > self.last_notification_time * interval: return False # If our status is not good, we bail out too if status in small_states and small_states[status] not in self.escalation_options: return False # Maybe the time is not in our escalation_period if self.escalation_period is not None and not self.escalation_period.is_time_valid(t): return False # Ok, I do not see why not escalade. So it's True :) return True # t = the reference time def get_next_notif_time(self, t_wished, status, creation_time, interval): small_states = {'WARNING': 'w', 'UNKNOWN': 'u', 'CRITICAL': 'c', 'RECOVERY': 'r', 'FLAPPING': 'f', 'DOWNTIME': 's', 'DOWN': 'd', 'UNREACHABLE': 'u', 'OK': 'o', 'UP': 'o'} # If we are not time based, we bail out! if not self.time_based: return None # Check if we are valid if status in small_states and small_states[status] not in self.escalation_options: return None # Look for the min of our future validity start = self.first_notification_time * interval + creation_time # If we are after the classic next time, we are not asking for a smaller interval if start > t_wished: return None # Maybe the time we found is not a valid one.... if self.escalation_period is not None and not self.escalation_period.is_time_valid(start): return None # Ok so I ask for my start as a possibility for the next notification time return start # Check is required prop are set: # template are always correct # contacts OR contactgroups is need def is_correct(self): state = True cls = self.__class__ # If we got the _time parameters, we are time based. Unless, we are not :) if hasattr(self, 'first_notification_time') or hasattr(self, 'last_notification_time'): self.time_based = True special_properties = _special_properties_time_based else: # classic ones special_properties = _special_properties for prop, entry in cls.properties.items(): if prop not in special_properties: if not hasattr(self, prop) and entry.required: logger.info('%s: I do not have %s', self.get_name(), prop) state = False # Bad boy... # Raised all previously saw errors like unknown contacts and co if self.configuration_errors != []: state = False for err in self.configuration_errors: logger.info(err) # Ok now we manage special cases... if not hasattr(self, 'contacts') and not hasattr(self, 'contact_groups'): logger.info('%s: I do not have contacts nor contact_groups', self.get_name()) state = False # If time_based or not, we do not check all properties if self.time_based: if not hasattr(self, 'first_notification_time'): logger.info('%s: I do not have first_notification_time', self.get_name()) state = False if not hasattr(self, 'last_notification_time'): logger.info('%s: I do not have last_notification_time', self.get_name()) state = False else: # we check classical properties if not hasattr(self, 'first_notification'): logger.info('%s: I do not have first_notification', self.get_name()) state = False if not hasattr(self, 'last_notification'): logger.info('%s: I do not have last_notification', self.get_name()) state = False return state
class Realm(Itemgroup): id = 1 # zero is always a little bit special... like in database my_type = 'realm' properties = Itemgroup.properties.copy() properties.update({ 'id': IntegerProp(default=0, fill_brok=['full_status']), 'realm_name': StringProp(fill_brok=['full_status']), 'realm_members': ListProp( default=[], split_on_coma=True ), # No status_broker_name because it put hosts, not host_name 'higher_realms': ListProp(default=[], split_on_coma=True), 'default': BoolProp(default=False), 'broker_complete_links': BoolProp(default=False), #'alias': {'required': True, 'fill_brok': ['full_status']}, #'notes': {'required': False, 'default':'', 'fill_brok': ['full_status']}, #'notes_url': {'required': False, 'default':'', 'fill_brok': ['full_status']}, #'action_url': {'required': False, 'default':'', 'fill_brok': ['full_status']}, }) running_properties = Item.running_properties.copy() running_properties.update({ 'serialized_confs': DictProp(default={}), }) macros = { 'REALMNAME': 'realm_name', 'REALMMEMBERS': 'members', } def get_name(self): return self.realm_name def get_realms(self): return self.realm_members def add_string_member(self, member): self.realm_members += ',' + member def get_realm_members(self): if self.has('realm_members'): return [r.strip() for r in self.realm_members] else: return [] # We fillfull properties with template ones if need # Because hostgroup we call may not have it's members # we call get_hosts_by_explosion on it def get_realms_by_explosion(self, realms): # First we tag the hg so it will not be explode # if a son of it already call it self.already_explode = True # Now the recursive part # rec_tag is set to False every HG we explode # so if True here, it must be a loop in HG # calls... not GOOD! if self.rec_tag: err = "Error: we've got a loop in realm definition %s" % self.get_name( ) self.configuration_errors.append(err) if self.has('members'): return self.members else: return '' # Ok, not a loop, we tag it and continue self.rec_tag = True p_mbrs = self.get_realm_members() for p_mbr in p_mbrs: p = realms.find_by_name(p_mbr.strip()) if p is not None: value = p.get_realms_by_explosion(realms) if value is not None: self.add_string_member(value) if self.has('members'): return self.members else: return '' def get_all_subs_satellites_by_type(self, sat_type): r = copy.copy(getattr(self, sat_type)) for p in self.realm_members: tmps = p.get_all_subs_satellites_by_type(sat_type) for s in tmps: r.append(s) return r def count_reactionners(self): self.nb_reactionners = 0 for reactionner in self.reactionners: if not reactionner.spare: self.nb_reactionners += 1 for realm in self.higher_realms: for reactionner in realm.reactionners: if not reactionner.spare and reactionner.manage_sub_realms: self.nb_reactionners += 1 def count_pollers(self): self.nb_pollers = 0 for poller in self.pollers: if not poller.spare: self.nb_pollers += 1 for realm in self.higher_realms: for poller in realm.pollers: if not poller.spare and poller.manage_sub_realms: self.nb_pollers += 1 def count_brokers(self): self.nb_brokers = 0 for broker in self.brokers: if not broker.spare: self.nb_brokers += 1 for realm in self.higher_realms: for broker in realm.brokers: if not broker.spare and broker.manage_sub_realms: self.nb_brokers += 1 def count_receivers(self): self.nb_receivers = 0 for receiver in self.receivers: if not receiver.spare: self.nb_receivers += 1 for realm in self.higher_realms: for receiver in realm.receivers: if not receiver.spare and receiver.manage_sub_realms: self.nb_receivers += 1 # Return the list of satellites of a certain type # like reactionner -> self.reactionners def get_satellties_by_type(self, type): if hasattr(self, type + 's'): return getattr(self, type + 's') else: logger.debug("[realm] do not have this kind of satellites: %s", type) return [] def fill_potential_satellites_by_type(self, sat_type): setattr(self, 'potential_%s' % sat_type, []) for satellite in getattr(self, sat_type): getattr(self, 'potential_%s' % sat_type).append(satellite) for realm in self.higher_realms: for satellite in getattr(realm, sat_type): if satellite.manage_sub_realms: getattr(self, 'potential_%s' % sat_type).append(satellite) # Return the list of potentials satellites of a certain type # like reactionner -> self.potential_reactionners def get_potential_satellites_by_type(self, type): if hasattr(self, 'potential_' + type + 's'): return getattr(self, 'potential_' + type + 's') else: logger.debug("[realm] do not have this kind of satellites: %s", type) return [] # Return the list of potentials satellites of a certain type # like reactionner -> self.nb_reactionners def get_nb_of_must_have_satellites(self, type): if hasattr(self, 'nb_' + type + 's'): return getattr(self, 'nb_' + type + 's') else: logger.debug("[realm] do not have this kind of satellites: %s", type) return 0 # Fill dict of realms for managing the satellites confs def prepare_for_satellites_conf(self): self.to_satellites = {} self.to_satellites['reactionner'] = {} self.to_satellites['poller'] = {} self.to_satellites['broker'] = {} self.to_satellites['receiver'] = {} self.to_satellites_need_dispatch = {} self.to_satellites_need_dispatch['reactionner'] = {} self.to_satellites_need_dispatch['poller'] = {} self.to_satellites_need_dispatch['broker'] = {} self.to_satellites_need_dispatch['receiver'] = {} self.to_satellites_managed_by = {} self.to_satellites_managed_by['reactionner'] = {} self.to_satellites_managed_by['poller'] = {} self.to_satellites_managed_by['broker'] = {} self.to_satellites_managed_by['receiver'] = {} self.count_reactionners() self.fill_potential_satellites_by_type('reactionners') self.count_pollers() self.fill_potential_satellites_by_type('pollers') self.count_brokers() self.fill_potential_satellites_by_type('brokers') self.count_receivers() self.fill_potential_satellites_by_type('receivers') s = "%s: (in/potential) (schedulers:%d) (pollers:%d/%d) (reactionners:%d/%d) (brokers:%d/%d) (receivers:%d/%d)" % \ (self.get_name(), len(self.schedulers), self.nb_pollers, len(self.potential_pollers), self.nb_reactionners, len(self.potential_reactionners), self.nb_brokers, len(self.potential_brokers), self.nb_receivers, len(self.potential_receivers) ) logger.info(s) # TODO: find a better name... # TODO: and if he goes active? def fill_broker_with_poller_reactionner_links(self, broker): # First we create/void theses links broker.cfg['pollers'] = {} broker.cfg['reactionners'] = {} broker.cfg['receivers'] = {} # First our own level for p in self.pollers: cfg = p.give_satellite_cfg() broker.cfg['pollers'][p.id] = cfg for r in self.reactionners: cfg = r.give_satellite_cfg() broker.cfg['reactionners'][r.id] = cfg for b in self.receivers: cfg = b.give_satellite_cfg() broker.cfg['receivers'][b.id] = cfg # Then sub if we must to it if broker.manage_sub_realms: # Now pollers for p in self.get_all_subs_satellites_by_type('pollers'): cfg = p.give_satellite_cfg() broker.cfg['pollers'][p.id] = cfg # Now reactionners for r in self.get_all_subs_satellites_by_type('reactionners'): cfg = r.give_satellite_cfg() broker.cfg['reactionners'][r.id] = cfg # Now receivers for r in self.get_all_subs_satellites_by_type('receivers'): cfg = r.give_satellite_cfg() broker.cfg['receivers'][r.id] = cfg # Get a conf package of satellites links that can be useful for # a scheduler def get_satellites_links_for_scheduler(self): cfg = {} # First we create/void theses links cfg['pollers'] = {} cfg['reactionners'] = {} # First our own level for p in self.pollers: c = p.give_satellite_cfg() cfg['pollers'][p.id] = c for r in self.reactionners: c = r.give_satellite_cfg() cfg['reactionners'][r.id] = c #print "***** Preparing a satellites conf for a scheduler", cfg return cfg
class Timeperiod(Item): id = 1 my_type = 'timeperiod' properties = { 'timeperiod_name': StringProp (fill_brok=['full_status']), 'alias': StringProp (default='',fill_brok=['full_status']), 'use': StringProp (default=''), 'exclude': StringProp (default=''), 'register': IntegerProp(default='1'), #All errors and warning raised during the configuration parsing #and taht will raised real warning/errors during the is_correct 'configuration_warnings': ListProp(default=[]), 'configuration_errors': ListProp(default=[]), # These are needed if a broker module calls methods on timeperiod objects 'dateranges': ListProp (fill_brok=['full_status'], default=[]), 'exclude': ListProp (fill_brok=['full_status'], default=[]), } def __init__(self, params={}): self.id = Timeperiod.id Timeperiod.id = Timeperiod.id + 1 self.unresolved = [] self.dateranges = [] self.exclude = '' self.customs = {} self.plus = {} self.invalid_entries = [] for key in params: if key in ['name', 'alias', 'timeperiod_name', 'exclude', 'use', 'register']: setattr(self, key, params[key]) else: self.unresolved.append(key+' '+params[key]) self.cache = {} #For tunning purpose only self.configuration_errors = [] self.configuration_warnings = [] def get_name(self): return self.timeperiod_name def clean(self): pass #We fillfull properties with template ones if need #for the unresolved values (like sunday ETCETC) def get_unresolved_properties_by_inheritance(self, items): #Ok, I do not have prop, Maybe my templates do? #Same story for plus for i in self.templates: self.unresolved.extend(i.unresolved) def is_time_valid(self, t): if self.has('exclude'): for dr in self.exclude: if dr.is_time_valid(t): return False for dr in self.dateranges: if dr.is_time_valid(t): return True return False #will give the first time > t which is valid def get_min_from_t(self, t): mins_incl = [] for dr in self.dateranges: mins_incl.append(dr.get_min_from_t(t)) return min(mins_incl) #will give the first time > t which is not valid def get_not_in_min_from_t(self, f): pass def find_next_valid_time_from_cache(self, t): try: return self.cache[t] except KeyError: return None #clean the get_next_valid_time_from_t cache #The entries are a dict on t. t < now are useless #Because we do not care about past anymore. #If not, it's not important, it's just a cache after all :) def clean_cache(self): now = int(time.time()) t_to_del = [] for t in self.cache: if t < now: t_to_del.append(t) for t in t_to_del: del self.cache[t] def get_next_valid_time_from_t(self, t): #first find from cache t = int(t) original_t = t #print self.get_name(), "Check valid time for", time.asctime(time.localtime(t)) res_from_cache = self.find_next_valid_time_from_cache(t) if res_from_cache is not None: return res_from_cache still_loop = True local_min = None #Loop for all minutes... while still_loop: #print self.get_name(), '\nLoop' #Ok, not in cache... dr_mins = [] for dr in self.dateranges: dr_mins.append(dr.get_next_valid_time_from_t(t)) #print "TOTO", self.get_name(), 'Mins:', dr_mins #for o in dr_mins: # print "F**K",time.asctime(time.localtime(o)), "\n" #Min but not the None valus... try: local_min = min([d for d in dr_mins if d is not None]) except ValueError: #dr_mins if full of None, not good local_min = None #if local_min != None: # print "Poposed?", local_min # print "Proposed local min", time.asctime(time.localtime(local_min)) #We do not loop unless the local_min is not valid still_loop = False #if we've got a real value, we check it with the exclude if local_min is not None: #Now check if local_min is not valid for tp in self.exclude: #print self.get_name(), "Check in TP" if tp.is_time_valid(local_min): still_loop = True #t = local_min + 60 #print self.get_name(), "TP pas content:", tp.get_name(), time.asctime(time.localtime(local_min)) local_min = tp.get_next_invalid_time_from_t(local_min+60) #if local_min != None: # print "Exclude TP proposed new local min", time.asctime(time.localtime(local_min)) # print local_min #print "Is it really a invalid date?", tp.is_time_valid(local_min), "if true F**K" #print self.get_name(), "Apres content:", tp.get_name(), time.asctime(time.localtime(local_min)) #else: # print self.get_name(), "Tp ca lui va", tp.get_name() if local_min is None: still_loop = False else: t = local_min #No loop more than one year if t > original_t + 3600*24*366 + 1: still_loop = False local_min = None #print "We got it!" #Ok, we update the cache... self.cache[original_t] = local_min return local_min def get_next_invalid_time_from_t(self, t): #print '\n\n', self.get_name(), 'search for next invalid from', time.asctime(time.localtime(t)) t = int(t) original_t = t still_loop = True if not self.is_time_valid(t): return t local_min = t res = None #Loop for all minutes... while still_loop: #print "Invalid loop with", time.asctime(time.localtime(local_min)) # #Ok, not in cache... # #print self.get_name(), "Begin loop with", time.asctime(time.localtime(local_min)) # next_exclude = None # for dr in self.exclude: # m = dr.get_next_valid_time_from_t(local_min) # if m != None: # #print time.asctime(time.localtime(m)) # if next_exclude == None or m <= next_exclude: # next_exclude = m # #Maybe the min of exclude is not valid, it is the min we can find. # if next_exclude != None and not self.is_time_valid(next_exclude): # #print self.get_name(), "find a possible early exit for invalid ! with", time.asctime(time.localtime(next_exclude)) # res = next_exclude # still_loop = False dr_mins = [] #But maybe we can find a better solution with next invalid of standart dateranges #print self.get_name(), "After valid of exclude, local_min =", time.asctime(time.localtime(local_min)) for dr in self.dateranges: #print self.get_name(), "Search a next invalid from DR", time.asctime(time.localtime(local_min)) #print dr.__dict__ m = dr.get_next_invalid_time_from_t(local_min) #print self.get_name(), "Dr give me next invalid", time.asctime(time.localtime(m)) if m is not None: #But maybe it's invalid for this dr, but valid for other ones. if not self.is_time_valid(m): #print "Final : Got a next invalid at", time.asctime(time.localtime(m)) dr_mins.append(m) #print "Add a m", time.asctime(time.localtime(m)) # else: # print dr.__dict__ # print "F**K bad result\n\n\n" if dr_mins != []: local_min = min(dr_mins) #print "After dr : found invalid local min:", time.asctime(time.localtime(local_min)), "is valid", self.is_time_valid(local_min) #print self.get_name(), 'Invalid: local min', time.asctime(time.localtime(local_min)) #We do not loop unless the local_min is not valid still_loop = False #if we've got a real value, we check it with the exclude if local_min is not None: #Now check if local_min is not valid for tp in self.exclude: #print self.get_name(),"we check for invalid", time.asctime(time.localtime(local_min)), 'with tp', tp.name if tp.is_time_valid(local_min): still_loop = True #local_min + 60 local_min = tp.get_next_invalid_time_from_t(local_min+60) #No loop more than one year if local_min > original_t + 60*24*366 + 1: still_loop = False res = None if not still_loop:#We find a possible value #We take the result the minimal possible if res is None or local_min < res: res = local_min #print "Finished Return the next invalid", time.asctime(time.localtime(local_min)) return local_min def has(self, prop): return hasattr(self, prop) #We are correct only if our daterange are #and if we have no unmatch entries def is_correct(self): b = True for dr in self.dateranges: b &= dr.is_correct() #Even one invalid is non correct for e in self.invalid_entries: b = False print "Error : The timeperiod %s got an invalid entry '%s'" % (self.get_name(), e) return b def __str__(self): s = '' s += str(self.__dict__)+'\n' for elt in self.dateranges: s += str(elt) (start,end) = elt.get_start_and_end_time() start = time.asctime(time.localtime(start)) end = time.asctime(time.localtime(end)) s += "\nStart and end:"+str((start, end)) s += '\nExclude' for elt in self.exclude: s += str(elt) return s def resolve_daterange(self, dateranges, entry): #print "Trying to resolve ", entry res = re.search('(\d{4})-(\d{2})-(\d{2}) - (\d{4})-(\d{2})-(\d{2}) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 1" (syear, smon, smday, eyear, emon, emday, skip_interval, other) = res.groups() dateranges.append(CalendarDaterange(syear, smon, smday, 0, 0, eyear, emon, emday, 0, 0, skip_interval, other)) return res = re.search('(\d{4})-(\d{2})-(\d{2}) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 2" (syear, smon, smday, skip_interval, other) = res.groups() eyear = syear emon = smon emday = smday dateranges.append(CalendarDaterange(syear, smon, smday, 0, 0, eyear, emon, emday, 0, 0, skip_interval, other)) return res = re.search('(\d{4})-(\d{2})-(\d{2}) - (\d{4})-(\d{2})-(\d{2})[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 3" (syear, smon, smday, eyear, emon, emday, other) = res.groups() dateranges.append(CalendarDaterange(syear, smon, smday, 0, 0, eyear, emon, emday, 0, 0, 0, other)) return res = re.search('(\d{4})-(\d{2})-(\d{2})[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 4" (syear, smon, smday, other) = res.groups() eyear = syear emon = smon emday = smday dateranges.append(CalendarDaterange(syear, smon, smday, 0, 0, eyear, emon, emday, 0, 0, 0, other)) return res = re.search('([a-z]*) ([\d-]+) ([a-z]*) - ([a-z]*) ([\d-]+) ([a-z]*) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 5" (swday, swday_offset, smon, ewday, ewday_offset, emon, skip_interval, other) = res.groups() dateranges.append(MonthWeekDayDaterange(0, smon, 0, swday, swday_offset, 0, emon, 0, ewday, ewday_offset, skip_interval, other)) return res = re.search('([a-z]*) ([\d-]+) - ([a-z]*) ([\d-]+) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 6" (t0, smday, t1, emday, skip_interval, other) = res.groups() if t0 in Daterange.weekdays and t1 in Daterange.weekdays: swday = t0 ewday = t1 swday_offset = smday ewday_offset = emday dateranges.append(WeekDayDaterange(0, 0, 0, swday, swday_offset, 0,0,0, ewday, ewday_offset, skip_interval, other)) return elif t0 in Daterange.months and t1 in Daterange.months: smon = t0 emon = t1 dateranges.append(MonthDateDaterange(0, smon, smday, 0,0,0,emon,emday,0,0,skip_interval,other)) return elif t0 == 'day' and t1 == 'day': dateranges.append(MonthDayDaterange(0,0,smday,0,0,0,0,emday, 0,0,skip_interval, other)) return res = re.search('([a-z]*) ([\d-]+) - ([\d-]+) / (\d+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 7" (t0, smday, emday, skip_interval, other) = res.groups() if t0 in Daterange.weekdays: swday = t0 swday_offset = smday ewday = swday ewday_offset = emday dateranges.append(WeekDayDaterange(0, 0, 0, swday, swday_offset, 0,0,0, ewday, ewday_offset, skip_interval, other)) return elif t0 in Daterange.months: smon = t0 emon = smon dateranges.append(MonthDateDaterange(0, smon, smday, 0,0,0,emon,emday,0,0,skip_interval, other)) return elif t0 == 'day': dateranges.append(MonthDayDaterange(0,0,smday,0,0,0,0,emday,0,0,skip_interval,other)) return res = re.search('([a-z]*) ([\d-]+) ([a-z]*) - ([a-z]*) ([\d-]+) ([a-z]*) [\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 8" (swday, swday_offset, smon, ewday, ewday_offset, emon, other) = res.groups() #print "Debug:", (swday, swday_offset, smon, ewday, ewday_offset, emon, other) dateranges.append(MonthWeekDayDaterange(0, smon, 0, swday, swday_offset, 0, emon, 0, ewday, ewday_offset, 0, other)) return res = re.search('([a-z]*) ([\d-]+) - ([\d-]+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 9" (t0, smday, emday, other) = res.groups() if t0 in Daterange.weekdays: swday = t0 swday_offset = smday ewday = swday ewday_offset = emday dateranges.append(WeekDayDaterange(0, 0, 0, swday, swday_offset, 0,0,0, ewday, ewday_offset, 0, other)) return elif t0 in Daterange.months: smon = t0 emon = smon dateranges.append(MonthDateDaterange(0, smon, smday, 0,0,0,emon,emday,0,0, 0, other)) return elif t0 == 'day': dateranges.append(MonthDayDaterange(0,0,smday,0,0,0,0,emday,0,0,0,other)) return res = re.search('([a-z]*) ([\d-]+) - ([a-z]*) ([\d-]+)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Googd catch 10" (t0, smday, t1, emday, other) = res.groups() if t0 in Daterange.weekdays and t1 in Daterange.weekdays: swday = t0 ewday = t1 swday_offset = smday ewday_offset = emday dateranges.append(WeekDayDaterange(0, 0, 0, swday, swday_offset, 0,0,0, ewday, ewday_offset, 0, other)) return elif t0 in Daterange.months and t1 in Daterange.months: smon = t0 emon = t1 dateranges.append(MonthDateDaterange(0, smon, smday, 0,0,0,emon,emday,0,0, 0,other)) return elif t0 == 'day' and t1 == 'day': dateranges.append(MonthDayDaterange(0,0,smday,0,0,0,0,emday, 0,0, 0, other)) return res = re.search('([a-z]*) ([\d-]+) ([a-z]*)[\s\t]*([0-9:, -]+)', entry) if res is not None: #print "Good catch 11" (t0, swday_offset, t1,other) = res.groups() if t0 in Daterange.weekdays and t1 in Daterange.months: swday = t0 smon = t1 emon = smon ewday = swday ewday_offset = swday_offset dateranges.append(MonthWeekDayDaterange(0, smon, 0, swday, swday_offset,0,emon,0,ewday,ewday_offset,0,other)) return res = re.search('([a-z]*) ([\d-]+)[\s\t]+([0-9:, -]+)', entry) if res is not None: #print "Good catch 12" (t0, smday, other) = res.groups() if t0 in Daterange.weekdays: swday = t0 swday_offset = smday ewday = swday ewday_offset = swday_offset dateranges.append(WeekDayDaterange(0,0,0,swday,swday_offset,0,0,0,ewday,ewday_offset,0,other)) return if t0 in Daterange.months: smon = t0 emon = smon emday = smday dateranges.append(MonthDateDaterange(0, smon, smday, 0,0,0,emon,emday,0,0, 0,other)) return if t0 == 'day': emday = smday dateranges.append(MonthDayDaterange(0,0,smday,0,0,0,0,emday, 0,0, 0, other)) return res = re.search('([a-z]*)[\s\t]+([0-9:, -]+)', entry) if res is not None: #print "Good catch 13" (t0, other) = res.groups() if t0 in Daterange.weekdays: day = t0 dateranges.append(StandardDaterange(day, other)) return print "No match for", entry self.invalid_entries.append(entry) def apply_inheritance(self): pass #create daterange from unresolved param def explode(self, timeperiods): for entry in self.unresolved: #print "Revolving entry", entry self.resolve_daterange(self.dateranges, entry) self.unresolved = [] #Will make tp in exclude with id of the timeperiods def linkify(self, timeperiods): new_exclude = [] if self.has('exclude') and self.exclude != '': print "I have excluded" print self.get_name(), self.exclude excluded_tps = self.exclude.split(',') #print "I will exclude from:", excluded_tps for tp_name in excluded_tps: tp = timeperiods.find_by_name(tp_name.strip()) if tp is not None: new_exclude.append(tp) else: print "Error : the timeperiod", tp_name, "is unknown!" self.exclude = new_exclude def check_exclude_rec(self): if self.rec_tag: print "Error :", self.get_name(), "is in a loop in exclude parameter" return False self.rec_tag = True for tp in self.exclude: tp.check_exclude_rec() return True def fill_data_brok_from(self, data, brok_type): cls = self.__class__ #Now config properties for prop, entry in cls.properties.items(): #Is this property intended for brokking? # if 'fill_brok' in entry: if brok_type in entry.fill_brok: if hasattr(self, prop): data[prop] = getattr(self, prop) elif 'default' in entry: data[prop] = entry.default #Get a brok with initial status def get_initial_status_brok(self): cls = self.__class__ my_type = cls.my_type data = {'id' : self.id} self.fill_data_brok_from(data, 'full_status') b = Brok('initial_'+my_type+'_status', data) return b
class SatelliteLink(Item): """SatelliteLink is a common Class for link to satellite for Arbiter with Conf Dispatcher. """ # id = 0 each Class will have it's own id properties = Item.properties.copy() properties.update({ 'address': StringProp(fill_brok=['full_status']), 'timeout': IntegerProp(default='3', fill_brok=['full_status']), 'data_timeout': IntegerProp(default='120', fill_brok=['full_status']), 'check_interval': IntegerProp(default='60', fill_brok=['full_status']), 'max_check_attempts': IntegerProp(default='3', fill_brok=['full_status']), 'spare': BoolProp(default='0', fill_brok=['full_status']), 'manage_sub_realms': BoolProp(default='1', fill_brok=['full_status']), 'manage_arbiters': BoolProp(default='0', fill_brok=['full_status'], to_send=True), 'modules': ListProp(default='', to_send=True), 'polling_interval': IntegerProp(default='1', fill_brok=['full_status'], to_send=True), 'use_timezone': StringProp(default='NOTSET', to_send=True), 'realm': StringProp(default='', fill_brok=['full_status'], brok_transformation=get_obj_name_two_args_and_void), 'satellitemap': DictProp(default=None, elts_prop=AddrProp, to_send=True, override=True), }) running_properties = Item.running_properties.copy() running_properties.update({ 'con': StringProp(default=None), 'alive': StringProp(default=True, fill_brok=['full_status']), 'broks': StringProp(default=[]), 'attempt': StringProp(default=0, fill_brok=['full_status']), # the number of failed attempt 'reachable': StringProp(default=False, fill_brok=[ 'full_status' ]), # can be network ask or not (dead or check in timeout or error) 'last_check': IntegerProp(default=0, fill_brok=['full_status']), 'managed_confs': StringProp(default={}), }) def __init__(self, *args, **kwargs): super(SatelliteLink, self).__init__(*args, **kwargs) self.arb_satmap = {'address': '0.0.0.0', 'port': 0} if hasattr(self, 'address'): self.arb_satmap['address'] = self.address if hasattr(self, 'port'): try: self.arb_satmap['port'] = int(self.port) except: pass def set_arbiter_satellitemap(self, satellitemap): """ arb_satmap is the satellitemap in current context: - A SatelliteLink is owned by an Arbiter - satellitemap attribute of SatelliteLink is the map defined IN THE satellite configuration but for creating connections, we need the have the satellitemap of the Arbiter """ self.arb_satmap = {'address': self.address, 'port': self.port} self.arb_satmap.update(satellitemap) def create_connection(self): try: self.uri = pyro.create_uri(self.arb_satmap['address'], self.arb_satmap['port'], "ForArbiter", self.__class__.use_ssl) # By default Pyro got problem in connect() function that can take # long seconds to raise a timeout. And even with the _setTimeout() # call. So we change the whole default connect() timeout socket.setdefaulttimeout(self.timeout) self.con = pyro.getProxy(self.uri) # But the multiprocessing module is not copatible with it! # so we must disable it imadiatly after socket.setdefaulttimeout(None) pyro.set_timeout(self.con, self.timeout) except Pyro_exp_pack, exp: # But the multiprocessing module is not compatible with it! # so we must disable it imadiatly after socket.setdefaulttimeout(None) self.con = None logger.error("Creating connection for %s: %s" % (self.get_name(), str(exp)))
class HostExtInfo(Item): # AutoSlots create the __slots__ with properties and # running_properties names __metaclass__ = AutoSlots id = 1 # zero is reserved for host (primary node for parents) my_type = 'hostextinfo' # properties defined by configuration # *required: is required in conf # *default: default value if no set in conf # *pythonize: function to call when transfort string to python object # *fill_brok: if set, send to broker. there are two categories: full_status for initial and update status, check_result for check results # *no_slots: do not take this property for __slots__ # Only for the inital call # conf_send_preparation: if set, will pass the property to this function. It's used to "flatten" # some dangerous properties like realms that are too 'linked' to be send like that. # brok_transformation: if set, will call the function with the value of the property # the major times it will be to flatten the data (like realm_name instead of the realm object). properties = Item.properties.copy() properties.update({ 'host_name': ListProp(brok_transformation=to_hostnames_list), 'notes': StringProp(default=''), 'notes_url': StringProp(default=''), 'icon_image': StringProp(default=''), 'icon_image_alt': StringProp(default=''), 'vrml_image': StringProp(default=''), 'statusmap_image': StringProp(default=''), # No slots for this 2 because begin property by a number seems bad # it's stupid! '2d_coords': StringProp(default='', no_slots=True), '3d_coords': StringProp(default='', no_slots=True), }) # Hosts macros and prop that give the information # the prop can be callable or not macros = { 'HOSTNAME': 'host_name', 'HOSTNOTESURL': 'notes_url', 'HOSTNOTES': 'notes', } ####### # __ _ _ _ # / _(_) | | (_) # ___ ___ _ __ | |_ _ __ _ _ _ _ __ __ _| |_ _ ___ _ __ # / __/ _ \| '_ \| _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ # | (_| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | | # \___\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| # __/ | # |___/ ###### # Check is required prop are set: # host_name is needed def is_correct(self): state = True cls = self.__class__ return state # For get a nice name def get_name(self): if not self.is_tpl(): try: return self.host_name except AttributeError: # outch, no hostname return 'UNNAMEDHOST' else: try: return self.name except AttributeError: # outch, no name for this template return 'UNNAMEDHOSTTEMPLATE' # For debugin purpose only def get_dbg_name(self): return self.host_name # Same but for clean call, no debug def get_full_name(self): return self.host_name
class Discoveryrule(Item): id = 1 #0 is always special in database, so we do not take risk here my_type = 'discoveryrule' properties = { 'discoveryrule_name': StringProp(), 'creation_type': StringProp(default='service'), # 'check_command': StringProp (), # 'service_description': StringProp (), # 'use': StringProp(), } running_properties = { 'configuration_errors': ListProp(default=[]), } macros = {} # The init of a discovery will set the property of # Discoveryrule.properties as in setattr, but all others # will be in a list because we need to have all names # and not lost all in __dict__ def __init__(self, params={}): cls = self.__class__ # We have our own id of My Class type :) # use set attr for going into the slots # instead of __dict__ :) setattr(self, 'id', cls.id) cls.id += 1 self.matches = {} # for matching rules self.not_matches = {} # for rules that should NOT match self.writing_properties = {} # Get teh properties of the Class we want if not 'creation_type' in params: params['creation_type'] = 'service' map = {'service': Service, 'host': Host} t = params['creation_type'] if not t in map: return tcls = map[t] # In my own property : # -> in __dict__ # In the properties of the 'creation_type' Class: # -> in self.writing_properties # if not, in matches or not match (if key starts # with a !, it's a not rule) # -> in self.matches or self.not_matches for key in params: # Some key are quite special if key in ['use']: self.writing_properties[key] = params[key] elif key in cls.properties: setattr(self, key, params[key]) elif key in tcls.properties: self.writing_properties[key] = params[key] else: if key.startswith('!'): key = key.split('!')[1] self.not_matches[key] = params['!' + key] else: self.matches[key] = params[key] # Then running prop :) cls = self.__class__ # adding running properties like latency, dependency list, etc for prop, entry in cls.running_properties.items(): # Copy is slow, so we check type # Type with __iter__ are list or dict, or tuple. # Item need it's own list, so qe copy val = entry.default if hasattr(val, '__iter__'): setattr(self, prop, copy(val)) else: setattr(self, prop, val) #eatch istance to have his own running prop! # Output name def get_name(self): try: return self.discoveryrule_name except AttributeError: return "UnnamedDiscoveryRule" # Try to see if the key,value is matching one or # our rule. If value got ',' we must look for each value # If one match, we quit # We can find in matches or not_matches def is_matching(self, key, value, look_in='matches'): if look_in == 'matches': d = self.matches else: d = self.not_matches # If we do not even have the key, we bailout if not key.strip() in d: return False # Get my matching patern m = d[key] if ',' in m: matchings = [mt.strip() for mt in m.split(',')] else: matchings = [m] # Split the alue by , too values = value.split(',') for m in matchings: for v in values: #print "Try to match", m, v if re.search(m, v): return True return False # Look if we match all discovery data or not # a disco data look as a list of (key, values) def is_matching_disco_datas(self, datas): # If we got not data, no way we can match if len(datas) == 0: return False # First we look if it's possible to match # we must match All self.matches things for m in self.matches: #print "Compare to", m match_one = False for (k, v) in datas: # We found at least one of our match key if m == k: if self.is_matching(k, v): #print "Got matching with", m, k, v match_one = True continue if not match_one: # It match none #print "Match none, FAlse" return False #print "It's possible to be OK" # And now look if ANY of not_matches is reach. If so # it's False for m in self.not_matches: #print "Compare to NOT", m match_one = False for (k, v) in datas: #print "K,V", k,v # We found at least one of our match key if m == k: #print "Go loop" if self.is_matching(k, v, look_in='not_matches'): #print "Got matching with", m, k, v match_one = True continue if match_one: #print "I match one, I quit" return False # Ok we match ALL rules in self.matches # and NONE of self.not_matches, we can go :) return True