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 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': StringProp(default=''), # No status_broker_name because it put hosts, not host_name 'higher_realms': StringProp(default=''), 'default': BoolProp(default='0'), 'broker_complete_links': BoolProp(default='0'), #'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': StringProp(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.split(',')] else: return [] # Use to make python properties # TODO: change itemgroup function pythonize? def pythonize(self): cls = self.__class__ for prop, tab in cls.properties.items(): try: old_val = getattr(self, prop) new_val = tab.pythonize(old_val) #print "Changing ", old_val, "by", new_val setattr(self, prop, new_val) except AttributeError, exp: pass # Will be catch at the is_correct moment
class ReceiverLink(SatelliteLink): """Please Add a Docstring to describe the class here""" id = 0 my_type = 'receiver' properties = SatelliteLink.properties.copy() properties.update({ 'receiver_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7772, fill_brok=['full_status']), 'manage_sub_realms': BoolProp(default=True, fill_brok=['full_status']), 'manage_arbiters': BoolProp(default=False, fill_brok=['full_status'], to_send=True), 'direct_routing': BoolProp(default=False, fill_brok=['full_status'], to_send=True), 'accept_passive_unknown_check_results': BoolProp(default=False, fill_brok=['full_status'], to_send=True), 'harakiri_threshold': StringProp(default=None, fill_brok=['full_status'], to_send=True), }) def get_name(self): return self.receiver_name def register_to_my_realm(self): self.realm.receivers.append(self) def push_host_names(self, sched_id, hnames): try: if self.con is None: self.create_connection() logger.info(" (%s)", self.uri) # If the connection failed to initialize, bail out if self.con is None: self.add_failed_check_attempt() return # r = self.con.push_host_names(sched_id, hnames) self.con.get('ping') self.con.post('push_host_names', { 'sched_id': sched_id, 'hnames': hnames }, wait='long') except HTTPExceptions, exp: self.add_failed_check_attempt(reason=str(exp))
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 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']), '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 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 SchedulerLink(SatelliteLink): """Please Add a Docstring to describe the class here""" id = 0 # Ok we lie a little here because we are a mere link in fact my_type = 'scheduler' properties = SatelliteLink.properties.copy() properties.update({ 'scheduler_name': StringProp(fill_brok=['full_status']), 'port': IntegerProp(default=7768, fill_brok=['full_status']), 'weight': IntegerProp(default=1, fill_brok=['full_status']), 'skip_initial_broks': BoolProp(default=False, fill_brok=['full_status']), 'accept_passive_unknown_check_results': BoolProp(default=False, fill_brok=['full_status']), 'harakiri_threshold': StringProp(default=None, fill_brok=['full_status'], to_send=True), }) running_properties = SatelliteLink.running_properties.copy() running_properties.update({ 'conf': StringProp(default=None), 'need_conf': StringProp(default=True), 'external_commands': StringProp(default=[]), 'push_flavor': IntegerProp(default=0), }) def get_name(self): return self.scheduler_name def run_external_commands(self, commands): if self.con is None: self.create_connection() if not self.alive: return None logger.debug("[SchedulerLink] Sending %d commands", len(commands)) try: self.con.post('run_external_commands', {'cmds': commands}) except HTTPExceptions, exp: self.con = None logger.debug(exp) return False
class Trigger(Item): id = 1 # zero is always special in database, so we do not take risk here my_type = 'trigger' properties = Item.properties.copy() properties.update({ 'trigger_name': StringProp(fill_brok=['full_status']), 'code_src': StringProp(default='', fill_brok=['full_status']), }) running_properties = Item.running_properties.copy() running_properties.update({ 'code_bin': StringProp(default=None), 'trigger_broker_raise_enabled': BoolProp(default=False) }) # For debugging purpose only (nice name) def get_name(self): try: return self.trigger_name except AttributeError: return 'UnnamedTrigger' def compile(self): self.code_bin = compile(self.code_src, "<irc>", "exec") # ctx is the object we are evaluating the code. In the code # it will be "self". def eval(myself, ctx): self = ctx # Ok we can declare for this trigger call our functions for (n, f) in trigger_functions.iteritems(): locals()[n] = f code = myself.code_bin # Comment? => compile(myself.code_bin, "<irc>", "exec") try: exec code in dict(locals()) except Exception as err: set_value(self, "UNKNOWN: Trigger error: %s" % err, "", 3) logger.error('%s Trigger %s failed: %s ; %s' % (self.host_name, myself.trigger_name, err, traceback.format_exc())) def __getstate__(self): return { 'trigger_name': self.trigger_name, 'code_src': self.code_src, 'trigger_broker_raise_enabled': self.trigger_broker_raise_enabled } def __setstate__(self, d): self.trigger_name = d['trigger_name'] self.code_src = d['code_src'] self.trigger_broker_raise_enabled = d['trigger_broker_raise_enabled']
class CommandCall(DummyCommandCall): # AutoSlots create the __slots__ with properties and # running_properties names __metaclass__ = AutoSlots #__slots__ = ('id', 'call', 'command', 'valid', 'args', 'poller_tag', # 'reactionner_tag', 'module_type', '__dict__') id = 0 my_type = 'CommandCall' properties = { 'call': StringProp(), 'command': StringProp(), 'poller_tag': StringProp(default='None'), 'reactionner_tag': StringProp(default='None'), 'module_type': StringProp(default=None), 'valid': BoolProp(default=False), 'args': StringProp(default=[]), } def __init__(self, commands, call, poller_tag='None', reactionner_tag='None'): self.id = self.__class__.id self.__class__.id += 1 self.call = call tab = call.split('!') self.command = tab[0] self.args = tab[1:] self.command = commands.find_cmd_by_name(self.command.strip()) if self.command is not None: self.valid = True else: self.valid = False self.command = tab[0] if self.valid: #If the host/service do not give an override poller_tag, take #the one of the command self.poller_tag = poller_tag #from host/service self.reactionner_tag = reactionner_tag self.module_type = self.command.module_type if self.valid and poller_tag is 'None': self.poller_tag = self.command.poller_tag #from command if not set # Same for reactionner tag if self.valid and reactionner_tag is 'None': self.reactionner_tag = self.command.reactionner_tag #from command if not set def is_valid(self): return self.valid def __str__(self): return str(self.__dict__) def get_name(self): return self.call
class ReceiverLink(SatelliteLink): id = 0 my_type = 'receiver' properties = SatelliteLink.properties.copy() properties.update({ 'receiver_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default='7772', 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), }) def get_name(self): return self.receiver_name def register_to_my_realm(self): self.realm.receivers.append(self)
def test_bool_property(self): p = BoolProp(default='1', class_inherit=[('Host', 'accept_passive_checks')]) print p.__dict__ s = "1" val = p.pythonize(s) print s, val self.assert_(val == True) s = "0" val = p.pythonize(s) print s, val self.assert_(val == False) #Now a service one p = BoolProp(default='0', fill_brok=['full_status']) print p.__dict__ s = "1" val = p.pythonize(s) print s, val self.assert_(val == True) self.assert_('full_status' in p.fill_brok)
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': StringProp(default='', 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': StringProp(fill_brok=['full_status']), 'service_notification_options': StringProp(fill_brok=['full_status']), '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']), '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='0', fill_brok=['full_status']), 'is_admin': BoolProp(default='0', fill_brok=['full_status']), 'retain_status_information': BoolProp(default='1', fill_brok=['full_status']), 'notificationways': StringProp(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 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 SchedulerLink(SatelliteLink): """Please Add a Docstring to describe the class here""" id = 0 # Ok we lie a little here because we are a mere link in fact my_type = 'scheduler' properties = SatelliteLink.properties.copy() properties.update({ 'scheduler_name': StringProp(fill_brok=['full_status']), 'port': IntegerProp(default='7768', fill_brok=['full_status']), 'weight': IntegerProp(default='1', fill_brok=['full_status']), 'skip_initial_broks': BoolProp(default='0', fill_brok=['full_status']), }) running_properties = SatelliteLink.running_properties.copy() running_properties.update({ 'conf': StringProp(default=None), 'need_conf': StringProp(default=True), 'external_commands': StringProp(default=[]), 'push_flavor': IntegerProp(default=0), }) def get_name(self): return self.scheduler_name def run_external_commands(self, commands): if self.con is None: self.create_connection() if not self.alive: return None logger.debug("[SchedulerLink] Sending %d commands" % len(commands)) try: self.con.run_external_commands(commands) except Pyro.errors.URIError, exp: self.con = None return False except Pyro.errors.ProtocolError, exp: self.con = None return False
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 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 Notification(Action): """Please Add a Docstring to describe the class here""" # AutoSlots create the __slots__ with properties and # running_properties names __metaclass__ = AutoSlots my_type = 'notification' properties = { 'is_a': StringProp(default='notification'), 'type': StringProp(default=''), 'notification_type': IntegerProp(default=0, fill_brok=['full_status']), 'start_time': StringProp(default=0, fill_brok=['full_status']), 'end_time': StringProp(default=0, fill_brok=['full_status']), 'contact_name': StringProp(default='', fill_brok=['full_status']), 'host_name': StringProp(default='', fill_brok=['full_status']), 'service_description': StringProp(default='', fill_brok=['full_status']), 'reason_type': StringProp(default=0, fill_brok=['full_status']), 'state': StringProp(default=0, fill_brok=['full_status']), 'output': StringProp(default='', fill_brok=['full_status']), 'ack_author': StringProp(default='', fill_brok=['full_status']), 'ack_data': StringProp(default='', fill_brok=['full_status']), 'escalated': BoolProp(default=False, fill_brok=['full_status']), 'contacts_notified': StringProp(default=0, fill_brok=['full_status']), 'env': StringProp(default={}), 'exit_status': IntegerProp(default=3), 'command_call': StringProp(default=None), 'execution_time': FloatProp(default=0), 'u_time': FloatProp(default=0.0), 's_time': FloatProp(default=0.0), 'contact': StringProp(default=None), '_in_timeout': BoolProp(default=False), 'notif_nb': IntegerProp(default=0), 'status': StringProp(default='scheduled'), 't_to_go': IntegerProp(default=0), 'command': StringProp(default=''), 'sched_id': IntegerProp(default=0), 'timeout': IntegerProp(default=10), 'check_time': IntegerProp(default=0), 'module_type': StringProp(default='fork', fill_brok=['full_status']), 'worker': StringProp(default='none'), 'reactionner_tag': StringProp(default='None'), 'creation_time': IntegerProp(default=0), 'enable_environment_macros': BoolProp(default=False), # Keep a list of currently active escalations 'already_start_escalations': StringProp(default=set()), } macros = { 'NOTIFICATIONTYPE': 'type', 'NOTIFICATIONRECIPIENTS': 'recipients', 'NOTIFICATIONISESCALATED': 'escalated', 'NOTIFICATIONAUTHOR': 'author', 'NOTIFICATIONAUTHORNAME': 'author_name', 'NOTIFICATIONAUTHORALIAS': 'author_alias', 'NOTIFICATIONCOMMENT': 'comment', 'HOSTNOTIFICATIONNUMBER': 'notif_nb', 'HOSTNOTIFICATIONID': 'id', 'SERVICENOTIFICATIONNUMBER': 'notif_nb', 'SERVICENOTIFICATIONID': 'id' } def __init__(self, type='PROBLEM', status='scheduled', command='UNSET', command_call=None, ref=None, contact=None, t_to_go=0, contact_name='', host_name='', service_description='', reason_type=1, state=0, ack_author='', ack_data='', escalated=False, contacts_notified=0, start_time=0, end_time=0, notification_type=0, id=None, notif_nb=1, timeout=10, env={}, module_type='fork', reactionner_tag='None', enable_environment_macros=0): self.is_a = 'notification' self.type = type if id is None: # id != None is for copy call only self.id = Action.id Action.id += 1 self._in_timeout = False self.timeout = timeout self.status = status self.exit_status = 3 self.command = command self.command_call = command_call self.output = None self.execution_time = 0 self.u_time = 0 # user executon time self.s_time = 0 # system execution time self.ref = ref # Set host_name and description from the ref try: self.host_name = self.ref.host_name except Exception: self.host_name = host_name try: self.service_description = self.ref.service_description except Exception: self.service_description = service_description self.env = env self.module_type = module_type self.t_to_go = t_to_go self.notif_nb = notif_nb self.contact = contact # For brok part self.contact_name = contact_name self.reason_type = reason_type self.state = state self.ack_author = ack_author self.ack_data = ack_data self.escalated = escalated self.contacts_notified = contacts_notified self.start_time = start_time self.end_time = end_time self.notification_type = notification_type self.creation_time = time.time() self.worker = 'none' self.reactionner_tag = reactionner_tag self.already_start_escalations = set() self.enable_environment_macros = enable_environment_macros # return a copy of the check but just what is important for execution # So we remove the ref and all def copy_shell(self): # We create a dummy check with nothing in it, just defaults values return self.copy_shell__( Notification('', '', '', '', '', '', '', id=self.id)) def is_launchable(self, t): return t >= self.t_to_go def is_administrative(self): if self.type in ('PROBLEM', 'RECOVERY'): return False else: return True def __str__(self): return "Notification %d status:%s command:%s ref:%s t_to_go:%s" % \ (self.id, self.status, self.command, getattr(self, 'ref', 'unknown'), time.asctime(time.localtime(self.t_to_go))) def get_id(self): return self.id def get_return_from(self, n): self.exit_status = n.exit_status self.execution_time = n.execution_time # Fill data with info of item by looking at brok_type # in props of properties or running_properties def fill_data_brok_from(self, data, brok_type): cls = self.__class__ # Now config properties for prop, entry in cls.properties.items(): if brok_type in entry.fill_brok: data[prop] = getattr(self, prop) # Get a brok with initial status def get_initial_status_brok(self): data = {'id': self.id} self.fill_data_brok_from(data, 'full_status') b = Brok('notification_raise', data) return b # Call by pickle for dataify the comment # because we DO NOT WANT REF in this pickleisation! def __getstate__(self): cls = self.__class__ # id is not in *_properties res = {'id': self.id} for prop in cls.properties: if hasattr(self, prop): res[prop] = getattr(self, prop) return res # Inverted function of getstate def __setstate__(self, state): cls = self.__class__ self.id = state['id'] for prop in cls.properties: if prop in state: setattr(self, prop, state[prop]) # Hook for load of 0.4 notification: there were no # creation time, must put one if not hasattr(self, 'creation_time'): self.creation_time = time.time() if not hasattr(self, 'reactionner_tag'): self.reactionner_tag = 'None' if not hasattr(self, 'worker'): self.worker = 'none' if not getattr(self, 'module_type', None): self.module_type = 'fork' if not hasattr(self, 'already_start_escalations'): self.already_start_escalations = set() if not hasattr(self, 'execution_time'): self.execution_time = 0 # s_time and u_time are added between 1.2 and 1.4 if not hasattr(self, 'u_time'): self.u_time = 0 self.s_time = 0
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 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 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 EventHandler(Action): # AutoSlots create the __slots__ with properties and # running_properties names __metaclass__ = AutoSlots my_type = 'eventhandler' properties = { 'is_a': StringProp(default='eventhandler'), 'type': StringProp(default=''), '_in_timeout': StringProp(default=False), 'status': StringProp(default=''), 'exit_status': StringProp(default=3), 'output': StringProp(default=''), 'long_output': StringProp(default=''), 't_to_go': StringProp(default=0), 'check_time': StringProp(default=0), 'execution_time': FloatProp(default=0), 'u_time': FloatProp(default=0.0), 's_time': FloatProp(default=0.0), 'env': StringProp(default={}), 'perf_data': StringProp(default=''), 'sched_id': IntegerProp(default=0), 'timeout': IntegerProp(default=10), 'check_time': IntegerProp(default=0), 'command': StringProp(default=''), 'module_type': StringProp(default='fork'), 'worker': StringProp(default='none'), 'reactionner_tag': StringProp(default='None'), 'is_snapshot': BoolProp(default=False), } # id = 0 #Is common to Actions def __init__(self, command, id=None, ref=None, timeout=10, env={}, module_type='fork', reactionner_tag='None', is_snapshot=False): self.is_a = 'eventhandler' self.type = '' self.status = 'scheduled' if id is None: # id != None is for copy call only self.id = Action.id Action.id += 1 self.ref = ref self._in_timeout = False self.timeout = timeout self.exit_status = 3 self.command = command self.output = '' self.long_output = '' self.t_to_go = time.time() self.check_time = 0 self.execution_time = 0 self.u_time = 0 self.s_time = 0 self.perf_data = '' self.env = {} self.module_type = module_type self.worker = 'none' self.reactionner_tag = reactionner_tag self.is_snapshot = is_snapshot # return a copy of the check but just what is important for execution # So we remove the ref and all def copy_shell(self): # We create a dummy check with nothing in it, just defaults values return self.copy_shell__( EventHandler('', id=self.id, is_snapshot=self.is_snapshot)) def get_return_from(self, e): self.exit_status = e.exit_status self.output = e.output self.long_output = getattr(e, 'long_output', '') self.check_time = e.check_time self.execution_time = getattr(e, 'execution_time', 0.0) self.perf_data = getattr(e, 'perf_data', '') def get_outputs(self, out, max_plugins_output_length): self.output = out def is_launchable(self, t): return t >= self.t_to_go def __str__(self): return "Check %d status:%s command:%s" % (self.id, self.status, self.command) def get_id(self): return self.id # Call by pickle to dataify the comment # because we DO NOT WANT REF in this pickleisation! def __getstate__(self): cls = self.__class__ # id is not in *_properties res = {'id': self.id} for prop in cls.properties: if hasattr(self, prop): res[prop] = getattr(self, prop) return res # Inverted function of getstate def __setstate__(self, state): cls = self.__class__ self.id = state['id'] for prop in cls.properties: if prop in state: setattr(self, prop, state[prop]) if not hasattr(self, 'worker'): self.worker = 'none' if not getattr(self, 'module_type', None): self.module_type = 'fork' # s_time and u_time are added between 1.2 and 1.4 if not hasattr(self, 'u_time'): self.u_time = 0 self.s_time = 0
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 Check(Action): # AutoSlots create the __slots__ with properties and # running_properties names __metaclass__ = AutoSlots properties = { 'is_a': StringProp(default='check'), 'type': StringProp(default=''), '_in_timeout': BoolProp(default=False), 'status': StringProp(default=''), 'exit_status': IntegerProp(default=3), 'state': IntegerProp(default=0), 'output': StringProp(default=''), 'long_output': StringProp(default=''), 'ref': IntegerProp(default=-1), 't_to_go': IntegerProp(default=0), 'depend_on': StringProp(default=[]), 'dep_check': StringProp(default=[]), 'check_time': IntegerProp(default=0), 'execution_time': IntegerProp(default=0), 'perf_data': StringProp(default=''), 'poller_tag': StringProp(default='None'), 'reactionner_tag': StringProp(default='None'), 'env': StringProp(default={}), 'internal': BoolProp(default=False), 'module_type': StringProp(default='fork'), 'worker': StringProp(default='none'), } #id = 0 #Is common to Actions def __init__(self, status, command, ref, t_to_go, dep_check=None, id=None, timeout=10,\ poller_tag='None', reactionner_tag='None', env={}, module_type='fork'): self.is_a = 'check' self.type = '' if id is None: #id != None is for copy call only self.id = Action.id Action.id += 1 self._in_timeout = False self.timeout = timeout self.status = status self.exit_status = 3 self.command = command self.output = '' self.long_output = '' self.ref = ref #self.ref_type = ref_type self.t_to_go = t_to_go self.depend_on = [] if dep_check is None: self.depend_on_me = [] else: self.depend_on_me = [dep_check] self.check_time = 0 self.execution_time = 0 self.perf_data = '' self.poller_tag = poller_tag self.reactionner_tag = reactionner_tag self.module_type = module_type self.env = env # we keep the reference of the poller that will take us self.worker = 'none' # If it's a business rule, manage it as a special check if ref and ref.got_business_rule or command.startswith('_internal'): self.internal = True else: self.internal = False #return a copy of the check but just what is important for execution #So we remove the ref and all def copy_shell(self): #We create a dummy check with nothing in it, jsut defaults values return self.copy_shell__(Check('', '', '', '', '', id=self.id)) def get_return_from(self, c): self.exit_status = c.exit_status self.output = c.output self.long_output = c.long_output self.check_time = c.check_time self.execution_time = c.execution_time self.perf_data = c.perf_data def is_launchable(self, t): return t > self.t_to_go def __str__(self): return "Check %d status:%s command:%s ref:%s" % ( self.id, self.status, self.command, self.ref) def get_id(self): return self.id
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 Check(Action): """ ODO: Add some comment about this class for the doc""" # AutoSlots create the __slots__ with properties and # running_properties names ###FIXME : reenable AutoSlots if possible #__metaclass__ = AutoSlots my_type = 'check' properties = { 'is_a': StringProp(default='check'), 'type': StringProp(default=''), '_in_timeout': BoolProp(default=False), 'status': StringProp(default=''), 'exit_status': IntegerProp(default=3), 'state': IntegerProp(default=0), 'output': StringProp(default=''), 'long_output': StringProp(default=''), 'ref': IntegerProp(default=-1), 't_to_go': IntegerProp(default=0), 'depend_on': StringProp(default=[]), 'dep_check': StringProp(default=[]), 'check_time': IntegerProp(default=0), 'execution_time': FloatProp(default=0.0), 'u_time': FloatProp(default=0.0), 's_time': FloatProp(default=0.0), 'perf_data': StringProp(default=''), 'check_type': IntegerProp(default=0), 'poller_tag': StringProp(default='None'), 'reactionner_tag': StringProp(default='None'), 'env': StringProp(default={}), 'internal': BoolProp(default=False), 'module_type': StringProp(default='fork'), 'worker': StringProp(default='none'), 'from_trigger': BoolProp(default=False), } def __init__(self, status, command, ref, t_to_go, dep_check=None, id=None, timeout=10, poller_tag='None', reactionner_tag='None', env={}, module_type='fork', from_trigger=False, dependency_check=False): self.is_a = 'check' self.type = '' if id is None: # id != None is for copy call only self.id = Action.id Action.id += 1 self._in_timeout = False self.timeout = timeout self.status = status self.exit_status = 3 self.command = command self.output = '' self.long_output = '' self.ref = ref #self.ref_type = ref_type self.t_to_go = t_to_go self.depend_on = [] if dep_check is None: self.depend_on_me = [] else: self.depend_on_me = [dep_check] self.check_time = 0 self.execution_time = 0 self.u_time = 0 # user executon time self.s_time = 0 # system execution time self.perf_data = '' self.check_type = 0 # which kind of check result? 0=active 1=passive self.poller_tag = poller_tag self.reactionner_tag = reactionner_tag self.module_type = module_type self.env = env # we keep the reference of the poller that will take us self.worker = 'none' # If it's a business rule, manage it as a special check if ref and ref.got_business_rule or command.startswith('_internal'): self.internal = True else: self.internal = False self.from_trigger = from_trigger self.dependency_check = dependency_check def copy_shell(self): """return a copy of the check but just what is important for execution So we remove the ref and all """ # We create a dummy check with nothing in it, just defaults values return self.copy_shell__(Check('', '', '', '', '', id=self.id)) def get_return_from(self, c): self.exit_status = c.exit_status self.output = c.output self.long_output = c.long_output self.check_time = c.check_time self.execution_time = c.execution_time self.perf_data = c.perf_data self.u_time = c.u_time self.s_time = c.s_time def is_launchable(self, t): return t > self.t_to_go def __str__(self): return "Check %d status:%s command:%s ref:%s" % \ (self.id, self.status, self.command, self.ref) def get_id(self): return self.id def set_type_active(self): self.check_type = 0 def set_type_passive(self): self.check_type = 1 def is_dependent(self): return self.dependency_check
class CommandCall(DummyCommandCall): """This class is use when a service, contact or host define a command with args. """ # AutoSlots create the __slots__ with properties and # running_properties names __metaclass__ = AutoSlots #__slots__ = ('id', 'call', 'command', 'valid', 'args', 'poller_tag', # 'reactionner_tag', 'module_type', '__dict__') id = 0 my_type = 'CommandCall' properties = { 'call': StringProp(), 'command': StringProp(), 'poller_tag': StringProp(default='None'), 'reactionner_tag': StringProp(default='None'), 'module_type': StringProp(default='fork'), 'valid': BoolProp(default=False), 'args': StringProp(default=[]), 'timeout': IntegerProp(default='-1'), 'late_relink_done':BoolProp(default=False), } def __init__(self, commands, call, poller_tag='None', reactionner_tag='None'): self.id = self.__class__.id self.__class__.id += 1 self.call = call self.timeout = -1 # Now split by ! and get command and args self.get_command_and_args() self.command = commands.find_by_name(self.command.strip()) self.late_relink_done = False # To do not relink again and again the same commandcall if self.command is not None: self.valid = True else: self.valid = False if self.valid: # If the host/service do not give an override poller_tag, take # the one of the command self.poller_tag = poller_tag # from host/service self.reactionner_tag = reactionner_tag self.module_type = self.command.module_type self.timeout = int(self.command.timeout) if self.valid and poller_tag is 'None': # from command if not set self.poller_tag = self.command.poller_tag # Same for reactionner tag if self.valid and reactionner_tag is 'None': # from command if not set self.reactionner_tag = self.command.reactionner_tag def get_command_and_args(self): """We want to get the command and the args with ! splitting. but don't forget to protect against the \! to do not split them """ # First protect p_call = self.call.replace('\!', '___PROTECT_EXCLAMATION___') tab = p_call.split('!') self.command = tab[0] # Reverse the protection self.args = [s.replace('___PROTECT_EXCLAMATION___', '!') for s in tab[1:]] # If we didn't already lately relink us, do it def late_linkify_with_command(self, commands): if self.late_relink_done: return self.late_relink_done = True c = commands.find_by_name(self.command) self.command = c def is_valid(self): return self.valid def __str__(self): return str(self.__dict__) def get_name(self): return self.call def __getstate__(self): """Call by pickle to dataify the comment because we DO NOT WANT REF in this pickleisation! """ cls = self.__class__ # id is not in *_properties res = {'id': self.id} for prop in cls.properties: if hasattr(self, prop): res[prop] = getattr(self, prop) # The command is a bit special, we just put it's name # or a '' if need if self.command and not isinstance(self.command, basestring): res['command'] = self.command.get_name() # Maybe it's a repickle of a unpickle thing... (like with deepcopy). If so # only take the value elif self.command and isinstance(self.command, basestring): res['command'] = self.command else: res['command'] = '' return res def __setstate__(self, state): """Inverted function of getstate""" cls = self.__class__ # We move during 1.0 to a dict state # but retention file from 0.8 was tuple if isinstance(state, tuple): self.__setstate_pre_1_0__(state) return self.id = state['id'] for prop in cls.properties: if prop in state: setattr(self, prop, state[prop]) def __setstate_pre_1_0__(self, state): """In 1.0 we move to a dict save. Before, it was a tuple save, like ({'id': 11}, {'poller_tag': 'None', 'reactionner_tag': 'None', 'command_line': u'/usr/local/nagios/bin/rss-multiuser', 'module_type': 'fork', 'command_name': u'notify-by-rss'}) """ for d in state: for k, v in d.items(): setattr(self, k, v)
class Command(Item): # AutoSlots create the __slots__ with properties and # running_properties names __metaclass__ = AutoSlots id = 0 my_type = "command" properties = Item.properties.copy() properties.update({ 'command_name': StringProp(fill_brok=['full_status']), 'command_line': StringProp(fill_brok=['full_status']), 'poller_tag': StringProp(default='None'), 'reactionner_tag': StringProp(default='None'), 'module_type': StringProp(default=None), 'timeout': IntegerProp(default=-1), 'enable_environment_macros': BoolProp(default=False), }) def __init__(self, params={}): setattr(self, 'id', self.__class__.id) #self.id = self.__class__.id self.__class__.id += 1 self.init_running_properties() self.customs = {} for key in params: # delistify attributes if there is only one value params[key] = self.compact_unique_attr_value(params[key]) # Manage customs values if key.startswith('_'): self.customs[key.upper()] = params[key] else: setattr(self, key, params[key]) if not hasattr(self, 'timeout'): self.timeout = '-1' if not hasattr(self, 'poller_tag'): self.poller_tag = 'None' if not hasattr(self, 'enable_environment_macros'): self.enable_environment_macros = 0 if not hasattr(self, 'reactionner_tag'): self.reactionner_tag = 'None' if not hasattr(self, 'module_type'): # If the command start with a _, set the module_type # as the name of the command, without the _ if getattr(self, 'command_line', '').startswith('_'): module_type = getattr(self, 'command_line', '').split(' ')[0] # and we remove the first _ self.module_type = module_type[1:] # If no command starting with _, be fork :) else: self.module_type = 'fork' def get_name(self): return self.command_name def __str__(self): return str(self.__dict__) # 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 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[prop]: if brok_type in entry.fill_brok: if hasattr(self, prop): data[prop] = getattr(self, prop) #elif 'default' in entry[prop]: # data[prop] = entry.default # Call by pickle to dataify the comment # because we DO NOT WANT REF in this pickleisation! def __getstate__(self): cls = self.__class__ # id is not in *_properties res = {'id': self.id} for prop in cls.properties: if hasattr(self, prop): res[prop] = getattr(self, prop) return res # Inversed function of getstate def __setstate__(self, state): cls = self.__class__ # We move during 1.0 to a dict state # but retention file from 0.8 was tuple if isinstance(state, tuple): self.__setstate_pre_1_0__(state) return self.id = state['id'] for prop in cls.properties: if prop in state: setattr(self, prop, state[prop]) # In 1.0 we move to a dict save. Before, it was # a tuple save, like # ({'id': 11}, {'poller_tag': 'None', 'reactionner_tag': 'None', # 'command_line': u'/usr/local/nagios/bin/rss-multiuser', # 'module_type': 'fork', 'command_name': u'notify-by-rss'}) def __setstate_pre_1_0__(self, state): for d in state: for k, v in d.items(): setattr(self, k, v)
class Downtime: id = 1 # Just to list the properties we will send as pickle # so to others daemons, so all but NOT REF properties = { 'activate_me': StringProp(default=[]), 'entry_time': IntegerProp(default=0, fill_brok=['full_status']), 'fixed': BoolProp(default=True, fill_brok=['full_status']), 'start_time': IntegerProp(default=0, fill_brok=['full_status']), 'duration': IntegerProp(default=0, fill_brok=['full_status']), 'trigger_id': IntegerProp(default=0), 'end_time': IntegerProp(default=0, fill_brok=['full_status']), 'real_end_time': IntegerProp(default=0), 'author': StringProp(default='', fill_brok=['full_status']), 'comment': StringProp(default=''), 'is_in_effect': BoolProp(default=False), 'has_been_triggered': BoolProp(default=False), 'can_be_deleted': BoolProp(default=False), # TODO: find a very good way to handle the downtime "ref". # ref must effectively not be in properties because it points # onto a real object. # 'ref': None } def __init__(self, ref, start_time, end_time, fixed, trigger_id, duration, author, comment): now = datetime.datetime.now() self.id = int(time.mktime(now.timetuple()) * 1e6 + now.microsecond) self.__class__.id = self.id + 1 self.ref = ref # pointer to srv or host we are apply self.activate_me = [] # The other downtimes i need to activate self.entry_time = int(time.time()) self.fixed = fixed self.start_time = start_time self.duration = duration self.trigger_id = trigger_id if self.trigger_id != 0: # triggered plus fixed makes no sense self.fixed = False self.end_time = end_time if fixed: self.duration = end_time - start_time # This is important for flexible downtimes. Here start_time and # end_time mean: in this time interval it is possible to trigger # the beginning of the downtime which lasts for duration. # Later, when a non-ok event happens, real_end_time will be # recalculated from now+duration # end_time will be displayed in the web interface, but real_end_time # is used internally self.real_end_time = end_time self.author = author self.comment = comment self.is_in_effect = False # fixed: start_time has been reached, # flexible: non-ok checkresult self.has_been_triggered = False # another downtime has triggered me self.can_be_deleted = False self.add_automatic_comment() def __str__(self): if self.is_in_effect is True: active = "active" else: active = "inactive" if self.fixed is True: type = "fixed" else: type = "flexible" return "%s %s Downtime id=%d %s - %s" % ( active, type, self.id, time.ctime(self.start_time), time.ctime(self.end_time)) def trigger_me(self, other_downtime): self.activate_me.append(other_downtime) def in_scheduled_downtime(self): return self.is_in_effect # The referenced host/service object enters now a (or another) scheduled # downtime. Write a log message only if it was not already in a downtime def enter(self): res = [] self.is_in_effect = True if self.fixed is False: now = time.time() self.real_end_time = now + self.duration if self.ref.scheduled_downtime_depth == 0: self.ref.raise_enter_downtime_log_entry() self.ref.create_notifications('DOWNTIMESTART') self.ref.scheduled_downtime_depth += 1 self.ref.in_scheduled_downtime = True for dt in self.activate_me: res.extend(dt.enter()) return res # The end of the downtime was reached. def exit(self): res = [] if self.is_in_effect is True: # This was a fixed or a flexible+triggered downtime self.is_in_effect = False self.ref.scheduled_downtime_depth -= 1 if self.ref.scheduled_downtime_depth <= 0: self.ref.raise_exit_downtime_log_entry() self.ref.create_notifications('DOWNTIMEEND') self.ref.in_scheduled_downtime = False else: # This was probably a flexible downtime which was not triggered # In this case it silently disappears pass self.del_automatic_comment() self.can_be_deleted = True # when a downtime ends and the service was critical # a notification is sent with the next critical check # So we should set a flag here which signals consume_result # to send a notification self.ref.in_scheduled_downtime_during_last_check = True return res # A scheduled downtime was prematurely canceled def cancel(self): res = [] self.is_in_effect = False self.ref.scheduled_downtime_depth -= 1 if self.ref.scheduled_downtime_depth == 0: self.ref.raise_cancel_downtime_log_entry() self.ref.in_scheduled_downtime = False self.del_automatic_comment() self.can_be_deleted = True self.ref.in_scheduled_downtime_during_last_check = True # Nagios does not notify on canceled downtimes, but we does self.ref.create_notifications('DOWNTIMECANCELLED') # Also cancel other downtimes triggered by me for dt in self.activate_me: res.extend(dt.cancel()) return res # Scheduling a downtime creates a comment automatically def add_automatic_comment(self): if self.fixed is True: text = ( "This %s has been scheduled for fixed downtime from %s to %s. " "Notifications for the %s will not be sent out during that time period." % ( self.ref.my_type, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.start_time)), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.end_time)), self.ref.my_type) ) else: hours, remainder = divmod(self.duration, 3600) minutes, seconds = divmod(remainder, 60) text = ("This %s has been scheduled for flexible downtime starting between %s and %s " "and lasting for a period of %d hours and %d minutes. " "Notifications for the %s will not be sent out during that time period." % ( self.ref.my_type, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.start_time)), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.end_time)), hours, minutes, self.ref.my_type) ) if self.ref.my_type == 'host': comment_type = 1 else: comment_type = 2 c = Comment(self.ref, False, "(Nagios Process)", text, comment_type, 2, 0, False, 0) self.comment_id = c.id self.extra_comment = c self.ref.add_comment(c) def del_automatic_comment(self): # Extra comment can be None if we load it from a old version of Shinken # TODO: remove it in a future version when every one got upgrade if self.extra_comment is not None: self.extra_comment.can_be_deleted = True # self.ref.del_comment(self.comment_id) # Fill data with info of item by looking at brok_type # in props of properties or running_properties def fill_data_brok_from(self, data, brok_type): cls = self.__class__ # Now config properties for prop, entry in cls.properties.items(): if hasattr(prop, 'fill_brok'): if brok_type in entry['fill_brok']: data[prop] = getattr(self, prop) # Get a brok with initial status def get_initial_status_brok(self): data = {'id': self.id} self.fill_data_brok_from(data, 'full_status') b = Brok('downtime_raise', data) return b # Call by pickle for dataify the downtime # because we DO NOT WANT REF in this pickleisation! def __getstate__(self): cls = self.__class__ # id is not in *_properties res = {'id': self.id} for prop in cls.properties: if hasattr(self, prop): res[prop] = getattr(self, prop) return res # Inverted function of getstate def __setstate__(self, state): cls = self.__class__ self.id = state['id'] for prop in cls.properties: if prop in state: setattr(self, prop, state[prop]) if self.id >= cls.id: cls.id = self.id + 1
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