#!/usr/bin/env python3 # import os import syslog import threading from swsscommon.swsscommon import ConfigDBConnector from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler # FILE RADIUS_PAM_AUTH_CONF_DIR = "/etc/pam_radius_auth.d/" RADIUS_PAM_AUTH_CONF_STATS_DIR = "/etc/pam_radius_auth.d/statistics/" class RadiusCountersDbMon (threading.Thread): def __init__(self, ID, name, radiusStatsInstance): threading.Thread.__init__(self) self.ID = ID self.name = name self.radiusStatsInstance = radiusStatsInstance def handle_CountersDbRadiusClear(self, key, data): # print("RadiusCountersDbMon.handle_CountersDbRadiusClear()") if key == 'clear': self.radiusStatsInstance.handle_clear() def run(self): # print("RadiusCountersDbMon.run()") self.radiusStatsInstance.counters_db.subscribe('RADIUS', lambda table, key, data: self.handle_CountersDbRadiusClear(key, data)) self.radiusStatsInstance.counters_db.listen() # print("RadiusCountersDbMon.run(): After listen()") class RadiusStatsFileHandler(FileSystemEventHandler): def __init__(self, radiusStatsInstance): self.radiusStatsInstance = radiusStatsInstance def on_any_event(self, event): # print("RadiusStatsFileHandler.on_any_event()") if event.is_directory: return None self.radiusStatsInstance.handle_update(os.path.basename(event.src_path)) class RadiusStatsFileMon (): def __init__(self, radiusStatsInstance): self.event_handler = RadiusStatsFileHandler(radiusStatsInstance) self.observer = Observer() self.observer.schedule(self.event_handler, RADIUS_PAM_AUTH_CONF_STATS_DIR, recursive=False) self.observer.start() # print("RadiusStatsFileMon.__init__(): After observer.start()") def stop(self): # print("RadiusStatsFileMon.stop()") self.observer.stop() # print("RadiusStatsFileMon.stop(): After observer.stop()") self.observer.join() # print("RadiusStatsFileMon.stop(): After observer.join()") class RadiusStatistics: def __init__(self, cfg_db, rad_global_conf, radius_conf): self.radius_counter_names = [ "counter_0", "access_requests", "access_accepts", "access_rejects", "accounting_requests", "accounting_responses", "counter_6", "counter_7", "counter_8", "counter_9", "counter_10", "access_challenges", "counter_12", "counter_13", "counter_14", "counter_15", "counter_16", "retried_access_requests", "counter_18", "counter_19", "retried_accounting_requests", "counter_21", "counter_22", "counter_23", "counter_24", "counter_25", "counter_26", "retried_access_challenges", "counter_28", "counter_29", "counter_30", "counter_31", "timeouts", "bad_authenticators", "invalid_packets", "counter_35", ] self.radius_global = { 'statistics': 'False' } self.radius_servers = {} self.config_db = cfg_db for row in rad_global_conf: self.radius_global_update(row, rad_global_conf[row]) for row in radius_conf: self.radius_server_update(row, radius_conf[row]) self.counters_db = ConfigDBConnector() self.counters_db.db_connect('COUNTERS_DB', wait_for_init=False, retry_on=True) syslog.syslog(syslog.LOG_INFO, 'CountersDB connect success') self.dbmon_thread = RadiusCountersDbMon("RadiusCountersDbMon", "RadiusCountersDbMon", self) self.dbmon_thread.daemon = True self.dbmon_thread.start() self.filemon = RadiusStatsFileMon(self) syslog.syslog(syslog.LOG_INFO, 'RADIUS Stats File Monitor started') def radius_global_update(self, key, data): if key == 'global': self.radius_global.update(data) for addr in self.radius_servers: self.create_file(addr) def radius_server_update(self, key, data): if data == {}: if key in self.radius_servers: del self.radius_servers[key] else: self.radius_servers[key] = data self.create_file(key) def create_file(self, addr): # print( "RadiusStatistics.create_file({})".format(addr)) stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + addr if self.radius_global['statistics'] == 'False': if os.path.exists(stats_file): os.unlink(stats_file) else: open(stats_file, 'a').close() os.chmod(stats_file, 0o666) self.handle_update(addr) def handle_clear(self): # print( "RadiusStatistics.handle_clear()") for filename in os.listdir(RADIUS_PAM_AUTH_CONF_STATS_DIR): stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + filename open(stats_file, 'w').close() def handle_update(self, srv): # print( "RadiusStatistics.handle_update({})".format(srv)) if self.radius_global['statistics'] == 'False': return stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + srv entry = None if os.path.exists(stats_file): with open(stats_file, 'r') as f: lines = f.readlines() if len(lines) > 0: radius_counters = lines[0].split(' ') entry = dict(zip(self.radius_counter_names, radius_counters)) counters_db = ConfigDBConnector() counters_db.db_connect('COUNTERS_DB', wait_for_init=False, retry_on=False) counters_db.set_entry('RADIUS_SERVER_STATS', srv, entry) counters_db.close(counters_db.COUNTERS_DB) class AAAStatsDaemon: def __init__(self): self.config_db = ConfigDBConnector() self.config_db.connect(wait_for_init=True, retry_on=True) syslog.syslog(syslog.LOG_INFO, 'ConfigDB connect success') radius_global = self.config_db.get_table('RADIUS') radius_server = self.config_db.get_table('RADIUS_SERVER') self.radiusstats = RadiusStatistics(self.config_db, radius_global, radius_server) def radius_global_handler(self, key, data): self.radiusstats.radius_global_update(key, data) def radius_server_handler(self, key, data): self.radiusstats.radius_server_update(key, data) def start(self): self.config_db.subscribe('RADIUS_SERVER', lambda table, key, data: self.radius_server_handler(key, data)) self.config_db.subscribe('RADIUS', lambda table, key, data: self.radius_global_handler(key, data)) self.config_db.listen() # print( "After config_db.listen()") syslog.syslog(syslog.LOG_INFO, 'Stopping FileMon') self.radiusstats.filemon.stop() # print( "Exiting") syslog.syslog(syslog.LOG_INFO, 'Exiting') def main(): daemon = AAAStatsDaemon() daemon.start() if __name__ == "__main__": main()