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
class SatelliteLink(Item): # pylint: disable=too-many-instance-attributes """SatelliteLink is a common Class for links between Arbiter and other satellites. Used by the Dispatcher object. """ # Next value used for auto generated instance_id _next_id = 1 # All the class properties that are 'to_send' are stored in the 'global' # configuration to be pushed to the satellite when the configuration is dispatched properties = Item.properties.copy() properties.update({ 'instance_id': StringProp(to_send=True), # When this property is set, the Arbiter will launch the corresponding daemon 'alignak_launched': BoolProp(default=False, fill_brok=['full_status'], to_send=True), # This property is set by the Arbiter when it detects that this daemon # is needed but not declared in the configuration 'missing_daemon': BoolProp(default=False, fill_brok=['full_status']), # Sent to the satellites and used to check the managed configuration # Those are not to_send=True because they are updated by the configuration Dispatcher # and set when the daemon receives its configuration 'managed_conf_id': StringProp(default=u''), 'push_flavor': StringProp(default=u''), 'hash': StringProp(default=u''), # A satellite link has the type/name of the daemon it is related to 'type': StringProp(default=u'', fill_brok=['full_status'], to_send=True), 'name': StringProp(default=u'', fill_brok=['full_status'], to_send=True), # Listening interface and address used by the other daemons 'host': StringProp(default=u'0.0.0.0', to_send=True), 'address': StringProp(default=u'127.0.0.1', fill_brok=['full_status'], to_send=True), 'active': BoolProp(default=True, fill_brok=['full_status'], to_send=True), 'short_timeout': IntegerProp(default=3, fill_brok=['full_status'], to_send=True), 'long_timeout': IntegerProp(default=120, fill_brok=['full_status'], to_send=True), # the delay (seconds) between two ping retries 'ping_period': IntegerProp(default=5), # The maximum number of retries before setting the daemon as dead 'max_check_attempts': IntegerProp(default=3, fill_brok=['full_status']), # For a spare daemon link 'spare': BoolProp(default=False, fill_brok=['full_status'], to_send=True), 'spare_check_interval': IntegerProp(default=5, fill_brok=['full_status']), 'spare_max_check_attempts': IntegerProp(default=3, fill_brok=['full_status']), 'manage_sub_realms': BoolProp(default=True, fill_brok=['full_status'], to_send=True), 'manage_arbiters': BoolProp(default=False, fill_brok=['full_status'], to_send=True), 'modules': ListProp(default=[''], split_on_comma=True), 'polling_interval': IntegerProp(default=5, fill_brok=['full_status'], to_send=True), 'use_timezone': StringProp(default=u'NOTSET', to_send=True), 'realm': StringProp(default=u'', fill_brok=['full_status'], brok_transformation=get_obj_name_two_args_and_void), 'realm_name': StringProp(default=u''), 'satellite_map': DictProp(default={}, elts_prop=AddrProp, to_send=True, override=True), 'use_ssl': BoolProp(default=False, fill_brok=['full_status'], to_send=True), 'hard_ssl_name_check': BoolProp(default=True, fill_brok=['full_status'], to_send=True), 'passive': BoolProp(default=False, fill_brok=['full_status'], to_send=True), }) running_properties = Item.running_properties.copy() running_properties.update({ 'con': StringProp(default=None), 'uri': StringProp(default=None), 'reachable': # Can be reached - assumed True as default ;) BoolProp(default=False, fill_brok=['full_status']), 'alive': # Is alive (attached process s launched...) BoolProp(default=False, fill_brok=['full_status']), 'valid': # Is valid (the daemon is the expected one) BoolProp(default=False, fill_brok=['full_status']), 'need_conf': # The daemon needs to receive a configuration BoolProp(default=True, fill_brok=['full_status']), 'have_conf': # The daemon has received a configuration BoolProp(default=False, fill_brok=['full_status']), 'stopping': # The daemon is requested to stop BoolProp(default=False, fill_brok=['full_status']), 'running_id': # The running identifier of my related daemon FloatProp(default=0, fill_brok=['full_status']), # the number of poll attempt from the arbiter dispatcher 'attempt': IntegerProp(default=0, fill_brok=['full_status']), # the last connection attempt timestamp 'last_connection': IntegerProp(default=0, fill_brok=['full_status']), # the number of failed attempt for the connection 'connection_attempt': IntegerProp(default=0, fill_brok=['full_status']), 'last_check': IntegerProp(default=0, fill_brok=['full_status']), 'cfg_managed': DictProp(default=None), 'cfg_to_manage': DictProp(default={}), 'configuration_sent': BoolProp(default=False), 'statistics': DictProp(default={}), }) def __init__(self, params=None, parsing=True): """Initialize a SatelliteLink If parsing is True, we are initializing from a configuration, else we are initializing from a copy of another satellite link data. This is used when the daemons receive their configuration from the arbiter. When initializing from an arbiter configuration, an instance_id property must exist else a LinkError exception is raised! If a satellite_map property exists in the provided parameters, it will update the default existing one """ super(SatelliteLink, self).__init__(params, parsing) logger.debug("Initialize a %s, params: %s", self.__class__.__name__, params) # My interface context self.broks = [] self.actions = {} self.wait_homerun = {} self.pushed_commands = [] self.init_running_properties() if parsing: # Create a new satellite link identifier self.instance_id = u'%s_%d' % (self.__class__.__name__, self.__class__._next_id) self.__class__._next_id += 1 elif 'instance_id' not in params: raise LinkError("When not parsing a configuration, " "an instance_id must exist in the provided parameters") self.fill_default() # Hack for ascending compatibility with Shinken configuration try: # We received a configuration with a 'name' property... if self.name: setattr(self, "%s_name" % self.type, self.name) else: # We received a configuration without a 'name' property... old form! if getattr(self, "%s_name" % self.type, None): setattr(self, 'name', getattr(self, "%s_name" % self.type)) else: self.name = "Unnamed %s" % self.type setattr(self, "%s_name" % self.type, self.name) except KeyError: setattr(self, 'name', getattr(self, "%s_name" % self.type)) # Initialize our satellite map, and update if required self.set_arbiter_satellite_map(params.get('satellite_map', {})) self.cfg = { 'self_conf': {}, 'schedulers': {}, 'arbiters': {} } # Create the daemon connection self.create_connection() def __repr__(self): # pragma: no cover return '<%s - %s/%s, %s//%s:%s, rid: %s, spare: %s, realm: %s, sub-realms: %s, ' \ 'managing: %s (%s) />' \ % (self.instance_id, self.type, self.name, self.scheme, self.address, self.port, self.running_id, self.spare, self.realm, self.manage_sub_realms, self.managed_conf_id, self.push_flavor) __str__ = __repr__ @property def scheme(self): """Daemon interface scheme :return: http or https if the daemon uses SSL :rtype: str """ _scheme = 'http' if self.use_ssl: _scheme = 'https' return _scheme @staticmethod def get_a_satellite_link(sat_type, sat_dict): """Get a SatelliteLink object for a given satellite type and a dictionary :param sat_type: type of satellite :param sat_dict: satellite configuration data :return: """ cls = get_alignak_class('alignak.objects.%slink.%sLink' % (sat_type, sat_type.capitalize())) return cls(params=sat_dict, parsing=False) def get_livestate(self): """Get the SatelliteLink live state. The live state is a tuple information containing a state identifier and a message, where: state is: - 0 for an up and running satellite - 1 if the satellite is not reachale - 2 if the satellite is dead - 3 else (not active) :return: tuple """ livestate = 0 if self.active: if not self.reachable: livestate = 1 elif not self.alive: livestate = 2 else: livestate = 3 livestate_output = "%s/%s is %s" % (self.type, self.name, [ "up and running.", "warning because not reachable.", "critical because not responding.", "not active by configuration." ][livestate]) return (livestate, livestate_output) def set_arbiter_satellite_map(self, satellite_map=None): """ satellite_map is the satellites map in current context: - A SatelliteLink is owned by an Arbiter - satellite_map attribute of a SatelliteLink is the map defined IN THE satellite configuration but for creating connections, we need to have the satellites map from the Arbiter point of view :return: None """ self.satellite_map = { 'address': self.address, 'port': self.port, 'use_ssl': self.use_ssl, 'hard_ssl_name_check': self.hard_ssl_name_check } if satellite_map: self.satellite_map.update(satellite_map) def get_and_clear_context(self): """Get and clean all of our broks, actions, external commands and homerun :return: list of all broks of the satellite link :rtype: list """ res = (self.broks, self.actions, self.wait_homerun, self.pushed_commands) self.broks = [] self.actions = {} self.wait_homerun = {} self.pushed_commands = [] return res def get_and_clear_broks(self): """Get and clean all of our broks :return: list of all broks of the satellite link :rtype: list """ res = self.broks self.broks = [] return res 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 """ logger.debug("- preparing: %s", self) self.cfg = { 'self_conf': self.give_satellite_cfg(), 'schedulers': {}, 'arbiters': {} } logger.debug("- prepared: %s", self.cfg) def give_satellite_cfg(self): """Get the default information for a satellite. Overridden by the specific satellites links :return: dictionary of information common to all the links :rtype: dict """ # All the satellite link class properties that are 'to_send' are stored in a # dictionary to be pushed to the satellite when the configuration is dispatched res = {} properties = self.__class__.properties for prop, entry in list(properties.items()): if hasattr(self, prop) and entry.to_send: res[prop] = getattr(self, prop) return res def give_satellite_json(self): """Get the json information for a satellite. This to provide information that will be exposed by a daemon on its HTTP interface. :return: dictionary of information common to all the links :rtype: dict """ daemon_properties = ['type', 'name', 'uri', 'spare', 'configuration_sent', 'realm_name', 'manage_sub_realms', 'active', 'reachable', 'alive', 'passive', 'last_check', 'polling_interval', 'max_check_attempts'] (livestate, livestate_output) = self.get_livestate() res = { "livestate": livestate, "livestate_output": livestate_output } for sat_prop in daemon_properties: res[sat_prop] = getattr(self, sat_prop, 'not_yet_defined') return res def manages(self, cfg_part): """Tell if the satellite is managing this configuration part The managed configuration is formed as a dictionary indexed on the link instance_id: { u'SchedulerLink_1': { u'hash': u'4d08630a3483e1eac7898e7a721bd5d7768c8320', u'push_flavor': u'4d08630a3483e1eac7898e7a721bd5d7768c8320', u'managed_conf_id': [u'Config_1'] } } Note that the managed configuration is a string array rather than a simple string... no special for this reason, probably due to the serialization when the configuration is pushed :/ :param cfg_part: configuration part as prepare by the Dispatcher :type cfg_part: Conf :return: True if the satellite manages this configuration :rtype: bool """ logger.debug("Do I (%s/%s) manage: %s, my managed configuration(s): %s", self.type, self.name, cfg_part, self.cfg_managed) # If we do not yet manage a configuration if not self.cfg_managed: logger.info("I (%s/%s) do not manage (yet) any configuration!", self.type, self.name) return False # Check in the schedulers list configurations for managed_cfg in list(self.cfg_managed.values()): # If not even the cfg_id in the managed_conf, bail out if managed_cfg['managed_conf_id'] == cfg_part.instance_id \ and managed_cfg['push_flavor'] == cfg_part.push_flavor: logger.debug("I do manage this configuration: %s", cfg_part) break else: logger.warning("I (%s/%s) do not manage this configuration: %s", self.type, self.name, cfg_part) return False return True def create_connection(self): """Initialize HTTP connection with a satellite (con attribute) and set its uri attribute This is called on the satellite link initialization :return: None """ # Create the HTTP client for the connection try: self.con = HTTPClient(address=self.satellite_map['address'], port=self.satellite_map['port'], short_timeout=self.short_timeout, long_timeout=self.long_timeout, use_ssl=self.satellite_map['use_ssl'], strong_ssl=self.satellite_map['hard_ssl_name_check']) self.uri = self.con.uri except HTTPClientException as exp: # logger.error("Error with '%s' when creating client: %s", self.name, str(exp)) # Set the satellite as dead self.set_dead() raise LinkError("Error with '%s' when creating client: %s" % (self.name, str(exp))) def set_alive(self): """Set alive, reachable, and reset attempts. If we change state, raise a status brok update alive, means the daemon is prenset in the system reachable, means that the HTTP connection is valid With this function we confirm that the daemon is reachable and, thus, we assume it is alive! :return: None """ was_alive = self.alive self.alive = True self.reachable = True self.attempt = 0 # We came from dead to alive! We must propagate the good news if not was_alive: logger.info("Setting %s satellite as alive :)", self.name) self.broks.append(self.get_update_status_brok()) def set_dead(self): """Set the satellite into dead state: If we change state, raise a status brok update :return:None """ was_alive = self.alive self.alive = False self.reachable = False self.attempt = 0 # We will have to create a new connection... self.con = None # We are dead now! We must propagate the sad news... if was_alive and not self.stopping: logger.warning("Setting the satellite %s as dead :(", self.name) self.broks.append(self.get_update_status_brok()) def add_failed_check_attempt(self, reason=''): """Set the daemon as unreachable and add a failed attempt if we reach the maximum attempts, set the daemon as dead :param reason: the reason of adding an attempts (stack trace sometimes) :type reason: str :return: None """ self.reachable = False self.attempt = self.attempt + 1 logger.debug("Failed attempt for %s (%d/%d), reason: %s", self.name, self.attempt, self.max_check_attempts, reason) # Don't need to warn again and again if the satellite is already dead # Only warn when it is alive if self.alive: if not self.stopping: logger.warning("Add failed attempt for %s (%d/%d) - %s", self.name, self.attempt, self.max_check_attempts, reason) else: logger.info("Stopping... failed attempt for %s (%d/%d) - also probably stopping", self.name, self.attempt, self.max_check_attempts) # If we reached the maximum attempts, set the daemon as dead if self.attempt >= self.max_check_attempts: if not self.stopping: logger.warning("Set %s as dead, too much failed attempts (%d), last problem is: %s", self.name, self.max_check_attempts, reason) else: logger.info("Stopping... set %s as dead, too much failed attempts (%d)", self.name, self.max_check_attempts) self.set_dead() def valid_connection(*outer_args, **outer_kwargs): # pylint: disable=unused-argument, no-method-argument """Check if the daemon connection is established and valid""" def decorator(func): # pylint: disable=missing-docstring def decorated(*args, **kwargs): # pylint: disable=missing-docstring # outer_args and outer_kwargs are the decorator arguments # args and kwargs are the decorated function arguments link = args[0] if not link.con: raise LinkError("The connection is not created for %s" % link.name) if not link.running_id: raise LinkError("The connection is not initialized for %s" % link.name) return func(*args, **kwargs) return decorated return decorator def communicate(*outer_args, **outer_kwargs): # pylint: disable=unused-argument, no-method-argument """Check if the daemon connection is authorized and valid""" def decorator(func): # pylint: disable=missing-docstring def decorated(*args, **kwargs): # pylint: disable=missing-docstring # outer_args and outer_kwargs are the decorator arguments # args and kwargs are the decorated function arguments fn_name = func.__name__ link = args[0] if not link.alive: logger.warning("%s is not alive for %s", link.name, fn_name) return None try: if not link.reachable: raise LinkError("The %s %s is not reachable" % (link.type, link.name)) logger.debug("[%s] Calling: %s, %s, %s", link.name, fn_name, args, kwargs) return func(*args, **kwargs) except HTTPClientConnectionException as exp: # A Connection error is raised when the daemon connection cannot be established # No way with the configuration parameters! if not link.stopping: logger.warning("A daemon (%s/%s) that we must be related with " "cannot be connected: %s", link.type, link.name, exp) else: logger.info("Stopping... daemon (%s/%s) cannot be connected. " "It is also probably stopping or yet stopped.", link.type, link.name) link.set_dead() except (LinkError, HTTPClientTimeoutException) as exp: link.add_failed_check_attempt("Connection timeout " "with '%s': %s" % (fn_name, str(exp))) return False except HTTPClientDataException as exp: # A Data error is raised when the daemon HTTP reponse is not 200! # No way with the communication if some problems exist in the daemon interface! # Abort all err = "Some daemons that we must be related with " \ "have some interface problems. Sorry, I bail out" logger.error(err) os.sys.exit(err) except HTTPClientException as exp: link.add_failed_check_attempt("Error with '%s': %s" % (fn_name, str(exp))) return None return decorated return decorator @communicate() def get_running_id(self): """Send a HTTP request to the satellite (GET /identity) Used to get the daemon running identifier that allows to know if the daemon got restarted This is called on connection initialization or re-connection If the daemon is notreachable, this function will raise an exception and the caller will receive a False as return :return: Boolean indicating if the running id was received :type: bool """ former_running_id = self.running_id logger.info(" get the running identifier for %s %s.", self.type, self.name) # An exception is raised in this function if the daemon is not reachable self.running_id = self.con.get('identity') if isinstance(self.running_id, dict): self.running_id = self.running_id['running_id'] if former_running_id == 0: if self.running_id: logger.info(" -> got: %s.", self.running_id) former_running_id = self.running_id # If the daemon has just started or has been restarted: it has a new running_id. if former_running_id != self.running_id: if former_running_id: logger.info(" -> The %s %s running identifier changed: %s. " "The daemon was certainly restarted!", self.type, self.name, self.running_id) # So we clear all verifications, they are obsolete now. logger.info("The running id of the %s %s changed (%s), " "we must clear its context.", self.type, self.name, self.running_id) (_, _, _, _) = self.get_and_clear_context() # Set the daemon as alive self.set_alive() return True @valid_connection() @communicate() def stop_request(self, stop_now=False): """Send a stop request to the daemon :param stop_now: stop now or go to stop wait mode :type stop_now: bool :return: the daemon response (True) """ logger.debug("Sending stop request to %s, stop now: %s", self.name, stop_now) res = self.con.get('stop_request', {'stop_now': '1' if stop_now else '0'}) return res @valid_connection() @communicate() def update_infos(self, forced=False, test=False): """Update satellite info each self.polling_interval seconds so we smooth arbiter actions for just useful actions. Raise a satellite update status Brok If forced is True, then ignore the ping period. This is used when the configuration has not yet been dispatched to the Arbiter satellites. If test is True, do not really ping the daemon (useful for the unit tests only) :param forced: ignore the ping smoothing :type forced: bool :param test: :type test: bool :return: None if the last request is too recent, False if a timeout was raised during the request, else the managed configurations dictionary """ logger.debug("Update informations, forced: %s", forced) # First look if it's not too early to ping now = time.time() if not forced and self.last_check and self.last_check + self.polling_interval > now: logger.debug("Too early to ping %s, ping period is %ds!, last check: %d, now: %d", self.name, self.polling_interval, self.last_check, now) return None self.get_conf(test=test) # Update the daemon last check timestamp self.last_check = time.time() # Update the state of this element self.broks.append(self.get_update_status_brok()) return self.cfg_managed @valid_connection() @communicate() def get_daemon_stats(self, details=False): """Send a HTTP request to the satellite (GET /get_daemon_stats) :return: Daemon statistics :rtype: dict """ logger.debug("Get daemon statistics for %s, %s %s", self.name, self.alive, self.reachable) return self.con.get('stats%s' % ('?details=1' if details else '')) @valid_connection() @communicate() def get_initial_broks(self, broker_name): """Send a HTTP request to the satellite (GET /_initial_broks) Used to build the initial broks for a broker connecting to a scheduler :param broker_name: the concerned broker name :type broker_name: str :return: Boolean indicating if the running id changed :type: bool """ logger.debug("Getting initial broks for %s, %s %s", self.name, self.alive, self.reachable) return self.con.get('_initial_broks', {'broker_name': broker_name}, wait=True) @valid_connection() @communicate() def wait_new_conf(self): """Send a HTTP request to the satellite (GET /wait_new_conf) :return: True if wait new conf, otherwise False :rtype: bool """ logger.debug("Wait new configuration for %s, %s %s", self.name, self.alive, self.reachable) return self.con.get('_wait_new_conf') @valid_connection() @communicate() def put_conf(self, configuration, test=False): """Send the configuration to the satellite HTTP request to the satellite (POST /push_configuration) If test is True, store the configuration internally :param configuration: The conf to send (data depend on the satellite) :type configuration: :return: None """ logger.debug("Sending configuration to %s, %s %s", self.name, self.alive, self.reachable) # ---------- if test: setattr(self, 'unit_test_pushed_configuration', configuration) # print("*** unit tests - sent configuration %s: %s" % (self.name, configuration)) return True # ---------- return self.con.post('_push_configuration', {'conf': configuration}, wait=True) @valid_connection() @communicate() def has_a_conf(self, magic_hash=None): # pragma: no cover """Send a HTTP request to the satellite (GET /have_conf) Used to know if the satellite has a conf :param magic_hash: Config hash. Only used for HA arbiter communication :type magic_hash: int :return: Boolean indicating if the satellite has a (specific) configuration :type: bool """ logger.debug("Have a configuration for %s, %s %s", self.name, self.alive, self.reachable) self.have_conf = self.con.get('_have_conf', {'magic_hash': magic_hash}) return self.have_conf @valid_connection() @communicate() def get_conf(self, test=False): """Send a HTTP request to the satellite (GET /managed_configurations) and update the cfg_managed attribute with the new information Set to {} on failure the managed configurations are a dictionary which keys are the scheduler link instance id and the values are the push_flavor If test is True, returns the unit test internally stored configuration Returns False if a timeout is raised :return: see @communicate, or the managed configuration """ logger.debug("Get managed configuration for %s, %s %s", self.name, self.alive, self.reachable) # ---------- if test: self.cfg_managed = {} self.have_conf = True logger.debug("Get managed configuration test ...") if getattr(self, 'unit_test_pushed_configuration', None) is not None: # Note this is a dict not a SatelliteLink object ! for scheduler_link in self.unit_test_pushed_configuration['schedulers'].values(): self.cfg_managed[scheduler_link['instance_id']] = { 'hash': scheduler_link['hash'], 'push_flavor': scheduler_link['push_flavor'], 'managed_conf_id': scheduler_link['managed_conf_id'] } # print("*** unit tests - get managed configuration %s: %s" # % (self.name, self.cfg_managed)) # ---------- else: self.cfg_managed = self.con.get('managed_configurations') logger.debug("My (%s) fresh managed configuration: %s", self.name, self.cfg_managed) self.have_conf = (self.cfg_managed != {}) return self.cfg_managed @valid_connection() @communicate() def push_broks(self, broks): """Send a HTTP request to the satellite (POST /push_broks) Send broks to the satellite :param broks: Brok list to send :type broks: list :return: True on success, False on failure :rtype: bool """ logger.debug("[%s] Pushing %d broks", self.name, len(broks)) return self.con.post('_push_broks', {'broks': broks}, wait=True) @valid_connection() @communicate() def push_actions(self, actions, scheduler_instance_id): """Post the actions to execute to the satellite. Indeed, a scheduler post its checks to a poller and its actions to a reactionner. :param actions: Action list to send :type actions: list :param scheduler_instance_id: Scheduler instance identifier :type scheduler_instance_id: uuid :return: True on success, False on failure :rtype: bool """ logger.debug("Pushing %d actions from %s", len(actions), scheduler_instance_id) return self.con.post('_push_actions', {'actions': actions, 'scheduler_instance_id': scheduler_instance_id}, wait=True) @valid_connection() @communicate() def push_results(self, results, scheduler_name): """Send a HTTP request to the satellite (POST /put_results) Send actions results to the satellite :param results: Results list to send :type results: list :param scheduler_name: Scheduler name :type scheduler_name: uuid :return: True on success, False on failure :rtype: bool """ logger.debug("Pushing %d results", len(results)) result = self.con.post('put_results', {'results': results, 'from': scheduler_name}, wait=True) return result @valid_connection() @communicate() def push_external_commands(self, commands): """Send a HTTP request to the satellite (POST /r_un_external_commands) to send the external commands to the satellite :param results: Results list to send :type results: list :return: True on success, False on failure :rtype: bool """ logger.debug("Pushing %d external commands", len(commands)) return self.con.post('_run_external_commands', {'cmds': commands}, wait=True) @valid_connection() @communicate() def get_external_commands(self): """Send a HTTP request to the satellite (GET /_external_commands) to get the external commands from the satellite. :return: External Command list on success, [] on failure :rtype: list """ res = self.con.get('_external_commands', wait=False) logger.debug("Got %d external commands from %s: %s", len(res), self.name, res) return unserialize(res, True) @valid_connection() @communicate() def get_broks(self, broker_name): """Send a HTTP request to the satellite (GET /_broks) Get broks from the satellite. Un-serialize data received. :param broker_name: the concerned broker link :type broker_name: BrokerLink :return: Broks list on success, [] on failure :rtype: list """ res = self.con.get('_broks', {'broker_name': broker_name}, wait=False) logger.debug("Got broks from %s: %s", self.name, res) return unserialize(res, True) @valid_connection() @communicate() def get_events(self): """Send a HTTP request to the satellite (GET /_events) Get monitoring events from the satellite. :return: Broks list on success, [] on failure :rtype: list """ res = self.con.get('_events', wait=False) logger.debug("Got events from %s: %s", self.name, res) return unserialize(res, True) @valid_connection() def get_results(self, scheduler_instance_id): """Send a HTTP request to the satellite (GET /_results) Get actions results from satellite (only passive satellites expose this method. :param scheduler_instance_id: scheduler instance identifier :type scheduler_instance_id: str :return: Results list on success, [] on failure :rtype: list """ res = self.con.get('_results', {'scheduler_instance_id': scheduler_instance_id}, wait=True) logger.debug("Got %d results from %s: %s", len(res), self.name, res) return res @valid_connection() def get_actions(self, params): """Send a HTTP request to the satellite (GET /_checks) Get actions from the scheduler. Un-serialize data received. :param params: the request parameters :type params: str :return: Actions list on success, [] on failure :rtype: list """ res = self.con.get('_checks', params, wait=True) logger.debug("Got checks to execute from %s: %s", self.name, res) return unserialize(res, True)
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
class SatelliteLink(Item): """SatelliteLink is a common Class for links between Arbiter and other satellites. Used by the Dispatcher object. """ 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=False, 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), 'realm_name': StringProp(default=''), '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 poll attempt from the arbiter dispatcher 'attempt': IntegerProp(default=0, fill_brok=['full_status']), # the last connection attempt timestamp 'last_connection': IntegerProp(default=0, fill_brok=['full_status']), # the number of failed attempt for the connection 'connection_attempt': IntegerProp(default=0, fill_brok=['full_status']), # the number of failed attempt for the connection 'max_failed_connections': IntegerProp(default=3, fill_brok=['full_status']), # can be network ask or not (dead or check in timeout or error) 'reachable': BoolProp(default=True, fill_brok=['full_status']), 'last_check': IntegerProp(default=0, fill_brok=['full_status']), 'managed_confs': DictProp(default={}), 'is_sent': BoolProp(default=False), }) def __init__(self, *args, **kwargs): super(SatelliteLink, self).__init__(*args, **kwargs) self.fill_default() 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 ValueError: # pragma: no cover, simple protection logger.error("Satellite port must be an integer: %s", self.port) # Create the link connection if not self.con: self.create_connection() 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 = None # Create the HTTP client for the connection try: 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 # Set the satellite as alive self.set_alive() except HTTPClientException as exp: logger.error("Error with '%s' when creating client: %s", self.get_name(), str(exp)) # Set the satellite as dead self.set_dead() 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 not self.reachable: logger.warning("Not reachable for put_conf: %s", self.get_name()) return False try: self.con.post('put_conf', {'conf': conf}, wait='long') return True except HTTPClientConnectionException as exp: # pragma: no cover, simple protection logger.warning( "[%s] Connection error when sending configuration: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection logger.warning( "[%s] Connection timeout when sending configuration: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) except HTTPClientException as exp: # pragma: no cover, simple protection logger.error("[%s] Error when sending configuration: %s", self.get_name(), str(exp)) self.con = None except AttributeError as exp: # pragma: no cover, simple protection # Connection is not created logger.error("[%s] put_conf - Connection does not exist!", self.get_name()) return False def get_all_broks(self): """Get and clean all of our broks :return: list of all broks in the satellite :rtype: list """ res = self.broks self.broks = [] return res def set_alive(self): """Set alive, reachable, and reset attempts. If we change state, raise a status brok update :return: None """ was_alive = self.alive self.alive = True self.reachable = True self.attempt = 0 # We came from dead to alive! We must propagate the good news if not was_alive: logger.warning("Setting the satellite %s as alive :)", self.get_name()) brok = self.get_update_status_brok() self.broks.append(brok) def set_dead(self): """Set the satellite into dead state: * Alive -> False * con -> None Create an update Brok :return:None """ was_alive = self.alive self.alive = False self.reachable = False self.con = None # We are dead now! ! We must propagate the sad news if was_alive: logger.warning("Setting the satellite %s as dead :(", self.get_name()) brok = self.get_update_status_brok() self.broks.append(brok) def add_failed_check_attempt(self, reason=''): """Go in reachable=False and add a failed attempt if we reach the max, go dead :param reason: the reason of adding an attempts (stack trace sometimes) :type reason: str :return: None """ self.reachable = False self.attempt += 1 self.attempt = min(self.attempt, self.max_check_attempts) logger.info("Failed attempt to %s (%d/%d), reason: %s", self.get_name(), self.attempt, self.max_check_attempts, reason) # Don't need to warn again and again if the satellite is already dead # Only warn when it is alive if self.alive: logger.warning("Add failed attempt to %s (%d/%d), reason: %s", self.get_name(), self.attempt, self.max_check_attempts, reason) # check when we just go HARD (dead) if self.attempt == self.max_check_attempts: self.set_dead() def update_infos(self, now): """Update satellite info each self.check_interval seconds so we smooth arbiter actions for just useful actions. Create update Brok :return: None """ # First look if it's not too early to ping if (now - self.last_check) < self.check_interval: return False self.last_check = now # We ping and update the managed list self.ping() if not self.alive: logger.info("Not alive for ping: %s", self.get_name()) return False if self.attempt > 0: logger.info("Not responding to ping: %s (%d / %d)", self.get_name(), self.attempt, self.max_check_attempts) return False self.update_managed_conf() # Update the state of this element brok = self.get_update_status_brok() self.broks.append(brok) def known_conf_managed_push(self, cfg_id, push_flavor): """The elements just got a new conf_id, we put it in our list because maybe the satellite is too busy to answer now :param cfg_id: config id :type cfg_id: int :param push_flavor: push_flavor we pushed earlier to the satellite :type push_flavor: int :return: None """ self.managed_confs[cfg_id] = push_flavor def ping(self): """Send a HTTP request to the satellite (GET /ping) Add failed attempt if an error occurs Otherwise, set alive this satellite :return: None """ if self.con is None: self.create_connection() # If the connection failed to initialize, bail out if self.con is None: self.add_failed_check_attempt('no connection exist on ping') return logger.debug("Pinging %s", self.get_name()) try: res = self.con.get('ping') # Should return us pong string if res == 'pong': self.set_alive() return True # This sould never happen! Except is the source code got modified! logger.warning("[%s] I responded '%s' to ping! WTF is it?", self.get_name(), res) self.add_failed_check_attempt('pinog / NOT pong') except HTTPClientConnectionException as exp: # pragma: no cover, simple protection logger.warning("[%s] Connection error when pinging: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection logger.warning("[%s] Connection timeout when pinging: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) except HTTPClientException as exp: logger.error("[%s] Error when pinging: %s", self.get_name(), str(exp)) # todo: raise an error and set daemon as dead? # any other error than conenction or timeout is really a bad situation !!! self.add_failed_check_attempt(reason=str(exp)) except AttributeError as exp: # pragma: no cover, simple protection # Connection is not created logger.error("[%s] ping - Connection does not exist!", self.get_name()) return False def wait_new_conf(self): # pragma: no cover, no more used """Send a HTTP request to the satellite (GET /wait_new_conf) TODO: is it still useful, wait_new_conf is implemented in the HTTP interface of each daemon :return: True if wait new conf, otherwise False :rtype: bool """ if not self.reachable: logger.warning("Not reachable for wait_new_conf: %s", self.get_name()) return False try: logger.warning("Arbiter wants me to wait for a new configuration") self.con.get('wait_new_conf') return True except HTTPClientConnectionException as exp: logger.warning( "[%s] Connection error when waiting new configuration: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection logger.warning( "[%s] Connection timeout when waiting new configuration: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) except HTTPClientException as exp: # pragma: no cover, simple protection logger.error("[%s] Error when waiting new configuration: %s", self.get_name(), str(exp)) except AttributeError as exp: # pragma: no cover, simple protection # Connection is not created logger.error("[%s] wait_new_conf - Connection does not exist!", self.get_name()) return False def have_conf(self, magic_hash=None): """Send a HTTP request to the satellite (GET /have_conf) Used to know if the satellite has a conf :param magic_hash: Config hash. Only used for HA arbiter communication :type magic_hash: int :return: Boolean indicating if the satellite has a (specific) configuration :type: bool """ if not self.reachable: logger.warning("Not reachable for have_conf: %s", self.get_name()) return False try: return self.con.get('have_conf', {'magic_hash': magic_hash}) except HTTPClientConnectionException as exp: logger.warning( "[%s] Connection error when testing if has configuration: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection logger.warning( "[%s] Connection timeout when testing if has configuration: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) except HTTPClientException as exp: # pragma: no cover, simple protection logger.error("[%s] Error when testing if has configuration: %s", self.get_name(), str(exp)) except AttributeError as exp: # pragma: no cover, simple protection # Connection is not created logger.error("[%s] have_conf - Connection does not exist! - %s", self.get_name(), exp) return False def remove_from_conf(self, sched_id): # pragma: no cover, no more used """Send a HTTP request to the satellite (GET /remove_from_conf) Tell a satellite to remove a scheduler from conf TODO: is it still useful, remove_from_conf is implemented in the HTTP interface of each daemon :param sched_id: scheduler id to remove :type sched_id: int :return: True on success, False on failure, None if can't connect :rtype: bool | None TODO: Return False instead of None """ if not self.reachable: logger.warning("Not reachable for remove_from_conf: %s", self.get_name()) return try: self.con.get('remove_from_conf', {'sched_id': sched_id}) # todo: do not handle the result to confirm? return True except HTTPClientConnectionException as exp: logger.warning( "[%s] Connection error when removing from configuration: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection logger.warning( "[%s] Connection timeout when removing from configuration: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) except HTTPClientException as exp: # pragma: no cover, simple protection logger.error("[%s] Error when removing from configuration: %s", self.get_name(), str(exp)) except AttributeError as exp: # pragma: no cover, simple protection # Connection is not created logger.error("[%s] remove_from_conf - Connection does not exist!", self.get_name()) return False def update_managed_conf(self): """Send a HTTP request to the satellite (GET /what_i_managed) and update managed_conf attribute with dict (cleaned) Set to {} on failure :return: None """ self.managed_confs = {} if not self.reachable: logger.warning("Not reachable for update_managed_conf: %s", self.get_name()) return try: res = self.con.get('what_i_managed') self.managed_confs = res # self.managed_confs = unserialize(str(res)) return True except HTTPClientConnectionException as exp: logger.warning( "[%s] Connection error when getting what I manage: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection logger.warning( "[%s] Connection timeout when getting what I manage: %s", self.get_name(), str(exp)) logger.debug("Connection: %s", self.con.__dict__) self.add_failed_check_attempt(reason=str(exp)) except HTTPClientException as exp: # pragma: no cover, simple protection logger.warning("Error to the %s '%s' when getting what I manage", self.my_type, self.get_name()) logger.exception("Raised exception: %s", exp) except AttributeError as exp: # pragma: no cover, simple protection # Connection is not created logger.error( "[%s] update_managed_conf - Connection does not exist!", self.get_name()) return False def do_i_manage(self, cfg_id, push_flavor): """Tell if the satellite is managing cfg_id with push_flavor :param cfg_id: config id :type cfg_id: int :param push_flavor: flavor id, random it generated at parsing :type push_flavor: int :return: True if the satellite has push_flavor in managed_confs[cfg_id] :rtype: bool """ if self.managed_confs: logger.debug("My managed configurations:") for conf in self.managed_confs: logger.debug("- %s", conf) else: logger.debug("No managed configuration!") # If not even the cfg_id in the managed_conf, bail out if cfg_id not in self.managed_confs: logger.warning("I (%s) do not manage this configuration: %s", self, cfg_id) return False # maybe it's in but with a false push_flavor. check it :) return self.managed_confs[cfg_id] == push_flavor def push_broks(self, broks): """Send a HTTP request to the satellite (GET /ping) and THEN Send a HTTP request to the satellite (POST /push_broks) Send broks to the satellite The first ping ensure the satellite is there to avoid a big timeout :param broks: Brok list to send :type broks: list :return: True on success, False on failure :rtype: bool """ if not self.reachable: logger.warning("Not reachable for push_broks: %s", self.get_name()) return False try: self.con.post('push_broks', {'broks': broks}, wait='long') return True except HTTPClientConnectionException as exp: logger.warning("[%s] Connection error when pushing broks: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection logger.warning("[%s] Connection timeout when pushing broks: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) except HTTPClientException as exp: # pragma: no cover, simple protection logger.error("[%s] Error when pushing broks: %s", self.get_name(), str(exp)) except AttributeError as exp: # pragma: no cover, simple protection # Connection is not created logger.error("[%s] push_broks - Connection does not exist!", self.get_name()) return False def get_external_commands(self): """Send a HTTP request to the satellite (GET /ping) and THEN send a HTTP request to the satellite (GET /get_external_commands) Get external commands from satellite. Un-serialize data received. :return: External Command list on success, [] on failure :rtype: list """ if not self.reachable: logger.warning("Not reachable for get_external_commands: %s", self.get_name()) return [] try: res = self.con.get('get_external_commands', wait='long') tab = unserialize(str(res)) # Protect against bad return if not isinstance(tab, list): self.con = None return [] return tab except HTTPClientConnectionException as exp: logger.warning( "[%s] Connection error when getting external commands: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) self.set_dead() except HTTPClientTimeoutException as exp: # pragma: no cover, simple protection logger.warning( "[%s] Connection timeout when getting external commands: %s", self.get_name(), str(exp)) self.add_failed_check_attempt(reason=str(exp)) except HTTPClientException as exp: # pragma: no cover, simple protection logger.error("[%s] Error when getting external commands: %s", self.get_name(), str(exp)) self.con = None except AttributeError as exp: # pragma: no cover, simple protection # Connection is not created logger.error( "[%s] get_external_commands - Connection does not exist!", self.get_name()) except AlignakClassLookupException as exp: # pragma: no cover, simple protection logger.error('Cannot un-serialize external commands received: %s', exp) return [] def prepare_for_conf(self): """Init cfg dict attribute with __class__.properties and extra __class__ attribute (like __init__ could do with an object) :return: None """ self.cfg = {'global': {}, 'schedulers': {}, 'arbiters': {}} properties = self.__class__.properties for prop, entry in properties.items(): if entry.to_send: self.cfg['global'][prop] = getattr(self, prop) cls = self.__class__ # Also add global values self.cfg['global']['statsd_host'] = cls.statsd_host self.cfg['global']['statsd_port'] = cls.statsd_port self.cfg['global']['statsd_prefix'] = cls.statsd_prefix self.cfg['global']['statsd_enabled'] = cls.statsd_enabled def add_global_conf_parameters(self, params): """Add some extra params in cfg dict attribute. Some attributes are in the global configuration :param params: dict to update cfg with :type params: dict :return: None """ for prop in params: self.cfg['global'][prop] = params[prop] def get_my_type(self): """Get the satellite type. Accessor to __class__.mytype ie : poller, scheduler, receiver, broker, arbiter or reactionner :return: Satellite type :rtype: str """ return self.__class__.my_type def give_satellite_cfg(self): """Get a configuration for this satellite. Not used by Scheduler and Arbiter (overridden) :return: Configuration for satellite :rtype: dict """ return { 'port': self.port, 'address': self.address, 'name': self.get_name(), 'instance_id': self.uuid, 'use_ssl': self.use_ssl, 'hard_ssl_name_check': self.hard_ssl_name_check, 'timeout': self.timeout, 'data_timeout': self.data_timeout, 'max_check_attempts': self.max_check_attempts, 'active': True, 'passive': self.passive, 'poller_tags': getattr(self, 'poller_tags', []), 'reactionner_tags': getattr(self, 'reactionner_tags', []) }