class ReactionnerLink(SatelliteLink): """ Class to manage the reactionner information """ my_type = 'reactionner' properties = SatelliteLink.properties.copy() properties.update({ 'reactionner_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7769, fill_brok=['full_status']), 'min_workers': IntegerProp(default=1, fill_brok=['full_status'], to_send=True), 'max_workers': IntegerProp(default=30, fill_brok=['full_status'], to_send=True), 'processes_by_worker': IntegerProp(default=256, fill_brok=['full_status'], to_send=True), 'reactionner_tags': ListProp(default=['None'], to_send=True), }) def register_to_my_realm( self): # pragma: no cover, seems not to be used anywhere """ Add this reactionner to the realm :return: None """ self.realm.reactionners.append(self)
class Hostescalation(Item): """Hostescalation class is used to implement notification escalation for hosts TODO: Why this class does not inherit from alignak.objects.Escalation. Maybe we can merge it """ my_type = 'hostescalation' properties = Item.properties.copy() properties.update({ 'host_name': StringProp(), 'hostgroup_name': StringProp(), 'first_notification': IntegerProp(), 'last_notification': IntegerProp(), 'notification_interval': IntegerProp(default=30), # like Nagios value 'escalation_period': StringProp(default=''), 'escalation_options': ListProp(default=['d', 'u', 'r', 'w', 'c']), 'contacts': StringProp(), 'contact_groups': StringProp(), 'first_notification_time': IntegerProp(), 'last_notification_time': IntegerProp(), })
class PollerLink(SatelliteLink): """ Class to manage the link between Arbiter and Poller. With it, arbiter can see if a poller is alive, and can send it new configuration """ my_type = 'poller' # To_send: send or not to satellite conf properties = SatelliteLink.properties.copy() properties.update({ 'poller_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7771, fill_brok=['full_status']), 'min_workers': IntegerProp(default=0, fill_brok=['full_status'], to_send=True), 'max_workers': IntegerProp(default=30, fill_brok=['full_status'], to_send=True), 'processes_by_worker': IntegerProp(default=256, fill_brok=['full_status'], to_send=True), 'poller_tags': ListProp(default=['None'], to_send=True), }) def register_to_my_realm( self): # pragma: no cover, seems not to be used anywhere """ Add this relation to the realm :return: None """ self.realm.pollers.append(self)
class Comment(AlignakObject): """Comment class implements comments for monitoring purpose. It contains data like author, type etc.. """ my_type = 'comment' properties = { 'entry_time': IntegerProp(), 'author': StringProp(default='(Alignak)'), 'comment': StringProp(default='Automatic Comment'), 'comment_type': IntegerProp(), 'entry_type': IntegerProp(), 'source': IntegerProp(), 'expires': BoolProp(), 'ref': StringProp(default=''), } def __init__(self, params, parsing=True): """Adds a comment to a particular service. :param ref: reference object (host / service) :type ref: alignak.object.schedulingitem.SchedulingItem :param author: Author of this comment :type author: str :param comment: text comment itself :type comment: str :param comment_type: comment type :: * 1 <=> HOST_COMMENT * 2 <=> SERVICE_COMMENT :type comment_type: int :param entry_type: type of entry linked to this comment :: * 1 <=> USER_COMMENT * 2 <=>DOWNTIME_COMMENT * 3 <=>FLAPPING_COMMENT * 4 <=>ACKNOWLEDGEMENT_COMMENT :type entry_type: int :param source: source of this comment :: * 0 <=> COMMENTSOURCE_INTERNAL * 1 <=> COMMENTSOURCE_EXTERNAL :type source: int :param expires: comment expires or not :type expires: bool :return: None """ super(Comment, self).__init__(params, parsing) if not hasattr(self, 'entry_time'): self.entry_time = int(time.time()) self.fill_default() def __str__(self): return "Comment id=%s %s" % (self.uuid, self.comment)
class SchedulerLink(SatelliteLink): """ Class to manage the scheduler information """ _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']), }) 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 run_external_commands(self, commands): """ Run external commands :param commands: :type commands: :return: False, None :rtype: bool | None TODO: need recode this fonction because return types are too many """ 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 SchedulerLink(SatelliteLink): """ Class to manage the scheduler information """ # Ok we lie a little here because we are a mere link in fact my_type = 'scheduler' properties = SatelliteLink.properties.copy() properties.update({ 'type': StringProp(default=u'scheduler', fill_brok=['full_status'], to_send=True), 'scheduler_name': StringProp(default='', fill_brok=['full_status']), 'port': IntegerProp(default=7768, fill_brok=['full_status'], to_send=True), 'weight': IntegerProp(default=1, fill_brok=['full_status']), 'skip_initial_broks': 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), }) running_properties = SatelliteLink.running_properties.copy() running_properties.update({ # 'conf': # StringProp(default=None), # 'cfg': # DictProp(default={}), 'need_conf': StringProp(default=True), 'external_commands': StringProp(default=[]), }) def get_override_configuration(self): """ Some parameters can give as 'overridden parameters' like use_timezone so they will be mixed (in the scheduler) with the standard conf sent by the arbiter :return: dictionary of properties :rtype: dict """ res = {} properties = self.__class__.properties for prop, entry in list(properties.items()): if entry.override: res[prop] = getattr(self, prop) return res
class Businessimpactmodulation(Item): """Businessimpactmodulation class is simply a modulation of the business impact value (of a Host/Service) during a modulation period. """ my_type = 'businessimpactmodulation' properties = Item.properties.copy() properties.update({'business_impact_modulation_name': StringProp(), 'business_impact': IntegerProp(), 'modulation_period': StringProp(default=''), }) def __init__(self, params=None, parsing=True): super(Businessimpactmodulation, self).__init__(params, parsing=parsing) # Ok just put None as modulation_period, means 24x7 if not hasattr(self, 'modulation_period'): self.modulation_period = '24x7' def get_name(self): """Accessor to business_impact_modulation_name attribute :return: business impact modulation name :rtype: str """ if hasattr(self, 'business_impact_modulation_name'): return self.business_impact_modulation_name return 'Unnamed'
class BrokerLink(SatelliteLink): """ Class to manage the broker information """ my_type = 'broker' properties = SatelliteLink.properties.copy() properties.update({ 'type': StringProp(default=u'broker', fill_brok=['full_status'], to_send=True), 'broker_name': StringProp(default='', fill_brok=['full_status']), 'port': IntegerProp(default=7772, fill_brok=['full_status'], to_send=True), 'initialized': BoolProp(default=False, fill_brok=['full_status'], to_send=True), }) def prepare_for_conf(self): """Initialize the pushed configuration dictionary with the inner properties that are to be propagated to the satellite link. :return: None """ super(BrokerLink, self).prepare_for_conf() self.cfg.update({ 'satellites': { 'receivers': {}, 'pollers': {}, 'reactionners': {} } })
class PollerLink(SatelliteLink): """ Class to manage the link between Arbiter and Poller. With this, an arbiter can communicate with a poller """ my_type = 'poller' # To_send: send or not to satellite conf properties = SatelliteLink.properties.copy() properties.update({ 'type': StringProp(default=u'poller', fill_brok=['full_status'], to_send=True), 'poller_name': StringProp(default='', fill_brok=['full_status']), 'port': IntegerProp(default=7771, 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), # 'worker_polling_interval': # IntegerProp(default=1, to_send=True), 'poller_tags': ListProp(default=['None'], to_send=True), })
class ArbiterLink(SatelliteLink): """ Class to manage the link to Arbiter daemon. With it, a master arbiter can communicate with a spare Arbiter daemon """ my_type = 'arbiter' properties = SatelliteLink.properties.copy() properties.update({ 'type': StringProp(default=u'arbiter', fill_brok=['full_status'], to_send=True), 'arbiter_name': StringProp(default='', fill_brok=['full_status']), 'host_name': StringProp(default=socket.gethostname(), to_send=True), 'port': IntegerProp(default=7770, to_send=True), 'last_master_speak': FloatProp(default=0.0) }) def is_me(self): # pragma: no cover, seems not to be used anywhere """Check if parameter name if same than name of this object TODO: is it useful? :return: true if parameter name if same than this name :rtype: bool """ logger.info( "And arbiter is launched with the hostname:%s " "from an arbiter point of view of addr:%s", self.host_name, socket.getfqdn()) return self.host_name == socket.getfqdn( ) or self.host_name == socket.gethostname() def do_not_run(self): """Check if satellite running or not If not, try to run :return: true if satellite not running :rtype: bool """ logger.debug("[%s] do_not_run", self.name) try: self.con.get('_do_not_run') return True except HTTPClientConnectionException as exp: # pragma: no cover, simple protection self.add_failed_check_attempt("Connection error when " "sending do not run: %s" % str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection self.add_failed_check_attempt("Connection timeout when " "sending do not run: %s" % str(exp)) except HTTPClientException as exp: self.add_failed_check_attempt("Error when " "sending do not run: %s" % str(exp)) return False
class Reactionner(Satellite): """ This class is an application that launches actions for the schedulers Actions can be: Notifications Event handlers When running the Reactionner will : Respond to pings from Arbiter Listen for new configurations from Arbiter The configuration consists of a list of Schedulers for which the Reactionner will launch actions for. """ do_checks = False # I do not do checks do_actions = True my_type = 'reactionner' properties = Satellite.properties.copy() properties.update({ 'type': StringProp(default='reactionner'), 'port': IntegerProp(default=7769) }) def __init__(self, **kwargs): """Reactionner daemon initialisation :param kwargs: command line arguments """ super(Reactionner, self).__init__(kwargs.get('daemon_name', 'Default-reactionner'), **kwargs)
class Poller(Satellite): """Poller class. Referenced as "app" in most Interface """ do_checks = True # I do checks do_actions = False # but no actions my_type = 'poller' properties = Satellite.properties.copy() properties.update({ 'daemon_type': StringProp(default='poller'), 'pidfile': PathProp(default='pollerd.pid'), 'port': IntegerProp(default=7771), 'local_log': PathProp(default='pollerd.log'), }) def __init__(self, config_file, is_daemon, do_replace, debug, debug_file, port=None, local_log=None, daemon_name=None): self.daemon_name = 'poller' if daemon_name: self.daemon_name = daemon_name super(Poller, self).__init__(self.daemon_name, config_file, is_daemon, do_replace, debug, debug_file, port, local_log)
class Reactionner(Satellite): """ This class is an application that launches actions for the schedulers Actions can be: Notifications Event handlers When running the Reactionner will : Respond to pings from Arbiter Listen for new configurations from Arbiter The configuration consists of a list of Schedulers for which the Reactionner will launch actions for. """ do_checks = False # I do not do checks do_actions = True my_type = 'reactionner' properties = Satellite.properties.copy() properties.update({ 'pidfile': PathProp(default='reactionnerd.pid'), 'port': IntegerProp(default=7769), 'local_log': PathProp(default='reactionnerd.log'), }) def __init__(self, config_file, is_daemon, do_replace, debug, debug_file, profile=''): super(Reactionner, self).__init__('reactionner', config_file, is_daemon, do_replace, debug, debug_file)
class ReceiverLink(SatelliteLink): """ Class to manage the receiver information """ _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), }) def register_to_my_realm(self): """ Add this reactionner to the realm :return: None """ self.realm.receivers.append(self) def push_host_names(self, sched_id, hnames): """ Send host names to receiver :param sched_id: id of the scheduler :type sched_id: int :param hnames: list of host names :type hnames: list :return: None """ 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 Serviceescalation(Item): """Serviceescalation class is used to implement notification escalation for services TODO: Why this class does not inherit from alignak.objects.Escalation. Maybe we can merge it """ _id = 1 # zero is always special in database, so we do not take risk here my_type = 'serviceescalation' properties = Item.properties.copy() properties.update({ 'host_name': StringProp(), 'hostgroup_name': StringProp(), 'service_description': StringProp(), 'first_notification': IntegerProp(), 'last_notification': IntegerProp(), 'notification_interval': IntegerProp(default=30), # like Nagios value 'escalation_period': StringProp(default=''), 'escalation_options': ListProp(default=['d', 'u', 'r', 'w', 'c'], split_on_coma=True), 'contacts': StringProp(), 'contact_groups': StringProp(), 'first_notification_time': IntegerProp(), 'last_notification_time': IntegerProp(), }) def get_name(self): """Get escalation name :return: name :rtype: str TODO: Remove this function """ return ''
class Contact(Item): """Host class implements monitoring concepts for contact. For example it defines host_notification_period, service_notification_period etc. """ _id = 1 # zero is always special in database, so we do not take risk here my_type = 'contact' properties = Item.properties.copy() properties.update({ 'contact_name': StringProp(fill_brok=['full_status']), 'alias': StringProp(default='none', fill_brok=['full_status']), 'contactgroups': ListProp(default=[], fill_brok=['full_status']), 'host_notifications_enabled': BoolProp(default=True, fill_brok=['full_status']), 'service_notifications_enabled': BoolProp(default=True, fill_brok=['full_status']), 'host_notification_period': StringProp(fill_brok=['full_status']), 'service_notification_period': StringProp(fill_brok=['full_status']), 'host_notification_options': ListProp(default=[''], fill_brok=['full_status'], split_on_coma=True), 'service_notification_options': ListProp(default=[''], fill_brok=['full_status'], split_on_coma=True), # To be consistent with notificationway object attributes 'host_notification_commands': ListProp(fill_brok=['full_status']), 'service_notification_commands': ListProp(fill_brok=['full_status']), 'min_business_impact': IntegerProp(default=0, fill_brok=['full_status']), 'email': StringProp(default='none', fill_brok=['full_status']), 'pager': StringProp(default='none', fill_brok=['full_status']), 'address1': StringProp(default='none', fill_brok=['full_status']), 'address2': StringProp(default='none', fill_brok=['full_status']), 'address3': StringProp(default='none', fill_brok=['full_status']), 'address4': StringProp(default='none', fill_brok=['full_status']), 'address5': StringProp(default='none', fill_brok=['full_status']), 'address6': StringProp(default='none', fill_brok=['full_status']), 'can_submit_commands': BoolProp(default=False, fill_brok=['full_status']), 'is_admin': BoolProp(default=False, fill_brok=['full_status']), 'expert': BoolProp(default=False, fill_brok=['full_status']), 'retain_status_information': BoolProp(default=True, fill_brok=['full_status']), 'notificationways': ListProp(default=[], fill_brok=['full_status']), 'password': StringProp(default='NOPASSWORDSET', fill_brok=['full_status']), }) running_properties = Item.running_properties.copy() running_properties.update({ 'modified_attributes': IntegerProp(default=0L, fill_brok=['full_status'], retention=True), 'downtimes': StringProp(default=[], fill_brok=['full_status'], retention=True), })
class Serviceescalation(Item): """Serviceescalation class is used to implement notification escalation for services TODO: Why this class does not inherit from alignak.objects.Escalation. Maybe we can merge it """ my_type = 'serviceescalation' properties = Item.properties.copy() properties.update({ 'host_name': StringProp(), 'hostgroup_name': StringProp(), 'service_description': StringProp(), 'first_notification': IntegerProp(), 'last_notification': IntegerProp(), 'notification_interval': IntegerProp(default=30), # like Nagios value 'escalation_period': StringProp(default=''), 'escalation_options': ListProp(default=['w', 'x', 'c', 'r'], split_on_comma=True), 'contacts': ListProp(default=[], merging='join', split_on_comma=True), 'contact_groups': ListProp(default=[], merging='join', split_on_comma=True), 'first_notification_time': IntegerProp(), 'last_notification_time': IntegerProp(), }) def __init__(self, params=None, parsing=True): if params is None: params = {} for prop in ['escalation_options']: if prop in params: params[prop] = [p.replace('u', 'x') for p in params[prop]] super(Serviceescalation, self).__init__(params, parsing=parsing)
class ReceiverLink(SatelliteLink): """ Class to manage the receiver information """ my_type = 'receiver' properties = SatelliteLink.properties.copy() properties.update({ 'type': StringProp(default='receiver', fill_brok=['full_status'], to_send=True), 'receiver_name': StringProp(default='', fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7772, fill_brok=['full_status'], to_send=True), })
class Resultmodulation(Item): """Resultmodulation class is simply a modulation of a check result exit code during a modulation_period. """ _id = 1 # zero is always special in database, so we do not take risk here my_type = 'resultmodulation' properties = Item.properties.copy() properties.update({ 'resultmodulation_name': StringProp(), 'exit_codes_match': IntListProp(default=[]), 'exit_code_modulation': IntegerProp(default=None), 'modulation_period': StringProp(default=None), }) def get_name(self): """Accessor to resultmodulation_name attribute :return: result modulation name :rtype: str """ return self.resultmodulation_name def module_return(self, return_code): """Module the exit code if necessary :: * modulation_period is legit * exit_code_modulation * return_code in exit_codes_match :param return_code: actual code returned by the check :type return_code: int :return: return_code modulated if necessary (exit_code_modulation) :rtype: int """ # Only if in modulation_period of modulation_period == None if self.modulation_period is None or self.modulation_period.is_time_valid( time.time()): # Try to change the exit code only if a new one is defined if self.exit_code_modulation is not None: # First with the exit_code_match if return_code in self.exit_codes_match: return_code = self.exit_code_modulation return return_code
class BrokerLink(SatelliteLink): """ Class to manage the broker information """ my_type = 'broker' properties = SatelliteLink.properties.copy() properties.update({ 'broker_name': StringProp(fill_brok=['full_status'], to_send=True), 'port': IntegerProp(default=7772, fill_brok=['full_status']), }) def register_to_my_realm( self): # pragma: no cover, seems not to be used anywhere """ Add this broker to the realm :return: None """ self.realm.brokers.append(self)
class Businessimpactmodulation(Item): """Businessimpactmodulation class is simply a modulation of the business impact value (of a Host/Service) during a modulation period. """ _id = 1 # zero is always special in database, so we do not take risk here my_type = 'businessimpactmodulation' properties = Item.properties.copy() properties.update({ 'business_impact_modulation_name': StringProp(), 'business_impact': IntegerProp(), 'modulation_period': StringProp(default=''), }) def get_name(self): """Accessor to business_impact_modulation_name attribute :return: business impact modulation name :rtype: str """ return self.business_impact_modulation_name
class ReceiverLink(SatelliteLink): """ Class to manage the receiver information """ 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), 'accept_passive_unknown_check_results': BoolProp(default=False, fill_brok=['full_status'], to_send=True), }) def register_to_my_realm(self): # pragma: no cover, seems not to be used anywhere """ Add this reactionner to the realm :return: None """ self.realm.receivers.append(self)
class Poller(Satellite): """Poller class. Referenced as "app" in most Interface """ do_checks = True # I do checks do_actions = False # but no actions my_type = 'poller' properties = Satellite.properties.copy() properties.update({ 'type': StringProp(default='poller'), 'port': IntegerProp(default=7771) }) def __init__(self, **kwargs): """Poller daemon initialisation :param kwargs: command line arguments """ super(Poller, self).__init__(kwargs.get('daemon_name', 'Default-poller'), **kwargs)
class Alignak(BaseSatellite): # pylint: disable=too-many-instance-attributes """Scheduler class. Referenced as "app" in most Interface """ properties = BaseSatellite.properties.copy() properties.update({ 'type': StringProp(default='scheduler'), 'port': IntegerProp(default=7768) }) def __init__(self, **kwargs): """Scheduler daemon initialisation :param kwargs: command line arguments """ super(Alignak, self).__init__(kwargs.get('daemon_name', 'Default-scheduler'), **kwargs) self.http_interface = SchedulerInterface(self) self.sched = Scheduler(self) # stats part # --- copied from scheduler.py self.nb_pulled_checks = 0 self.nb_pulled_actions = 0 # self.nb_checks_send = 0 self.nb_pushed_checks = 0 self.nb_pushed_actions = 0 self.nb_pulled_broks = 0 # --- # And possible links for satellites self.brokers = {} self.pollers = {} self.reactionners = {} self.receivers = {} # This because it is the Satellite that has thes properties and I am a Satellite # todo: change this? # Broks are stored in each broker link, not locally # self.broks = [] self.broks_lock = threading.RLock() # Modules are only loaded one time self.have_modules = False self.first_scheduling = False def get_broks(self, broker_name): """Send broks to a specific broker :param broker_name: broker name to send broks :type broker_name: str :greturn: dict of brok for this broker :rtype: dict[alignak.brok.Brok] """ logger.debug("Broker %s requests my broks list", broker_name) res = [] if not broker_name: return res for broker_link in list(self.brokers.values()): if broker_name == broker_link.name: for brok in sorted(broker_link.broks, key=lambda x: x.creation_time): # Only provide broks that did not yet sent to our external modules if getattr(brok, 'sent_to_externals', False): res.append(brok) brok.got = True broker_link.broks = [ b for b in broker_link.broks if not getattr(b, 'got', False) ] logger.debug("Providing %d broks to %s", len(res), broker_name) break else: logger.warning("Got a brok request from an unknown broker: %s", broker_name) return res def compensate_system_time_change(self, difference): # pragma: no cover, # pylint: disable=too-many-branches # not with unit tests """Compensate a system time change of difference for all hosts/services/checks/notifs :param difference: difference in seconds :type difference: int :return: None """ super(Alignak, self).compensate_system_time_change(difference) # We only need to change some value self.program_start = max(0, self.program_start + difference) if not hasattr(self.sched, "conf"): # Race condition where time change before getting conf return # Then we compensate all host/services for host in self.sched.hosts: host.compensate_system_time_change(difference) for serv in self.sched.services: serv.compensate_system_time_change(difference) # Now all checks and actions for chk in list(self.sched.checks.values()): # Already launch checks should not be touch if chk.status == u'scheduled' and chk.t_to_go is not None: t_to_go = chk.t_to_go ref = self.sched.find_item_by_id(chk.ref) new_t = max(0, t_to_go + difference) timeperiod = self.sched.timeperiods[ref.check_period] if timeperiod is not None: # But it's no so simple, we must match the timeperiod new_t = timeperiod.get_next_valid_time_from_t(new_t) # But maybe no there is no more new value! Not good :( # Say as error, with error output if new_t is None: chk.state = u'waitconsume' chk.exit_status = 2 chk.output = '(Error: there is no available check time after time change!)' chk.check_time = time.time() chk.execution_time = 0 else: chk.t_to_go = new_t ref.next_chk = new_t # Now all checks and actions for act in list(self.sched.actions.values()): # Already launch checks should not be touch if act.status == u'scheduled': t_to_go = act.t_to_go # Event handler do not have ref ref_id = getattr(act, 'ref', None) new_t = max(0, t_to_go + difference) # Notification should be check with notification_period if act.is_a == u'notification': ref = self.sched.find_item_by_id(ref_id) if ref.notification_period: # But it's no so simple, we must match the timeperiod notification_period = self.sched.timeperiods[ ref.notification_period] new_t = notification_period.get_next_valid_time_from_t( new_t) # And got a creation_time variable too act.creation_time += difference # But maybe no there is no more new value! Not good :( # Say as error, with error output if new_t is None: act.state = 'waitconsume' act.exit_status = 2 act.output = '(Error: there is no available check time after time change!)' act.check_time = time.time() act.execution_time = 0 else: act.t_to_go = new_t def do_before_loop(self): """Stop the scheduling process""" if self.sched: self.sched.stop_scheduling() def do_loop_turn(self): """Scheduler loop turn Simply run the Alignak scheduler loop This is called when a configuration got received by the scheduler daemon. As of it, check if the first scheduling has been done... and manage this. :return: None """ if not self.first_scheduling: # Ok, now all is initialized, we can make the initial broks logger.info("First scheduling launched") _t0 = time.time() # Program start brok self.sched.initial_program_status() # First scheduling self.sched.schedule() statsmgr.timer('first_scheduling', time.time() - _t0) logger.info("First scheduling done") # Connect to our passive satellites if needed for satellite in [ s for s in list(self.pollers.values()) if s.passive ]: if not self.daemon_connection_init(satellite): logger.error("Passive satellite connection failed: %s", satellite) for satellite in [ s for s in list(self.reactionners.values()) if s.passive ]: if not self.daemon_connection_init(satellite): logger.error("Passive satellite connection failed: %s", satellite) # Ticks are for recurrent function call like consume, del zombies etc self.sched.ticks = 0 self.first_scheduling = True # Each loop turn, execute the daemon specific treatment... # only if the daemon has a configuration to manage if self.sched.pushed_conf: # If scheduling is not yet enabled, enable scheduling if not self.sched.must_schedule: self.sched.start_scheduling() self.sched.before_run() self.sched.run() else: logger.warning("#%d - No monitoring configuration to scheduler...", self.loop_count) def get_managed_configurations(self): """Get the configurations managed by this scheduler The configuration managed by a scheduler is the self configuration got by the scheduler during the dispatching. :return: a dict of scheduler links with instance_id as key and hash, push_flavor and configuration identifier as values :rtype: dict """ # for scheduler_link in list(self.schedulers.values()): # res[scheduler_link.instance_id] = { # 'hash': scheduler_link.hash, # 'push_flavor': scheduler_link.push_flavor, # 'managed_conf_id': scheduler_link.managed_conf_id # } res = {} if self.sched.pushed_conf and self.cur_conf and 'instance_id' in self.cur_conf: res[self.cur_conf['instance_id']] = { 'hash': self.cur_conf['hash'], 'push_flavor': self.cur_conf['push_flavor'], 'managed_conf_id': self.cur_conf['managed_conf_id'] } logger.debug("Get managed configuration: %s", res) return res def setup_new_conf(self): # pylint: disable=too-many-statements, too-many-branches, too-many-locals """Setup new conf received for scheduler :return: None """ # Execute the base class treatment... super(Alignak, self).setup_new_conf() # ...then our own specific treatment! with self.conf_lock: # self_conf is our own configuration from the alignak environment # self_conf = self.cur_conf['self_conf'] logger.debug("Got config: %s", self.cur_conf) if 'conf_part' not in self.cur_conf: self.cur_conf['conf_part'] = None conf_part = self.cur_conf['conf_part'] # Ok now we can save the retention data if self.sched.pushed_conf is not None: self.sched.update_retention() # Get the monitored objects configuration t00 = time.time() received_conf_part = None try: received_conf_part = unserialize(conf_part) assert received_conf_part is not None except AssertionError as exp: # This to indicate that no configuration is managed by this scheduler... logger.warning( "No managed configuration received from arbiter") except AlignakClassLookupException as exp: # pragma: no cover # This to indicate that the new configuration is not managed... self.new_conf = { "_status": "Cannot un-serialize configuration received from arbiter", "_error": str(exp) } logger.error(self.new_conf) logger.error("Back trace of the error:\n%s", traceback.format_exc()) return except Exception as exp: # pylint: disable=broad-except # This to indicate that the new configuration is not managed... self.new_conf = { "_status": "Cannot un-serialize configuration received from arbiter", "_error": str(exp) } logger.error(self.new_conf) self.exit_on_exception(exp, str(self.new_conf)) # if not received_conf_part: # return logger.info( "Monitored configuration %s received at %d. Un-serialized in %d secs", received_conf_part, t00, time.time() - t00) logger.info("Scheduler received configuration : %s", received_conf_part) # Now we create our pollers, reactionners and brokers for link_type in ['pollers', 'reactionners', 'brokers']: if link_type not in self.cur_conf['satellites']: logger.error("Missing %s in the configuration!", link_type) continue my_satellites = getattr(self, link_type, {}) received_satellites = self.cur_conf['satellites'][link_type] for link_uuid in received_satellites: rs_conf = received_satellites[link_uuid] logger.debug("- received %s - %s: %s", rs_conf['instance_id'], rs_conf['type'], rs_conf['name']) # Must look if we already had a configuration and save our broks already_got = rs_conf['instance_id'] in my_satellites broks = [] actions = {} wait_homerun = {} external_commands = {} running_id = 0 if already_got: logger.warning("I already got: %s", rs_conf['instance_id']) # Save some information running_id = my_satellites[link_uuid].running_id (broks, actions, wait_homerun, external_commands) = \ my_satellites[link_uuid].get_and_clear_context() # Delete the former link del my_satellites[link_uuid] # My new satellite link... new_link = SatelliteLink.get_a_satellite_link( link_type[:-1], rs_conf) my_satellites[new_link.uuid] = new_link logger.info("I got a new %s satellite: %s", link_type[:-1], new_link) new_link.running_id = running_id new_link.external_commands = external_commands new_link.broks = broks new_link.wait_homerun = wait_homerun new_link.actions = actions # Replacing the satellite address and port by those defined in satellite_map if new_link.name in self.cur_conf['override_conf'].get( 'satellite_map', {}): override_conf = self.cur_conf['override_conf'] overriding = override_conf.get('satellite_map')[ new_link.name] logger.warning( "Do not override the configuration for: %s, with: %s. " "Please check whether this is necessary!", new_link.name, overriding) # First mix conf and override_conf to have our definitive conf for prop in getattr(self.cur_conf, 'override_conf', []): logger.debug("Overriden: %s / %s ", prop, getattr(received_conf_part, prop, None)) logger.debug("Overriding: %s / %s ", prop, self.cur_conf['override_conf']) setattr(received_conf_part, prop, self.cur_conf['override_conf'].get(prop, None)) # Scheduler modules if not self.have_modules: try: logger.debug("Modules configuration: %s", self.cur_conf['modules']) self.modules = unserialize(self.cur_conf['modules'], no_load=True) except AlignakClassLookupException as exp: # pragma: no cover, simple protection logger.error( 'Cannot un-serialize modules configuration ' 'received from arbiter: %s', exp) if self.modules: logger.debug("I received some modules configuration: %s", self.modules) self.have_modules = True self.do_load_modules(self.modules) # and start external modules too self.modules_manager.start_external_instances() else: logger.info("I do not have modules") if received_conf_part: logger.info("Loading configuration...") # Propagate the global parameters to the configuration items received_conf_part.explode_global_conf() # We give the configuration to our scheduler self.sched.reset() self.sched.load_conf(self.cur_conf['instance_id'], self.cur_conf['instance_name'], received_conf_part) # Once loaded, the scheduler has an inner pushed_conf object logger.info("Loaded: %s", self.sched.pushed_conf) # Update the scheduler ticks according to the daemon configuration self.sched.update_recurrent_works_tick(self) # We must update our pushed configuration macros with correct values # from the configuration parameters # self.sched.pushed_conf.fill_resource_macros_names_macros() # Creating the Macroresolver Class & unique instance m_solver = MacroResolver() m_solver.init(received_conf_part) # Now create the external commands manager # We are an applyer: our role is not to dispatch commands, but to apply them ecm = ExternalCommandManager( received_conf_part, 'applyer', self.sched, received_conf_part.accept_passive_unknown_check_results, received_conf_part.log_external_commands) # Scheduler needs to know about this external command manager to use it if necessary self.sched.external_commands_manager = ecm # Ok now we can load the retention data self.sched.retention_load() # Log hosts/services initial states self.sched.log_initial_states() # Create brok new conf brok = Brok({'type': 'new_conf', 'data': {}}) self.sched.add_brok(brok) # Initialize connection with all our satellites logger.info("Initializing connection with my satellites:") my_satellites = self.get_links_of_type(s_type='') for satellite in list(my_satellites.values()): logger.info("- : %s/%s", satellite.type, satellite.name) if not self.daemon_connection_init(satellite): logger.error("Satellite connection failed: %s", satellite) if received_conf_part: # Enable the scheduling process logger.info("Loaded: %s", self.sched.pushed_conf) self.sched.start_scheduling() # Now I have a configuration! self.have_conf = True def clean_previous_run(self): """Clean variables from previous configuration :return: None """ # Execute the base class treatment... super(Alignak, self).clean_previous_run() # Clean all lists self.pollers.clear() self.reactionners.clear() self.brokers.clear() def get_daemon_stats(self, details=False): """Increase the stats provided by the Daemon base class :return: stats dictionary :rtype: dict """ # Call the base Daemon one res = super(Alignak, self).get_daemon_stats(details=details) res.update({ 'name': self.name, 'type': self.type, 'monitored_objects': {} }) counters = res['counters'] # Satellites counters counters['brokers'] = len(self.brokers) counters['pollers'] = len(self.pollers) counters['reactionners'] = len(self.reactionners) counters['receivers'] = len(self.receivers) if not self.sched: return res # # Hosts/services problems counters # m_solver = MacroResolver() # counters['hosts_problems'] = m_solver._get_total_host_problems() # counters['hosts_unhandled_problems'] = m_solver._get_total_host_problems_unhandled() # counters['services_problems'] = m_solver._get_total_service_problems() # counters['services_unhandled_problems'] = m_solver._get_total_service_problems_unhandled() # Get statistics from the scheduler scheduler_stats = self.sched.get_scheduler_stats(details=details) res['counters'].update(scheduler_stats['counters']) scheduler_stats.pop('counters') res.update(scheduler_stats) return res def get_monitoring_problems(self): """Get the current scheduler livesynthesis :return: live synthesis and problems dictionary :rtype: dict """ res = {} if not self.sched: return res # Get statistics from the scheduler scheduler_stats = self.sched.get_scheduler_stats(details=True) if 'livesynthesis' in scheduler_stats: res['livesynthesis'] = scheduler_stats['livesynthesis'] if 'problems' in scheduler_stats: res['problems'] = scheduler_stats['problems'] return res def main(self): """Main function for Scheduler, launch after the init:: * Init daemon * Load module manager * Launch main loop * Catch any Exception that occurs :return: None """ try: # Start the daemon mode if not self.do_daemon_init_and_start(): self.exit_on_error(message="Daemon initialization error", exit_code=3) # We wait for initial conf self.wait_for_initial_conf() if self.new_conf: # Setup the received configuration self.setup_new_conf() # Now the main loop self.do_main_loop() logger.info("Exited from the main loop.") # On main loop exit, call the scheduler after run process self.sched.after_run() self.request_stop() except Exception: # pragma: no cover, this should never happen indeed ;) self.exit_on_exception(traceback.format_exc()) raise
class Downtime: """ Schedules downtime for a specified service. If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The specified service downtime can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the specified service should not be triggered by another downtime entry. """ _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: d_type = "fixed" else: d_type = "flexible" return "%s %s Downtime id=%d %s - %s" % ( active, d_type, self._id, time.ctime(self.start_time), time.ctime(self.end_time)) @property def id(self): # pylint: disable=C0103 """Getter for id, raise deprecation warning :return: self._id """ warnings.warn("Access to deprecated attribute id %s Item class" % self.__class__, DeprecationWarning, stacklevel=2) return self._id @id.setter def id(self, value): # pylint: disable=C0103 """Setter for id, raise deprecation warning :param value: value to set :return: None """ warnings.warn("Access to deprecated attribute id of %s class" % self.__class__, DeprecationWarning, stacklevel=2) self._id = value def trigger_me(self, other_downtime): """Wrapper to activate_me.append function Used to add another downtime to activate :param other_downtime: other downtime to activate/cancel :type other_downtime: :return: None """ self.activate_me.append(other_downtime) def in_scheduled_downtime(self): """Getter for is_in_effect attribute :return: True if downtime is in effect, False otherwise :rtype: bool """ return self.is_in_effect def enter(self): """Set ref in scheduled downtime and raise downtime log entry (start) :return: [], always :rtype: list TODO: res is useless """ 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 downtime in self.activate_me: res.extend(downtime.enter()) return res def exit(self): """Remove ref in scheduled downtime and raise downtime log entry (exit) :return: [], always | None :rtype: list TODO: res is useless """ 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 def cancel(self): """Remove ref in scheduled downtime and raise downtime log entry (cancel) :return: [], always :rtype: list TODO: res is useless """ 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 # res.extend(self.ref.create_notifications('DOWNTIMECANCELLED')) # Also cancel other downtimes triggered by me for downtime in self.activate_me: res.extend(downtime.cancel()) return res def add_automatic_comment(self): """Add comment on ref for downtime :return: None """ 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 comm = Comment(self.ref, False, "(Alignak)", text, comment_type, 2, 0, False, 0) self.comment_id = comm._id self.extra_comment = comm self.ref.add_comment(comm) def del_automatic_comment(self): """Remove automatic comment on ref previously created :return: None """ # Extra comment can be None if we load it from a old version of Alignak # 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) def fill_data_brok_from(self, data, brok_type): """Fill data with info of item by looking at brok_type in props of properties or running_properties :param data: data to fill :type data: :param brok_type: type of brok :type brok_type: str :return: None TODO: Duplicate from Notification.fill_data_brok_from """ 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) def get_initial_status_brok(self): """Get a initial status brok :return: brok with wanted data :rtype: alignak.brok.Brok TODO: Duplicate from Notification.fill_data_brok_from """ data = {'_id': self._id} self.fill_data_brok_from(data, 'full_status') brok = Brok('downtime_raise', data) return brok def __getstate__(self): """Call by pickle for dataify the comment because we DO NOT WANT REF in this pickleisation! :return: dict containing notification data :rtype: dict TODO: REMOVE THIS """ 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 def __setstate__(self, state): """Inverted function of getstate :param state: state to restore :type state: dict :return: None TODO: REMOVE THIS """ cls = self.__class__ # Maybe it's not a dict but a list like in the old 0.4 format # so we should call the 0.4 function for it if isinstance(state, list): self.__setstate_deprecated__(state) return 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 def __setstate_deprecated__(self, state): """In 1.0 we move to a dict save. :param state: it's the state :type state: dict :return: None TODO: REMOVE THIS""" cls = self.__class__ # Check if the len of this state is like the previous, # if not, we will do errors! # -1 because of the '_id' prop if len(cls.properties) != (len(state) - 1): logger.info("Passing downtime") return self._id = state.pop() for prop in cls.properties: val = state.pop() setattr(self, prop, val) if self._id >= cls._id: cls._id = self._id + 1
class Contactgroup(Itemgroup): """Class to manage a group of contacts A Contactgroup is used to manage a group of contacts """ _id = 1 my_type = 'contactgroup' properties = Itemgroup.properties.copy() properties.update({ '_id': IntegerProp(default=0, fill_brok=['full_status']), 'contactgroup_name': StringProp(fill_brok=['full_status']), 'alias': StringProp(fill_brok=['full_status']), }) macros = { 'CONTACTGROUPALIAS': 'alias', 'CONTACTGROUPMEMBERS': 'get_members' } def get_contacts(self): """ Get list of contacts of this group :return: list of contacts :rtype: list[alignak.objects.contact.Contact] """ if getattr(self, 'members', None) is not None: return [m.strip() for m in self.members] else: return [] def get_name(self): """ Get name of group :return: Name of contactgroup :rtype: str """ return getattr(self, 'contactgroup_name', 'UNNAMED-CONTACTGROUP') def get_contactgroup_members(self): """ Get contactgroup members :return: list of hosts :rtype: list """ # TODO: imho a Contactgroup instance should always have defined # its contactgroup_members attribute, even if it's empty / the empty list. if hasattr(self, 'contactgroup_members'): # more over: it should already be in the list form, # not anymore in the "bare" string from as readed # from configuration (files or db or whaterver) return [m.strip() for m in self.contactgroup_members.split(',')] else: return [] def get_contacts_by_explosion(self, contactgroups): """ Get hosts of this group :param contactgroups: Contactgroups object, use to look for a specific one :type contactgroups: alignak.objects.contactgroup.Contactgroups :return: list of contact of this group :rtype: list[alignak.objects.contact.Contact] """ # 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 CG we explode # so if True here, it must be a loop in HG # calls... not GOOD! if self.rec_tag: logger.error( "[contactgroup::%s] got a loop in contactgroup definition", self.get_name()) if hasattr(self, 'members'): return self.members else: return '' # Ok, not a loop, we tag it and continue self.rec_tag = True cg_mbrs = self.get_contactgroup_members() for cg_mbr in cg_mbrs: contactgroup = contactgroups.find_by_name(cg_mbr.strip()) if contactgroup is not None: value = contactgroup.get_contacts_by_explosion(contactgroups) if value is not None: self.add_string_member(value) if hasattr(self, 'members'): return self.members else: return ''
class Acknowledge(AlignakObject): # pylint: disable=R0903 """ Allows you to acknowledge the current problem for the specified service. By acknowledging the current problem, future notifications (for the same service state) are disabled. If the acknowledge is "sticky", the acknowledgement will remain until the service returns to an OK state. Otherwise the acknowledgement will automatically be removed when the service state changes. If the acknowledge is "notify", a notification will be sent out to contacts indicating that the current service problem has been acknowledged and when the acknowledge is cleared. """ my_type = 'acknowledge' properties = { 'sticky': BoolProp(default=True), 'notify': BoolProp(default=False), 'end_time': IntegerProp(default=0), 'author': StringProp(default=u'Alignak'), 'comment': StringProp(default=u''), 'comment_id': StringProp(default=u'') } def __init__(self, params=None, parsing=False): super(Acknowledge, self).__init__(params, parsing=parsing) self.fill_default() def serialize(self): """This function serialize into a simple dict object. It is used when transferring data to other daemons over the network (http) Here we directly return all attributes :return: json representation of a Acknowledge :rtype: dict """ return {'uuid': self.uuid, 'ref': self.ref, 'sticky': self.sticky, 'notify': self.notify, 'end_time': self.end_time, 'author': self.author, 'comment': self.comment} def get_raise_brok(self, host_name, service_name=''): """Get a start acknowledge brok :param comment_type: 1 = host, 2 = service :param host_name: :param service_name: :return: brok with wanted data :rtype: alignak.brok.Brok """ data = self.serialize() data['host'] = host_name if service_name != '': data['service'] = service_name return Brok({'type': 'acknowledge_raise', 'data': data}) def get_expire_brok(self, host_name, service_name=''): """Get an expire acknowledge brok :type item: item :return: brok with wanted data :rtype: alignak.brok.Brok """ data = self.serialize() data['host'] = host_name if service_name != '': data['service'] = service_name return Brok({'type': 'acknowledge_expire', 'data': data})
class Receiver(Satellite): """Receiver class. Referenced as "app" in most Interface """ my_type = 'receiver' properties = Satellite.properties.copy() properties.update({ 'pidfile': PathProp(default='receiverd.pid'), 'port': IntegerProp(default=7773), 'local_log': PathProp(default='receiverd.log'), }) def __init__(self, config_file, is_daemon, do_replace, debug, debug_file): super(Receiver, self).__init__('receiver', config_file, is_daemon, do_replace, debug, debug_file) # Our arbiters self.arbiters = {} # Our pollers and reactionners self.pollers = {} self.reactionners = {} # Modules are load one time self.have_modules = False # Can have a queue of external_commands give by modules # will be taken by arbiter to process self.external_commands = [] # and the unprocessed one, a buffer self.unprocessed_external_commands = [] self.host_assoc = {} self.direct_routing = False self.accept_passive_unknown_check_results = False self.http_interface = ReceiverInterface(self) # Now create the external commander. It's just here to dispatch # the commands to schedulers ecm = ExternalCommandManager(None, 'receiver') ecm.load_receiver(self) self.external_command = ecm def add(self, elt): """Add an object to the receiver one Handles brok and externalcommand :param elt: object to add :type elt: object :return: None """ cls_type = elt.__class__.my_type if cls_type == 'brok': # For brok, we TAG brok with our instance_id elt.instance_id = 0 self.broks[elt._id] = elt return elif cls_type == 'externalcommand': logger.debug("Enqueuing an external command: %s", str(ExternalCommand.__dict__)) self.unprocessed_external_commands.append(elt) def push_host_names(self, sched_id, hnames): """Link hostnames to scheduler id. Called by alignak.satellite.IForArbiter.push_host_names :param sched_id: scheduler id to link to :type sched_id: int :param hnames: host names list :type hnames: list :return: None """ for h_name in hnames: self.host_assoc[h_name] = sched_id def get_sched_from_hname(self, hname): """Get scheduler linked to the given host_name :param hname: host_name we want the scheduler from :type hname: str :return: scheduler with id corresponding to the mapping table :rtype: dict """ item = self.host_assoc.get(hname, None) sched = self.schedulers.get(item, None) return sched def manage_brok(self, brok): """Send brok to modules. Modules have to implement their own manage_brok function. They usually do if they inherits from basemodule REF: doc/receiver-modules.png (4-5) :param brok: brok to manage :type brok: alignak.brok.Brok :return: None """ to_del = [] # Call all modules if they catch the call for mod in self.modules_manager.get_internal_instances(): try: mod.manage_brok(brok) except Exception, exp: logger.warning("The mod %s raise an exception: %s, I kill it", mod.get_name(), str(exp)) logger.warning("Exception type: %s", type(exp)) logger.warning("Back trace of this kill: %s", traceback.format_exc()) to_del.append(mod) # Now remove mod that raise an exception self.modules_manager.clear_instances(to_del)
class Check(Action): # pylint: disable=R0902 """Check class implements monitoring concepts of checks :(status, state, output) Check instance are used to store monitoring plugins data (exit status, output) and used by schedule to raise alert, reschedule check etc. """ # AutoSlots create the __slots__ with properties and # running_properties names # FIXME : re-enable AutoSlots if possible # __metaclass__ = AutoSlots my_type = 'check' properties = Action.properties.copy() properties.update({ 'is_a': StringProp(default=u'check'), 'state': IntegerProp(default=0), 'depend_on': ListProp(default=[]), 'depend_on_me': ListProp(default=[], split_on_comma=False), 'passive_check': BoolProp(default=False), 'freshness_expiry_check': BoolProp(default=False), 'poller_tag': StringProp(default=u'None'), 'dependency_check': BoolProp(default=False), }) def __init__(self, params=None, parsing=False): super(Check, self).__init__(params, parsing=parsing) if self.command.startswith('_'): self.internal = True def __str__(self): # pragma: no cover return "Check %s %s, item: %s, status: %s, command:'%s'" % \ (self.uuid, "active" if not self.passive_check else "passive", self.ref, self.status, self.command) def get_return_from(self, check): """Update check data from action (notification for instance) :param check: action to get data from :type check: alignak.action.Action :return: None """ for prop in [ 'exit_status', 'output', 'long_output', 'check_time', 'execution_time', 'perf_data', 'u_time', 's_time' ]: setattr(self, prop, getattr(check, prop)) def set_type_active(self): """Set this check as an active one (indeed, not passive) :return: None """ self.passive_check = False def set_type_passive(self): """Set this check as a passive one :return: None """ self.passive_check = True def is_dependent(self): """Getter for dependency_check attribute :return: True if this check was created for a dependent one, False otherwise :rtype: bool """ return self.dependency_check def serialize(self): """This function serializes into a simple dict object. The only usage is to send to poller, and it does not need to have the depend_on and depend_on_me properties. :return: json representation of a Check :rtype: dict """ res = super(Check, self).serialize() if 'depend_on' in res: del res['depend_on'] if 'depend_on_me' in res: del res['depend_on_me'] return res
class SatelliteLink(Item): """SatelliteLink is a common Class for links between Arbiter and other satellites. Used by the Dispatcher object. """ # _id = 0 each Class will have it's own id properties = Item.properties.copy() properties.update({ 'address': StringProp(default='localhost', 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=False, 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), 'modules': ListProp(default=[''], to_send=True, split_on_coma=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={}, elts_prop=AddrProp, to_send=True, override=True), 'use_ssl': BoolProp(default=False, fill_brok=['full_status']), 'hard_ssl_name_check': BoolProp(default=True, fill_brok=['full_status']), 'passive': BoolProp(default=False, fill_brok=['full_status'], to_send=True), }) running_properties = Item.running_properties.copy() running_properties.update({ 'con': StringProp(default=None), 'alive': BoolProp(default=True, fill_brok=['full_status']), 'broks': StringProp(default=[]), # the number of failed attempt 'attempt': StringProp(default=0, fill_brok=['full_status']), # can be network ask or not (dead or check in timeout or error) 'reachable': BoolProp(default=False, fill_brok=['full_status']), '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 Exception: pass def get_name(self): """Get the name of the link based on its type if *mytype*_name is an attribute then returns self.*mytype*_name. otherwise returns "Unnamed *mytype*" Example : self.poller_name or "Unnamed poller" :return: String corresponding to the link name :rtype: str """ return getattr(self, "{0}_name".format(self.get_my_type()), "Unnamed {0}".format(self.get_my_type())) 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 :return: None """ self.arb_satmap = {'address': self.address, 'port': self.port, 'use_ssl': self.use_ssl, 'hard_ssl_name_check': self.hard_ssl_name_check} self.arb_satmap.update(satellitemap) def create_connection(self): """Initialize HTTP connection with a satellite (con attribute) and set uri attribute :return: None """ 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, strong_ssl=self.hard_ssl_name_check ) self.uri = self.con.uri def put_conf(self, conf): """Send the conf (serialized) to the satellite HTTP request to the satellite (POST / put_conf) :param conf: The conf to send (data depend on the satellite) :type conf: :return: None """ if self.con is None: self.create_connection() # Maybe the connection was not ok, bail out if not self.con: return False try: self.con.get('ping') self.con.post('put_conf', {'conf': conf}, wait='long') 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