def _update_bird_conf_file(self, operation): """Update BIRD configuration. It adds to or removes IP prefix from BIRD configuration. It also updates generation time stamp in the configuration file. Main program will exit if configuration file cant be read/written. Arguments: operation (obj): Either an AddOperation or DeleteOperation object Returns: True if BIRD configuration was updated otherwise False. """ conf_updated = False prefixes = [] ip_version = operation.ip_version config_file = self.bird_configuration[ip_version]['config_file'] variable_name = self.bird_configuration[ip_version]['variable_name'] changes_counter =\ self.bird_configuration[ip_version]['changes_counter'] dummy_ip_prefix =\ self.bird_configuration[ip_version]['dummy_ip_prefix'] try: prefixes = get_ip_prefixes_from_bird(config_file) except OSError as error: self.log.error( "failed to open Bird configuration %s, this is a " "FATAL error, thus exiting main program", error) sys.exit(1) if not prefixes: self.log.error( "found empty bird configuration %s, this is a FATAL" " error, thus exiting main program", config_file) sys.exit(1) if dummy_ip_prefix not in prefixes: self.log.warning( "dummy IP prefix %s wasn't found in bird " "configuration, adding it. This shouldn't have " "happened!", dummy_ip_prefix) prefixes.insert(0, dummy_ip_prefix) conf_updated = True ip_prefixes_without_check = set(prefixes).difference( self.ip_prefixes[ip_version]) if ip_prefixes_without_check: self.log.warning( "found %s IP prefixes in Bird configuration but " "we aren't configured to run health checks on " "them. Either someone modified the configuration " "manually or something went horrible wrong. We " "remove them from Bird configuration", ','.join(ip_prefixes_without_check)) # This is faster than using lambda and filter. # NOTE: We don't use remove method as we want to remove more than # occurrences of the IP prefixes without check. prefixes[:] = (ip for ip in prefixes if ip not in ip_prefixes_without_check) conf_updated = True # Update the list of IP prefixes based on the status of health check. if operation.update(prefixes): conf_updated = True if not conf_updated: self.log.info('no updates for bird configuration') return conf_updated if self.bird_configuration[ip_version]['keep_changes']: archive_bird_conf(config_file, changes_counter) # some IP prefixes are either removed or added, create # configuration with new data. tempname = write_temp_bird_conf(dummy_ip_prefix, config_file, variable_name, prefixes) try: os.rename(tempname, config_file) except OSError as error: self.log.critical( "failed to create Bird configuration %s, this " "is a FATAL error, thus exiting main program", error) sys.exit(1) else: self.log.info("Bird configuration for IPv%s is updated", ip_version) # dummy_ip_prefix is always there if len(prefixes) == 1: self.log.warning("Bird configuration doesn't have IP prefixes for " "any of the services we monitor! It means local " "node doesn't receive any traffic") return conf_updated
def _update_bird_conf_file(self, operation): """Updates BIRD configuration. Adds/removes entries from a list and updates generation time stamp. Main program will exit if configuration file cant be read/written. Arguments: operation (obj): Either an AddOperation or DeleteOperation object Returns: True if BIRD configuration was updated otherwise False. """ conf_updated = False prefixes = [] ip_version = operation.ip_version config_file = self.bird_configuration[ip_version]['config_file'] variable_name = self.bird_configuration[ip_version]['variable_name'] changes_counter =\ self.bird_configuration[ip_version]['changes_counter'] dummy_ip_prefix =\ self.bird_configuration[ip_version]['dummy_ip_prefix'] try: prefixes = get_ip_prefixes_from_bird(config_file) except OSError as error: msg = ("failed to open Bird configuration {e}, this is a FATAL " "error, thus exiting main program" .format(e=error)) self.log.error(msg, priority=80) sys.exit(1) if not prefixes: msg = ("found empty bird configuration:{f}, this is a FATAL " "error, thus exiting main program" .format(f=config_file)) self.log.error(msg, priority=80) sys.exit(1) if dummy_ip_prefix not in prefixes: msg = ("dummy IP prefix {i} wasn't found in bird configuration, " "adding it. This shouldn't have happened!" .format(i=dummy_ip_prefix)) self.log.warning(msg, priority=20) prefixes.insert(0, dummy_ip_prefix) conf_updated = True ip_prefixes_without_check = set(prefixes).difference( self.ip_prefixes[ip_version]) if ip_prefixes_without_check: msg = ("found {i} IP prefixes in Bird configuration but we aren't " "configured to run health checks on them. Either someone " "modified the configuration manually or something went " "horrible wrong. We remove them from Bird configuration" .format(i=','.join(ip_prefixes_without_check))) self.log.warning(msg, priority=20) # This is faster than using lambda and filter. # NOTE: We don't use remove method as we want to remove more than # occurrences of the IP prefixes without check. prefixes[:] = (ip for ip in prefixes if ip not in ip_prefixes_without_check) conf_updated = True # Update the list of IP prefixes based on the status of health check. if operation.update(prefixes): conf_updated = True if not conf_updated: self.log.info('no updates for bird configuration') return conf_updated if self.bird_configuration[ip_version]['keep_changes']: archive_bird_conf(self.log, config_file, changes_counter) # some IP prefixes are either removed or added, create # configuration with new data. tempname = write_temp_bird_conf( self.log, dummy_ip_prefix, config_file, variable_name, prefixes ) try: os.rename(tempname, config_file) except OSError as error: msg = ('failed to create Bird configuration {e}, this is a FATAL ' 'error, thus exiting main program' .format(e=error)) self.log.critical(msg, priority=80) sys.exit(1) else: self.log.info("Bird configuration for IPv{v} is updated" .format(v=ip_version)) # dummy_ip_prefix is always there if len(prefixes) == 1: self.log.warning("Bird configuration doesn't have IP prefixes for " "any of the services we monitor! It means local " "node doesn't receive any traffic", priority=80) return conf_updated
def _update_bird_conf_file(self, operation): """Updates BIRD configuration. Adds/removes entries from a list and updates generation time stamp. Main program will exit if configuration file cant be read/written. Arguments: operation (obj): Either an AddOperation or DeleteOperation object Returns: True if BIRD configuration was updated otherwise False. """ conf_updated = False prefixes = [] try: prefixes = get_ip_prefixes_from_bird(self.bird_conf_file) except OSError as error: msg = ("failed to open Bird configuration {e}, this is a FATAL " "error, thus exiting main program" .format(e=error)) self.log.error(msg, priority=80) sys.exit(1) if not prefixes: msg = ("found empty bird configuration:{f}, this is a FATAL " "error, thus exiting main program" .format(f=self.bird_conf_file)) self.log.error(msg, priority=80) sys.exit(1) if self.dummy_ip_prefix not in prefixes: msg = ("dummy IP prefix {i} wasn't found in bird configuration, " "adding it. This shouldn't have happened!" .format(i=self.dummy_ip_prefix)) self.log.warning(msg, priority=20) prefixes.insert(0, self.dummy_ip_prefix) conf_updated = True # Remove IP prefixes for which we don't have a configuration for them. notconfigured_ip_prefixes = ip_prefixes_without_config(prefixes, self.config, self.services) if notconfigured_ip_prefixes: msg = ("found {i} IP prefixes in Bird configuration but we aren't " "configured to run health checks on them. Either someone " "modified the configuration manually or something went " "horrible wrong. Thus, we remove them from Bird " "configuration" .format(i=','.join(notconfigured_ip_prefixes))) self.log.warning(msg, priority=20) # This is faster than using lambda and filter. # NOTE: We don't use remove method as we want to remove more than # occurrences of the IP prefixes without check. prefixes[:] = (ip for ip in prefixes if ip not in notconfigured_ip_prefixes) conf_updated = True # Update the list of IP prefixes based on the status of health check. if operation.update(prefixes): conf_updated = True if not conf_updated: self.log.info('no updates for bird configuration') return conf_updated if self.keep_bird_changes: archive_bird_conf(self.log, self.bird_conf_file, self.changes_counter) # some IP prefixes are either removed or added, create # configuration with new data. tempname = write_temp_bird_conf(self.log, self.dummy_ip_prefix, self.bird_conf_file, self.bird_constant_name, prefixes) try: os.rename(tempname, self.bird_conf_file) except OSError as error: msg = ('failed to create Bird configuration {e}, this is a FATAL ' 'error, thus exiting main program' .format(e=error)) self.log.critical(msg, priority=80) sys.exit(1) else: self.log.info('Bird configuration is updated') # dummy_ip_prefix is always there if len(prefixes) == 1: self.log.warning("Bird configuration doesn't have IP prefixes for " "any of the services we monitor! It means local " "node doesn't receive any traffic", priority=80) return conf_updated
def _update_bird_prefix_conf(self, operation): """Updates BIRD configuration. Adds/removes entries from a list and updates generation time stamp. Main program will exit if configuration file cant be read/written. Arguments: operation (obj): Either an AddOperation or DeleteOperation object Returns: True if BIRD configuration was updated otherwise False. """ conf_updated = False prefixes = [] comment = ("# {} is a dummy IP Prefix. It should NOT be used and " "REMOVED from the constant.".format(self.dummy_ip_prefix)) try: prefixes = get_ip_prefixes_from_bird(self.bird_conf_file, die=False) except OSError as err: self.log.error("Failed to open bird configuration, {}".format(err)) self.log.critical("This is a FATAL error, exiting") sys.exit(1) if not prefixes: self.log.error("Found empty bird configuration:{}".format( self.bird_conf_file)) self.log.critical("This is a FATAL error, exiting") sys.exit(1) if self.dummy_ip_prefix not in prefixes: self.log.warning("Dummy IP prefix {} wasn't found in bird " "configuration, adding it. This shouldn't have " "happened!".format(self.dummy_ip_prefix)) prefixes.insert(0, self.dummy_ip_prefix) conf_updated = True # Update the list of IP prefixes. conf_updated = operation.update(prefixes) if not conf_updated: self.log.info("No updates for bird configuration") return conf_updated # some IP prefixes are either removed or added, truncate configuration # with new data. try: with open(self.bird_conf_file, 'r+') as bird_conf: bird_conf.seek(0) bird_conf.write("# Generated {time} by anycast-healthchecker" "\n".format(time=time.ctime())) bird_conf.write("{}\n".format(comment)) bird_conf.write("define {} =\n".format(self.bird_constant_name)) bird_conf.write("{}[\n".format(4 * ' ')) # all entries of the array constant need a trailing comma # except the last one. A single element array doesn't need the # trailing comma. bird_conf.write(',\n'.join(map(lambda p: ' '*8 + p, prefixes))) bird_conf.write("\n{spaces}];\n".format(spaces=4 * ' ')) bird_conf.truncate() bird_conf.close() self.log.info("Bird configuration is updated") except OSError as error: self.log.critical("Failed to update bird configuration") self.log.critical(error) self.log.critical("This is a FATAL error, exiting") sys.exit(1) return conf_updated
def _update_bird_prefix_conf(self, health_action): """Updates BIRD configuration. Adds/removes entries from a list and updates generation time stamp. Main program will exit if configuration file cant be read/written. Arguments: health_action (tuple): A 3 element tuple: 1st: The name of the thread (str) 2nd: ip_prefix (str) 3nd: Action to take, either 'add' or 'del' (str) Returns: True if BIRD configuration was updated otherwise False. """ conf_updated = False prefixes = [] name = health_action[0] ip_prefix = health_action[1] action = health_action[2] comment = ("# {} is a dummy IP Prefix. It should NOT be used and " "REMOVED from the constant.".format(self.dummy_ip_prefix)) try: prefixes = get_ip_prefixes_from_bird(self.bird_conf_file, die=False) except OSError as err: self.log.error("Failed to open bird configuration, {}".format(err)) self.log.critical("This is a FATAL error, exiting") sys.exit(1) if not prefixes: self.log.error("Found empty bird configuration:{}".format( self.bird_conf_file)) self.log.critical("This is a FATAL error, exiting") sys.exit(1) if self.dummy_ip_prefix not in prefixes: self.log.warning("Dummy IP prefix {} wasn't found in bird " "configuration, adding it. This shouldn't have " "happened!".format(self.dummy_ip_prefix)) prefixes.insert(0, self.dummy_ip_prefix) conf_updated = True if action == 'del' and ip_prefix in prefixes: self.log.info("Withdrawing {} for {}".format(ip_prefix, name)) prefixes.remove(ip_prefix) conf_updated = True elif action == 'add' and ip_prefix not in prefixes: self.log.info("Announcing {} for {}".format(ip_prefix, name)) prefixes.append(ip_prefix) conf_updated = True if not conf_updated: self.log.info("No updates for bird configuration") return conf_updated # some IP prefixes are either removed or added, truncate configuration # with new data. try: with open(self.bird_conf_file, 'r+') as bird_conf: bird_conf.seek(0) bird_conf.write("# Generated {time} by anycast-healthchecker" "\n".format(time=time.ctime())) bird_conf.write("{}\n".format(comment)) bird_conf.write("define {} =\n".format(self.bird_constant_name)) bird_conf.write("{}[\n".format(4 * ' ')) # all entries of the array constant need a trailing comma # except the last one. A single element array doesn't need the # trailing comma. for prefix in prefixes[:-1]: bird_conf.write("{}{},\n".format(8 * ' ', prefix)) bird_conf.write("{}{}\n".format(8 * ' ', prefixes[len(prefixes) - 1])) bird_conf.write("{}];\n".format(4 * ' ')) bird_conf.truncate() bird_conf.close() self.log.info("Bird configuration is updated") except OSError as error: self.log.critical("Failed to update bird configuration") self.log.critical(error) self.log.critical("This is a FATAL error, exiting") sys.exit(1) return conf_updated