diff --git a/mktxp/basep.py b/mktxp/basep.py index f67e160..a72700b 100644 --- a/mktxp/basep.py +++ b/mktxp/basep.py @@ -11,24 +11,27 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from http.server import HTTPServer from datetime import datetime from prometheus_client.core import REGISTRY from prometheus_client import MetricsHandler, start_http_server from mktxp.cli.config.config import config_handler from mktxp.collectors_handler import CollectorsHandler -from mktxp.metrics_handler import RouterMetricsHandler +from mktxp.router_entries_handler import RouterEntriesHandler from mktxp.cli.output.capsman_out import CapsmanOutput from mktxp.cli.output.wifi_out import WirelessOutput +from mktxp.cli.output.dhcp_out import DHCPOutput + class MKTXPProcessor: ''' Base Export Processing ''' @staticmethod def start(): - router_metrics_handler = RouterMetricsHandler() - REGISTRY.register(CollectorsHandler(router_metrics_handler)) + router_entries_handler = RouterEntriesHandler() + REGISTRY.register(CollectorsHandler(router_entries_handler)) MKTXPProcessor.run(port=config_handler._entry().port) @staticmethod @@ -45,13 +48,18 @@ class MKTXPCLIProcessor: ''' @staticmethod def capsman_clients(entry_name): - router_metric = RouterMetricsHandler.router_metric(entry_name) - if router_metric: - CapsmanOutput.clients_summary(router_metric) + router_entry = RouterEntriesHandler.router_entry(entry_name) + if router_entry: + CapsmanOutput.clients_summary(router_entry) @staticmethod def wifi_clients(entry_name): - router_metric = RouterMetricsHandler.router_metric(entry_name) - if router_metric: - WirelessOutput.clients_summary(router_metric) + router_entry = RouterEntriesHandler.router_entry(entry_name) + if router_entry: + WirelessOutput.clients_summary(router_entry) + @staticmethod + def dhcp_clients(entry_name): + router_entry = RouterEntriesHandler.router_entry(entry_name) + if router_entry: + DHCPOutput.clients_summary(router_entry) diff --git a/mktxp/cli/config/_mktxp.conf b/mktxp/cli/config/_mktxp.conf index 9888f25..567c776 100644 --- a/mktxp/cli/config/_mktxp.conf +++ b/mktxp/cli/config/_mktxp.conf @@ -12,9 +12,10 @@ [MKTXP] - port = 49090 - socket_timeout = 2 - initial_delay_on_failure = 120 - max_delay_on_failure = 900 - delay_inc_div = 5 - bandwidth_test_interval = 420 + port = 49090 # default metrics HTTP server port + bandwidth_test_interval = 420 # Interval between periodic bandwidth tests + + socket_timeout = 2 # Socket connection timeout + initial_delay_on_failure = 120 # Delay untill next connection attempt to a RouterOS Device + max_delay_on_failure = 900 # Max delay untill next connection attempt to a RouterOS Device + delay_inc_div = 5 # Delay increment factor diff --git a/mktxp/cli/config/config.py b/mktxp/cli/config/config.py index 064c95f..6c92341 100755 --- a/mktxp/cli/config/config.py +++ b/mktxp/cli/config/config.py @@ -23,263 +23,263 @@ from mktxp.utils.utils import FSHelper ''' class MKTXPConfigKeys: - ''' MKTXP config file keys - ''' - # Section Keys - ENABLED_KEY = 'enabled' - HOST_KEY = 'hostname' - PORT_KEY = 'port' - USER_KEY = 'username' - PASSWD_KEY = 'password' + ''' MKTXP config file keys + ''' + # Section Keys + ENABLED_KEY = 'enabled' + HOST_KEY = 'hostname' + PORT_KEY = 'port' + USER_KEY = 'username' + PASSWD_KEY = 'password' - SSL_KEY = 'use_ssl' - NO_SSL_CERTIFICATE = 'no_ssl_certificate' - SSL_CERTIFICATE_VERIFY = 'ssl_certificate_verify' + SSL_KEY = 'use_ssl' + NO_SSL_CERTIFICATE = 'no_ssl_certificate' + SSL_CERTIFICATE_VERIFY = 'ssl_certificate_verify' - FE_DHCP_KEY = 'dhcp' - FE_DHCP_LEASE_KEY = 'dhcp_lease' - FE_DHCP_POOL_KEY = 'pool' - FE_INTERFACE_KEY = 'interface' - FE_FIREWALL_KEY = 'firewall' - FE_MONITOR_KEY = 'monitor' - FE_ROUTE_KEY = 'route' - FE_WIRELESS_KEY = 'wireless' - FE_WIRELESS_CLIENTS_KEY = 'wireless_clients' - FE_CAPSMAN_KEY = 'capsman' - FE_CAPSMAN_CLIENTS_KEY = 'capsman_clients' + FE_DHCP_KEY = 'dhcp' + FE_DHCP_LEASE_KEY = 'dhcp_lease' + FE_DHCP_POOL_KEY = 'pool' + FE_INTERFACE_KEY = 'interface' + FE_FIREWALL_KEY = 'firewall' + FE_MONITOR_KEY = 'monitor' + FE_ROUTE_KEY = 'route' + FE_WIRELESS_KEY = 'wireless' + FE_WIRELESS_CLIENTS_KEY = 'wireless_clients' + FE_CAPSMAN_KEY = 'capsman' + FE_CAPSMAN_CLIENTS_KEY = 'capsman_clients' - MKTXP_SOCKET_TIMEOUT = 'socket_timeout' - MKTXP_SOCKET_TIMEOUT = 'socket_timeout' - MKTXP_INITIAL_DELAY = 'initial_delay_on_failure' - MKTXP_MAX_DELAY = 'max_delay_on_failure' - MKTXP_INC_DIV = 'delay_inc_div' - MKTXP_BANDWIDTH_TEST_INTERVAL = 'bandwidth_test_interval' + MKTXP_SOCKET_TIMEOUT = 'socket_timeout' + MKTXP_SOCKET_TIMEOUT = 'socket_timeout' + MKTXP_INITIAL_DELAY = 'initial_delay_on_failure' + MKTXP_MAX_DELAY = 'max_delay_on_failure' + MKTXP_INC_DIV = 'delay_inc_div' + MKTXP_BANDWIDTH_TEST_INTERVAL = 'bandwidth_test_interval' - # UnRegistered entries placeholder - NO_ENTRIES_REGISTERED = 'NoEntriesRegistered' + # UnRegistered entries placeholder + NO_ENTRIES_REGISTERED = 'NoEntriesRegistered' - MKTXP_USE_COMMENTS_OVER_NAMES = 'use_comments_over_names' + MKTXP_USE_COMMENTS_OVER_NAMES = 'use_comments_over_names' - # Base router id labels - ROUTERBOARD_NAME = 'routerboard_name' - ROUTERBOARD_ADDRESS = 'routerboard_address' + # Base router id labels + ROUTERBOARD_NAME = 'routerboard_name' + ROUTERBOARD_ADDRESS = 'routerboard_address' - # Default values - DEFAULT_API_PORT = 8728 - DEFAULT_API_SSL_PORT = 8729 - DEFAULT_MKTXP_PORT = 49090 - DEFAULT_MKTXP_SOCKET_TIMEOUT = 2 - DEFAULT_MKTXP_INITIAL_DELAY = 120 - DEFAULT_MKTXP_MAX_DELAY = 900 - DEFAULT_MKTXP_INC_DIV = 5 - DEFAULT_MKTXP_BANDWIDTH_TEST_INTERVAL = 420 + # Default values + DEFAULT_API_PORT = 8728 + DEFAULT_API_SSL_PORT = 8729 + DEFAULT_MKTXP_PORT = 49090 + DEFAULT_MKTXP_SOCKET_TIMEOUT = 2 + DEFAULT_MKTXP_INITIAL_DELAY = 120 + DEFAULT_MKTXP_MAX_DELAY = 900 + DEFAULT_MKTXP_INC_DIV = 5 + DEFAULT_MKTXP_BANDWIDTH_TEST_INTERVAL = 420 - BOOLEAN_KEYS = (ENABLED_KEY, SSL_KEY, NO_SSL_CERTIFICATE, SSL_CERTIFICATE_VERIFY, - FE_DHCP_KEY, FE_DHCP_LEASE_KEY, FE_DHCP_POOL_KEY, FE_INTERFACE_KEY, FE_FIREWALL_KEY, - FE_MONITOR_KEY, FE_ROUTE_KEY, MKTXP_USE_COMMENTS_OVER_NAMES, - FE_WIRELESS_KEY, FE_WIRELESS_CLIENTS_KEY, FE_CAPSMAN_KEY, FE_CAPSMAN_CLIENTS_KEY) - - STR_KEYS = (HOST_KEY, USER_KEY, PASSWD_KEY) - MKTXP_INT_KEYS = (PORT_KEY, MKTXP_SOCKET_TIMEOUT, MKTXP_INITIAL_DELAY, MKTXP_MAX_DELAY, MKTXP_INC_DIV, MKTXP_BANDWIDTH_TEST_INTERVAL) + BOOLEAN_KEYS = (ENABLED_KEY, SSL_KEY, NO_SSL_CERTIFICATE, SSL_CERTIFICATE_VERIFY, + FE_DHCP_KEY, FE_DHCP_LEASE_KEY, FE_DHCP_POOL_KEY, FE_INTERFACE_KEY, FE_FIREWALL_KEY, + FE_MONITOR_KEY, FE_ROUTE_KEY, MKTXP_USE_COMMENTS_OVER_NAMES, + FE_WIRELESS_KEY, FE_WIRELESS_CLIENTS_KEY, FE_CAPSMAN_KEY, FE_CAPSMAN_CLIENTS_KEY) + + STR_KEYS = (HOST_KEY, USER_KEY, PASSWD_KEY) + MKTXP_INT_KEYS = (PORT_KEY, MKTXP_SOCKET_TIMEOUT, MKTXP_INITIAL_DELAY, MKTXP_MAX_DELAY, MKTXP_INC_DIV, MKTXP_BANDWIDTH_TEST_INTERVAL) - # MKTXP config entry nane - MKTXP_CONFIG_ENTRY_NAME = 'MKTXP' + # MKTXP config entry nane + MKTXP_CONFIG_ENTRY_NAME = 'MKTXP' class ConfigEntry: - MKTXPEntry = namedtuple('MKTXPEntry', [MKTXPConfigKeys.ENABLED_KEY, MKTXPConfigKeys.HOST_KEY, MKTXPConfigKeys.PORT_KEY, - MKTXPConfigKeys.USER_KEY, MKTXPConfigKeys.PASSWD_KEY, - MKTXPConfigKeys.SSL_KEY, MKTXPConfigKeys.NO_SSL_CERTIFICATE, MKTXPConfigKeys.SSL_CERTIFICATE_VERIFY, - - MKTXPConfigKeys.FE_DHCP_KEY, MKTXPConfigKeys.FE_DHCP_LEASE_KEY, MKTXPConfigKeys.FE_DHCP_POOL_KEY, MKTXPConfigKeys.FE_INTERFACE_KEY, MKTXPConfigKeys.FE_FIREWALL_KEY, - MKTXPConfigKeys.FE_MONITOR_KEY, MKTXPConfigKeys.FE_ROUTE_KEY, MKTXPConfigKeys.FE_WIRELESS_KEY, MKTXPConfigKeys.FE_WIRELESS_CLIENTS_KEY, - MKTXPConfigKeys.FE_CAPSMAN_KEY, MKTXPConfigKeys.FE_CAPSMAN_CLIENTS_KEY, MKTXPConfigKeys.MKTXP_USE_COMMENTS_OVER_NAMES - ]) - _MKTXPEntry = namedtuple('_MKTXPEntry', [MKTXPConfigKeys.PORT_KEY, MKTXPConfigKeys.MKTXP_SOCKET_TIMEOUT, - MKTXPConfigKeys.MKTXP_INITIAL_DELAY, MKTXPConfigKeys.MKTXP_MAX_DELAY, - MKTXPConfigKeys.MKTXP_INC_DIV, MKTXPConfigKeys.MKTXP_BANDWIDTH_TEST_INTERVAL]) + MKTXPEntry = namedtuple('MKTXPEntry', [MKTXPConfigKeys.ENABLED_KEY, MKTXPConfigKeys.HOST_KEY, MKTXPConfigKeys.PORT_KEY, + MKTXPConfigKeys.USER_KEY, MKTXPConfigKeys.PASSWD_KEY, + MKTXPConfigKeys.SSL_KEY, MKTXPConfigKeys.NO_SSL_CERTIFICATE, MKTXPConfigKeys.SSL_CERTIFICATE_VERIFY, + + MKTXPConfigKeys.FE_DHCP_KEY, MKTXPConfigKeys.FE_DHCP_LEASE_KEY, MKTXPConfigKeys.FE_DHCP_POOL_KEY, MKTXPConfigKeys.FE_INTERFACE_KEY, MKTXPConfigKeys.FE_FIREWALL_KEY, + MKTXPConfigKeys.FE_MONITOR_KEY, MKTXPConfigKeys.FE_ROUTE_KEY, MKTXPConfigKeys.FE_WIRELESS_KEY, MKTXPConfigKeys.FE_WIRELESS_CLIENTS_KEY, + MKTXPConfigKeys.FE_CAPSMAN_KEY, MKTXPConfigKeys.FE_CAPSMAN_CLIENTS_KEY, MKTXPConfigKeys.MKTXP_USE_COMMENTS_OVER_NAMES + ]) + _MKTXPEntry = namedtuple('_MKTXPEntry', [MKTXPConfigKeys.PORT_KEY, MKTXPConfigKeys.MKTXP_SOCKET_TIMEOUT, + MKTXPConfigKeys.MKTXP_INITIAL_DELAY, MKTXPConfigKeys.MKTXP_MAX_DELAY, + MKTXPConfigKeys.MKTXP_INC_DIV, MKTXPConfigKeys.MKTXP_BANDWIDTH_TEST_INTERVAL]) class OSConfig(metaclass = ABCMeta): - ''' OS-related config - ''' - @staticmethod - def os_config(quiet = False): - ''' Factory method - ''' - if sys.platform == 'linux': - return LinuxConfig() - elif sys.platform == 'darwin': - return OSXConfig() - else: - if not quiet: - print(f'Non-supported platform: {sys.platform}') - return None + ''' OS-related config + ''' + @staticmethod + def os_config(quiet = False): + ''' Factory method + ''' + if sys.platform == 'linux': + return LinuxConfig() + elif sys.platform == 'darwin': + return OSXConfig() + else: + if not quiet: + print(f'Non-supported platform: {sys.platform}') + return None - @property - @abstractmethod - def mktxp_user_dir_path(self): - pass + @property + @abstractmethod + def mktxp_user_dir_path(self): + pass class OSXConfig(OSConfig): - ''' OSX-related config - ''' - @property - def mktxp_user_dir_path(self): - return FSHelper.full_path('~/mktxp') + ''' OSX-related config + ''' + @property + def mktxp_user_dir_path(self): + return FSHelper.full_path('~/mktxp') class LinuxConfig(OSConfig): - ''' Linux-related config - ''' - @property - def mktxp_user_dir_path(self): - return FSHelper.full_path('~/mktxp') - #return FSHelper.full_path('/etc/mktxp') + ''' Linux-related config + ''' + @property + def mktxp_user_dir_path(self): + return FSHelper.full_path('~/mktxp') + #return FSHelper.full_path('/etc/mktxp') class MKTXPConfigHandler: - def __init__(self): - self.os_config = OSConfig.os_config() - if not self.os_config: - sys.exit(1) + def __init__(self): + self.os_config = OSConfig.os_config() + if not self.os_config: + sys.exit(1) - # mktxp user config folder - if not os.path.exists(self.os_config.mktxp_user_dir_path): - os.makedirs(self.os_config.mktxp_user_dir_path) + # mktxp user config folder + if not os.path.exists(self.os_config.mktxp_user_dir_path): + os.makedirs(self.os_config.mktxp_user_dir_path) - # if needed, stage the user config data - self.usr_conf_data_path = os.path.join(self.os_config.mktxp_user_dir_path, 'mktxp.conf') - self.mktxp_conf_path = os.path.join(self.os_config.mktxp_user_dir_path, '_mktxp.conf') + # if needed, stage the user config data + self.usr_conf_data_path = os.path.join(self.os_config.mktxp_user_dir_path, 'mktxp.conf') + self.mktxp_conf_path = os.path.join(self.os_config.mktxp_user_dir_path, '_mktxp.conf') - self._create_os_path(self.usr_conf_data_path, 'mktxp/cli/config/mktxp.conf') - self._create_os_path(self.mktxp_conf_path, 'mktxp/cli/config/_mktxp.conf') + self._create_os_path(self.usr_conf_data_path, 'mktxp/cli/config/mktxp.conf') + self._create_os_path(self.mktxp_conf_path, 'mktxp/cli/config/_mktxp.conf') - self.re_compiled = {} + self.re_compiled = {} + + self._read_from_disk() - self._read_from_disk() + def _read_from_disk(self): + ''' (Force-)Read conf data from disk + ''' + self.config = ConfigObj(self.usr_conf_data_path) + self._config = ConfigObj(self.mktxp_conf_path) - def _read_from_disk(self): - ''' (Force-)Read conf data from disk - ''' - self.config = ConfigObj(self.usr_conf_data_path) - self._config = ConfigObj(self.mktxp_conf_path) - - def _create_os_path(self, os_path, resource_path): - if not os.path.exists(os_path): - # stage from the conf templates - lookup_path = resource_filename(Requirement.parse("mktxp"), resource_path) - shutil.copy(lookup_path, os_path) + def _create_os_path(self, os_path, resource_path): + if not os.path.exists(os_path): + # stage from the conf templates + lookup_path = resource_filename(Requirement.parse("mktxp"), resource_path) + shutil.copy(lookup_path, os_path) - # MKTXP entries - ############## - def register_entry(self, entry_name, entry_args, quiet = False): - ''' Registers MKTXP conf entry - ''' - if entry_name in self.registered_entries(): - if not quiet: - print('"{0}": entry name already registered'.format(entry_name)) - return False - else: - self.config[entry_name] = entry_args - self.config.write() - if not quiet: - print('Entry registered: {0}'.format(entry_name)) - return True + # MKTXP entries + ############## + def register_entry(self, entry_name, entry_args, quiet = False): + ''' Registers MKTXP conf entry + ''' + if entry_name in self.registered_entries(): + if not quiet: + print('"{0}": entry name already registered'.format(entry_name)) + return False + else: + self.config[entry_name] = entry_args + self.config.write() + if not quiet: + print('Entry registered: {0}'.format(entry_name)) + return True - def unregister_entry(self, entry_name, quiet = False): - ''' Un-registers MKTXP conf entry - ''' - if self.config[entry_name]: - del(self.config[entry_name]) - self.config.write() - if not quiet: - print('Unregistered entry: {}'.format(entry_name)) - return True - else: - if not quiet: - print('Entry is not registered: {}'.format(entry_name)) - return False + def unregister_entry(self, entry_name, quiet = False): + ''' Un-registers MKTXP conf entry + ''' + if self.config[entry_name]: + del(self.config[entry_name]) + self.config.write() + if not quiet: + print('Unregistered entry: {}'.format(entry_name)) + return True + else: + if not quiet: + print('Entry is not registered: {}'.format(entry_name)) + return False - def registered_entries(self): - ''' All MKTXP registered entries - ''' - registered_entries = [entry_name for entry_name in self.config.keys()] - if not registered_entries: - registered_entries = [MKTXPConfigKeys.NO_ENTRIES_REGISTERED] + def registered_entries(self): + ''' All MKTXP registered entries + ''' + registered_entries = [entry_name for entry_name in self.config.keys()] + if not registered_entries: + registered_entries = [MKTXPConfigKeys.NO_ENTRIES_REGISTERED] - return registered_entries + return registered_entries - def entry(self, entry_name): - ''' Given an entry name, reads and returns the entry info - ''' - entry_reader = self.entry_reader(entry_name) - return ConfigEntry.MKTXPEntry(**entry_reader) + def entry(self, entry_name): + ''' Given an entry name, reads and returns the entry info + ''' + entry_reader = self.entry_reader(entry_name) + return ConfigEntry.MKTXPEntry(**entry_reader) - def _entry(self): - ''' MKTXP internal config entry - ''' - _entry_reader = self._entry_reader() - return ConfigEntry._MKTXPEntry(**_entry_reader) + def _entry(self): + ''' MKTXP internal config entry + ''' + _entry_reader = self._entry_reader() + return ConfigEntry._MKTXPEntry(**_entry_reader) - # Helpers - def entry_reader(self, entry_name): - entry_reader = {} - write_needed = False - for key in MKTXPConfigKeys.BOOLEAN_KEYS: - if self.config[entry_name].get(key): - entry_reader[key] = self.config[entry_name].as_bool(key) - else: - entry_reader[key] = False - write_needed = True # read from disk next time + # Helpers + def entry_reader(self, entry_name): + entry_reader = {} + write_needed = False + for key in MKTXPConfigKeys.BOOLEAN_KEYS: + if self.config[entry_name].get(key): + entry_reader[key] = self.config[entry_name].as_bool(key) + else: + entry_reader[key] = False + write_needed = True # read from disk next time - for key in MKTXPConfigKeys.STR_KEYS: - entry_reader[key] = self.config[entry_name][key] + for key in MKTXPConfigKeys.STR_KEYS: + entry_reader[key] = self.config[entry_name][key] - # port - if self.config[entry_name].get(MKTXPConfigKeys.PORT_KEY): - entry_reader[MKTXPConfigKeys.PORT_KEY] = self.config[entry_name].as_int(MKTXPConfigKeys.PORT_KEY) - else: - entry_reader[MKTXPConfigKeys.PORT_KEY] = self._default_value_for_key(MKTXPConfigKeys.SSL_KEY, entry_reader[MKTXPConfigKeys.SSL_KEY]) - write_needed = True # read from disk next time + # port + if self.config[entry_name].get(MKTXPConfigKeys.PORT_KEY): + entry_reader[MKTXPConfigKeys.PORT_KEY] = self.config[entry_name].as_int(MKTXPConfigKeys.PORT_KEY) + else: + entry_reader[MKTXPConfigKeys.PORT_KEY] = self._default_value_for_key(MKTXPConfigKeys.SSL_KEY, entry_reader[MKTXPConfigKeys.SSL_KEY]) + write_needed = True # read from disk next time - if write_needed: - self.config[entry_name] = entry_reader - self.config.write() + if write_needed: + self.config[entry_name] = entry_reader + self.config.write() - return entry_reader + return entry_reader - def _entry_reader(self): - _entry_reader = {} - entry_name = MKTXPConfigKeys.MKTXP_CONFIG_ENTRY_NAME - write_needed = False - for key in MKTXPConfigKeys.MKTXP_INT_KEYS: - if self._config[entry_name].get(key): - _entry_reader[key] = self._config[entry_name].as_int(key) - else: - _entry_reader[key] = self._default_value_for_key(key) - write_needed = True # read from disk next time - - if write_needed: - self._config[entry_name] = _entry_reader - self._config.write() - - return _entry_reader + def _entry_reader(self): + _entry_reader = {} + entry_name = MKTXPConfigKeys.MKTXP_CONFIG_ENTRY_NAME + write_needed = False + for key in MKTXPConfigKeys.MKTXP_INT_KEYS: + if self._config[entry_name].get(key): + _entry_reader[key] = self._config[entry_name].as_int(key) + else: + _entry_reader[key] = self._default_value_for_key(key) + write_needed = True # read from disk next time + + if write_needed: + self._config[entry_name] = _entry_reader + self._config.write() + + return _entry_reader - def _default_value_for_key(self, key, value = None): - return { - MKTXPConfigKeys.SSL_KEY: lambda value: MKTXPConfigKeys.DEFAULT_API_SSL_PORT if value else MKTXPConfigKeys.DEFAULT_API_PORT, - MKTXPConfigKeys.PORT_KEY: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_PORT, - MKTXPConfigKeys.MKTXP_SOCKET_TIMEOUT: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_SOCKET_TIMEOUT, - MKTXPConfigKeys.MKTXP_INITIAL_DELAY: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_INITIAL_DELAY, - MKTXPConfigKeys.MKTXP_MAX_DELAY: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_MAX_DELAY, - MKTXPConfigKeys.MKTXP_INC_DIV: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_INC_DIV, - MKTXPConfigKeys.MKTXP_BANDWIDTH_TEST_INTERVAL: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_BANDWIDTH_TEST_INTERVAL - }[key](value) + def _default_value_for_key(self, key, value = None): + return { + MKTXPConfigKeys.SSL_KEY: lambda value: MKTXPConfigKeys.DEFAULT_API_SSL_PORT if value else MKTXPConfigKeys.DEFAULT_API_PORT, + MKTXPConfigKeys.PORT_KEY: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_PORT, + MKTXPConfigKeys.MKTXP_SOCKET_TIMEOUT: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_SOCKET_TIMEOUT, + MKTXPConfigKeys.MKTXP_INITIAL_DELAY: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_INITIAL_DELAY, + MKTXPConfigKeys.MKTXP_MAX_DELAY: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_MAX_DELAY, + MKTXPConfigKeys.MKTXP_INC_DIV: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_INC_DIV, + MKTXPConfigKeys.MKTXP_BANDWIDTH_TEST_INTERVAL: lambda value: MKTXPConfigKeys.DEFAULT_MKTXP_BANDWIDTH_TEST_INTERVAL + }[key](value) # Simplest possible Singleton impl diff --git a/mktxp/cli/dispatch.py b/mktxp/cli/dispatch.py index e246e0e..42f8cad 100755 --- a/mktxp/cli/dispatch.py +++ b/mktxp/cli/dispatch.py @@ -12,6 +12,7 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + import sys import subprocess import mktxp.cli.checks.chk_pv @@ -20,6 +21,7 @@ from mktxp.cli.options import MKTXPOptionsParser, MKTXPCommands from mktxp.cli.config.config import config_handler, ConfigEntry from mktxp.basep import MKTXPProcessor, MKTXPCLIProcessor + class MKTXPDispatcher: ''' Base MKTXP Commands Dispatcher ''' @@ -92,7 +94,7 @@ class MKTXPDispatcher: def print(self, args): if not (args['wifi_clients'] or args['capsman_clients']): - print("Select metric option(s) to print out, or run 'mktxp print' -h to find out more") + print("Select metric option(s) to print out, or run 'mktxp print -h' to find out more") if args['wifi_clients']: MKTXPCLIProcessor.wifi_clients(args['entry_name']) @@ -100,6 +102,10 @@ class MKTXPDispatcher: if args['capsman_clients']: MKTXPCLIProcessor.capsman_clients(args['entry_name']) + if args['dhcp_clients']: + MKTXPCLIProcessor.dhcp_clients(args['entry_name']) + + def main(): MKTXPDispatcher().dispatch() diff --git a/mktxp/cli/options.py b/mktxp/cli/options.py index 97c8033..373ca3d 100755 --- a/mktxp/cli/options.py +++ b/mktxp/cli/options.py @@ -11,6 +11,7 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + import os import pkg_resources from argparse import ArgumentParser, HelpFormatter @@ -132,6 +133,9 @@ Selected metrics info can be printed on the command line. For more information, help = "WiFi clients metrics", action = 'store_true') + optional_args_group.add_argument('-dc', '--dhcp_clients', dest='dhcp_clients', + help = "DHCP clients metrics", + action = 'store_true') # Options checking def _check_args(self, args, parser): diff --git a/mktxp/cli/output/base_out.py b/mktxp/cli/output/base_out.py index 8993e7a..7454155 100644 --- a/mktxp/cli/output/base_out.py +++ b/mktxp/cli/output/base_out.py @@ -21,16 +21,17 @@ from mktxp.cli.config.config import config_handler class BaseOutputProcessor: OutputCapsmanEntry = namedtuple('OutputCapsmanEntry', ['dhcp_name', 'dhcp_address', 'mac_address', 'rx_signal', 'interface', 'ssid', 'tx_rate', 'rx_rate', 'uptime']) OutputWiFiEntry = namedtuple('OutputWiFiEntry', ['dhcp_name', 'dhcp_address', 'mac_address', 'signal_strength', 'signal_to_noise', 'interface', 'tx_rate', 'rx_rate', 'uptime']) + OutputDHCPEntry = namedtuple('OutputDHCPEntry', ['host_name', 'comment', 'address', 'active_address', 'mac_address', 'server', 'expires_after']) @staticmethod - def augment_record(router_metric, registration_record, dhcp_lease_records): + def augment_record(router_entry, registration_record, dhcp_lease_records): try: dhcp_lease_record = next((dhcp_lease_record for dhcp_lease_record in dhcp_lease_records if dhcp_lease_record['mac_address']==registration_record['mac_address'])) dhcp_name = dhcp_lease_record.get('host_name') dhcp_comment = dhcp_lease_record.get('comment') if dhcp_name and dhcp_comment: - dhcp_name = f'{dhcp_name[0:20]} ({dhcp_comment[0:20]})' if not router_metric.router_entry.use_comments_over_names else dhcp_comment + dhcp_name = f'{dhcp_name[0:20]} ({dhcp_comment[0:20]})' if not router_entry.config_entry.use_comments_over_names else dhcp_comment elif dhcp_comment: dhcp_name = dhcp_comment else: @@ -49,9 +50,12 @@ class BaseOutputProcessor: registration_record['rx_bytes'] = registration_record['bytes'].split(',')[1] del registration_record['bytes'] - registration_record['tx_rate'] = BaseOutputProcessor.parse_rates(registration_record['tx_rate']) - registration_record['rx_rate'] = BaseOutputProcessor.parse_rates(registration_record['rx_rate']) - registration_record['uptime'] = naturaldelta(BaseOutputProcessor.parse_timedelta_seconds(registration_record['uptime']), months=True, minimum_unit='seconds', when=None) + if registration_record.get('tx_rate'): + registration_record['tx_rate'] = BaseOutputProcessor.parse_rates(registration_record['tx_rate']) + if registration_record.get('rx_rate'): + registration_record['rx_rate'] = BaseOutputProcessor.parse_rates(registration_record['rx_rate']) + if registration_record.get('uptime'): + registration_record['uptime'] = naturaldelta(BaseOutputProcessor.parse_timedelta_seconds(registration_record['uptime']), months=True, minimum_unit='seconds', when=None) if registration_record.get('signal_strength'): registration_record['signal_strength'] = BaseOutputProcessor.parse_signal_strength(registration_record['signal_strength']) diff --git a/mktxp/cli/output/capsman_out.py b/mktxp/cli/output/capsman_out.py index 33da805..8f6186f 100644 --- a/mktxp/cli/output/capsman_out.py +++ b/mktxp/cli/output/capsman_out.py @@ -11,27 +11,31 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from tabulate import tabulate from mktxp.cli.output.base_out import BaseOutputProcessor +from mktxp.datasources.dhcp_ds import DHCPMetricsDataSource +from mktxp.datasources.capsman_ds import CapsmanRegistrationsMetricsDataSource + class CapsmanOutput: ''' CAPsMAN CLI Output ''' @staticmethod - def clients_summary(router_metric): + def clients_summary(router_entry): registration_labels = ['interface', 'ssid', 'mac_address', 'rx_signal', 'uptime', 'tx_rate', 'rx_rate'] - registration_records = router_metric.capsman_registration_table_records(registration_labels, False) + registration_records = CapsmanRegistrationsMetricsDataSource.metric_records(router_entry, metric_labels = registration_labels, add_router_id = False) if not registration_records: print('No CAPsMAN registration records') return # translate / trim / augment registration records dhcp_lease_labels = ['host_name', 'comment', 'address', 'mac_address'] - dhcp_lease_records = router_metric.dhcp_lease_records(dhcp_lease_labels, False) + dhcp_lease_records = DHCPMetricsDataSource.metric_records(router_entry, metric_labels = dhcp_lease_labels, add_router_id = False) dhcp_rt_by_interface = {} for registration_record in sorted(registration_records, key = lambda rt_record: rt_record['rx_signal'], reverse=True): - BaseOutputProcessor.augment_record(router_metric, registration_record, dhcp_lease_records) + BaseOutputProcessor.augment_record(router_entry, registration_record, dhcp_lease_records) interface = registration_record['interface'] if interface in dhcp_rt_by_interface.keys(): diff --git a/mktxp/cli/output/dhcp_out.py b/mktxp/cli/output/dhcp_out.py new file mode 100644 index 0000000..5661010 --- /dev/null +++ b/mktxp/cli/output/dhcp_out.py @@ -0,0 +1,48 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from tabulate import tabulate +from mktxp.cli.output.base_out import BaseOutputProcessor +from mktxp.datasources.dhcp_ds import DHCPMetricsDataSource + + +class DHCPOutput: + ''' DHCP Clients CLI Output + ''' + @staticmethod + def clients_summary(router_entry): + dhcp_lease_labels = ['host_name', 'comment', 'active_address', 'address', 'mac_address', 'server', 'expires_after'] + dhcp_lease_records = DHCPMetricsDataSource.metric_records(router_entry, metric_labels = dhcp_lease_labels, add_router_id = False) + if not dhcp_lease_records: + print('No DHCP registration records') + return + + dhcp_by_server = {} + for dhcp_lease_record in sorted(dhcp_lease_records, key = lambda dhcp_record: dhcp_record['active_address'], reverse=True): + server = dhcp_lease_record['server'] + if server in dhcp_by_server.keys(): + dhcp_by_server[server].append(dhcp_lease_record) + else: + dhcp_by_server[server] = [dhcp_lease_record] + + num_records = 0 + output_table = [] + for key in dhcp_by_server.keys(): + for record in dhcp_by_server[key]: + output_table.append(BaseOutputProcessor.OutputDHCPEntry(**record)) + num_records += 1 + output_table.append({}) + print() + print(tabulate(output_table, headers = "keys", tablefmt="github")) + print(tabulate([{0:'DHCP Clients:', 1:num_records}], tablefmt="text")) diff --git a/mktxp/cli/output/wifi_out.py b/mktxp/cli/output/wifi_out.py index f7faf2f..7387a62 100644 --- a/mktxp/cli/output/wifi_out.py +++ b/mktxp/cli/output/wifi_out.py @@ -11,27 +11,31 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from tabulate import tabulate from mktxp.cli.output.base_out import BaseOutputProcessor +from mktxp.datasources.dhcp_ds import DHCPMetricsDataSource +from mktxp.datasources.wireless_ds import WirelessMetricsDataSource + class WirelessOutput: ''' Wireless Clients CLI Output ''' @staticmethod - def clients_summary(router_metric): + def clients_summary(router_entry): registration_labels = ['interface', 'mac_address', 'signal_strength', 'uptime', 'tx_rate', 'rx_rate', 'signal_to_noise'] - registration_records = router_metric.wireless_registration_table_records(registration_labels, False) + registration_records = WirelessMetricsDataSource.metric_records(router_entry, metric_labels = registration_labels, add_router_id = False) if not registration_records: print('No wireless registration records') return # translate / trim / augment registration records dhcp_lease_labels = ['host_name', 'comment', 'address', 'mac_address'] - dhcp_lease_records = router_metric.dhcp_lease_records(dhcp_lease_labels, False) + dhcp_lease_records = DHCPMetricsDataSource.metric_records(router_entry, metric_labels = dhcp_lease_labels, add_router_id = False) dhcp_rt_by_interface = {} for registration_record in sorted(registration_records, key = lambda rt_record: rt_record['signal_strength'], reverse=True): - BaseOutputProcessor.augment_record(router_metric, registration_record, dhcp_lease_records) + BaseOutputProcessor.augment_record(router_entry, registration_record, dhcp_lease_records) interface = registration_record['interface'] if interface in dhcp_rt_by_interface.keys(): diff --git a/mktxp/collectors/bandwidth_collector.py b/mktxp/collectors/bandwidth_collector.py index 579402b..2cdb7c6 100644 --- a/mktxp/collectors/bandwidth_collector.py +++ b/mktxp/collectors/bandwidth_collector.py @@ -12,16 +12,19 @@ ## GNU General Public License for more details. +import socket import speedtest from datetime import datetime from multiprocessing import Pool from mktxp.cli.config.config import config_handler from mktxp.collectors.base_collector import BaseCollector + result_list = [{'download': 0, 'upload': 0, 'ping': 0}] def get_result(bandwidth_dict): result_list[0] = bandwidth_dict + class BandwidthCollector(BaseCollector): ''' MKTXP collector ''' @@ -53,8 +56,21 @@ class BandwidthCollector(BaseCollector): @staticmethod def bandwidth_worker(): - bandwidth_test = speedtest.Speedtest() - bandwidth_test.get_best_server() - bandwidth_test.download() - bandwidth_test.upload() - return bandwidth_test.results.dict() + if BandwidthCollector.inet_connected(): + bandwidth_test = speedtest.Speedtest() + bandwidth_test.get_best_server() + bandwidth_test.download() + bandwidth_test.upload() + return bandwidth_test.results.dict() + else: + return {'download': 0, 'upload': 0, 'ping': 0} + + @staticmethod + def inet_connected(host="8.8.8.8", port=53, timeout=3): + try: + socket.setdefaulttimeout(timeout) + socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port)) + return True + except socket.error as exc: + return False + diff --git a/mktxp/collectors/base_collector.py b/mktxp/collectors/base_collector.py index 5284c11..9c01a8a 100644 --- a/mktxp/collectors/base_collector.py +++ b/mktxp/collectors/base_collector.py @@ -15,6 +15,7 @@ from prometheus_client.core import GaugeMetricFamily, CounterMetricFamily, InfoMetricFamily from mktxp.cli.config.config import MKTXPConfigKeys + class BaseCollector: ''' Base Collector methods For use by custom collectors diff --git a/mktxp/collectors/capsman_collector.py b/mktxp/collectors/capsman_collector.py index b038fbd..865090b 100644 --- a/mktxp/collectors/capsman_collector.py +++ b/mktxp/collectors/capsman_collector.py @@ -11,43 +11,47 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.cli.output.base_out import BaseOutputProcessor from mktxp.cli.config.config import MKTXPConfigKeys from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.dhcp_ds import DHCPMetricsDataSource +from mktxp.datasources.capsman_ds import CapsmanCapsMetricsDataSource, CapsmanRegistrationsMetricsDataSource + class CapsmanCollector(BaseCollector): ''' CAPsMAN Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): remote_caps_labels = ['identity', 'version', 'base_mac', 'board', 'base_mac'] - remote_caps_records = router_metric.capsman_remote_caps_records(remote_caps_labels) + remote_caps_records = CapsmanCapsMetricsDataSource.metric_records(router_entry, metric_labels = remote_caps_labels) if remote_caps_records: remote_caps_metrics = BaseCollector.info_collector('capsman_remote_caps', 'CAPsMAN remote caps', remote_caps_records, remote_caps_labels) yield remote_caps_metrics registration_labels = ['interface', 'ssid', 'mac_address', 'tx_rate', 'rx_rate', 'rx_signal', 'uptime', 'bytes'] - registration_records = router_metric.capsman_registration_table_records(registration_labels) + registration_records = CapsmanRegistrationsMetricsDataSource.metric_records(router_entry, metric_labels = registration_labels) if registration_records: # calculate number of registrations per interface registration_per_interface = {} for registration_record in registration_records: registration_per_interface[registration_record['interface']] = registration_per_interface.get(registration_record['interface'], 0) + 1 # compile registrations-per-interface records - registration_per_interface_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], - MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], + registration_per_interface_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], + MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], 'interface': key, 'count': value} for key, value in registration_per_interface.items()] # yield registrations-per-interface metrics registration_per_interface_metrics = BaseCollector.gauge_collector('capsman_registrations_count', 'Number of active registration per CAPsMAN interface', registration_per_interface_records, 'count', ['interface']) yield registration_per_interface_metrics # the client info metrics - if router_metric.router_entry.capsman_clients: + if router_entry.config_entry.capsman_clients: # translate / trim / augment registration records dhcp_lease_labels = ['mac_address', 'address', 'host_name', 'comment'] - dhcp_lease_records = router_metric.dhcp_lease_records(dhcp_lease_labels) + dhcp_lease_records = DHCPMetricsDataSource.metric_records(router_entry, metric_labels = dhcp_lease_labels) for registration_record in registration_records: - BaseOutputProcessor.augment_record(router_metric, registration_record, dhcp_lease_records) + BaseOutputProcessor.augment_record(router_entry, registration_record, dhcp_lease_records) tx_byte_metrics = BaseCollector.counter_collector('capsman_clients_tx_bytes', 'Number of sent packet bytes', registration_records, 'tx_bytes', ['dhcp_name']) yield tx_byte_metrics diff --git a/mktxp/collectors/dhcp_collector.py b/mktxp/collectors/dhcp_collector.py index 7908aab..c6685bb 100644 --- a/mktxp/collectors/dhcp_collector.py +++ b/mktxp/collectors/dhcp_collector.py @@ -11,17 +11,19 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.cli.config.config import MKTXPConfigKeys from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.dhcp_ds import DHCPMetricsDataSource class DHCPCollector(BaseCollector): ''' DHCP Metrics collector ''' @staticmethod - def collect(router_metric): - dhcp_lease_labels = ['active_address', 'mac_address', 'host_name', 'comment', 'server', 'expires_after'] - dhcp_lease_records = router_metric.dhcp_lease_records(dhcp_lease_labels) + def collect(router_entry): + dhcp_lease_labels = ['active_address', 'address', 'mac_address', 'host_name', 'comment', 'server', 'expires_after'] + dhcp_lease_records = DHCPMetricsDataSource.metric_records(router_entry, metric_labels = dhcp_lease_labels) if dhcp_lease_records: # calculate number of leases per DHCP server dhcp_lease_servers = {} @@ -29,8 +31,8 @@ class DHCPCollector(BaseCollector): dhcp_lease_servers[dhcp_lease_record['server']] = dhcp_lease_servers.get(dhcp_lease_record['server'], 0) + 1 # compile leases-per-server records - dhcp_lease_servers_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], - MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], + dhcp_lease_servers_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], + MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], 'server': key, 'count': value} for key, value in dhcp_lease_servers.items()] # yield lease-per-server metrics @@ -38,7 +40,7 @@ class DHCPCollector(BaseCollector): yield dhcp_lease_server_metrics # active lease metrics - if router_metric.router_entry.dhcp_lease: + if router_entry.config_entry.dhcp_lease: dhcp_lease_metrics = BaseCollector.info_collector('dhcp_lease', 'DHCP Active Leases', dhcp_lease_records, dhcp_lease_labels) yield dhcp_lease_metrics \ No newline at end of file diff --git a/mktxp/collectors/firewall_collector.py b/mktxp/collectors/firewall_collector.py index 68678a6..8fbecdb 100644 --- a/mktxp/collectors/firewall_collector.py +++ b/mktxp/collectors/firewall_collector.py @@ -11,34 +11,37 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. -from mktxp.collectors.base_collector import BaseCollector + from mktxp.cli.config.config import MKTXPConfigKeys +from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.firewall_ds import FirewallMetricsDataSource + class FirewallCollector(BaseCollector): ''' Firewall rules traffic metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): # initialize all pool counts, including those currently not used firewall_labels = ['chain', 'action', 'bytes', 'comment'] - firewall_filter_records = router_metric.firewall_records(firewall_labels) + firewall_filter_records = FirewallMetricsDataSource.metric_records(router_entry, metric_labels = firewall_labels) if firewall_filter_records: - metris_records = [FirewallCollector.metric_record(router_metric, record) for record in firewall_filter_records] + metris_records = [FirewallCollector.metric_record(router_entry, record) for record in firewall_filter_records] firewall_filter_metrics = BaseCollector.counter_collector('firewall_filter', 'Total amount of bytes matched by firewall rules', metris_records, 'bytes', ['name']) yield firewall_filter_metrics - firewall_raw_records = router_metric.firewall_records(firewall_labels, raw = True) + firewall_raw_records = FirewallMetricsDataSource.metric_records(router_entry, metric_labels = firewall_labels, raw = True) if firewall_raw_records: - metris_records = [FirewallCollector.metric_record(router_metric, record) for record in firewall_raw_records] + metris_records = [FirewallCollector.metric_record(router_entry, record) for record in firewall_raw_records] firewall_raw_metrics = BaseCollector.counter_collector('firewall_raw', 'Total amount of bytes matched by raw firewall rules', metris_records, 'bytes', ['name']) yield firewall_raw_metrics # Helpers @staticmethod - def metric_record(router_metric, firewall_record): + def metric_record(router_entry, firewall_record): name = f"| {firewall_record['chain']} | {firewall_record['action']} | {firewall_record['comment']}" bytes = firewall_record['bytes'] - return {MKTXPConfigKeys.ROUTERBOARD_NAME: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], - MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], + return {MKTXPConfigKeys.ROUTERBOARD_NAME: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], + MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], 'name': name, 'bytes': bytes} diff --git a/mktxp/collectors/health_collector.py b/mktxp/collectors/health_collector.py index 2f8bb70..0e6e291 100644 --- a/mktxp/collectors/health_collector.py +++ b/mktxp/collectors/health_collector.py @@ -11,16 +11,18 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.health_ds import HealthMetricsDataSource class HealthCollector(BaseCollector): ''' System Health Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): health_labels = ['voltage', 'temperature'] - health_records = router_metric.health_records(health_labels) + health_records = HealthMetricsDataSource.metric_records(router_entry, metric_labels = health_labels) if health_records: voltage_metrics = BaseCollector.gauge_collector('system_routerboard_voltage', 'Supplied routerboard voltage', health_records, 'voltage') yield voltage_metrics diff --git a/mktxp/collectors/identity_collector.py b/mktxp/collectors/identity_collector.py index 50e5404..80fd623 100644 --- a/mktxp/collectors/identity_collector.py +++ b/mktxp/collectors/identity_collector.py @@ -11,15 +11,18 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.identity_ds import IdentityMetricsDataSource + class IdentityCollector(BaseCollector): ''' System Identity Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): identity_labels = ['name'] - identity_records = router_metric.identity_records(identity_labels) + identity_records = IdentityMetricsDataSource.metric_records(router_entry, metric_labels = identity_labels) if identity_records: identity_metrics = BaseCollector.info_collector('system_identity', 'System identity', identity_records, identity_labels) yield identity_metrics diff --git a/mktxp/collectors/interface_collector.py b/mktxp/collectors/interface_collector.py index 4abbc07..f7e98a6 100644 --- a/mktxp/collectors/interface_collector.py +++ b/mktxp/collectors/interface_collector.py @@ -11,20 +11,23 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.interface_ds import InterfaceTrafficMetricsDataSource + class InterfaceCollector(BaseCollector): ''' Router Interface Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): interface_traffic_labels = ['name', 'comment', 'rx_byte', 'tx_byte', 'rx_packet', 'tx_packet', 'rx_error', 'tx_error', 'rx_drop', 'tx_drop'] - interface_traffic_records = router_metric.interface_traffic_records(interface_traffic_labels) - + interface_traffic_records = InterfaceTrafficMetricsDataSource.metric_records(router_entry, metric_labels = interface_traffic_labels) + if interface_traffic_records: for interface_traffic_record in interface_traffic_records: if interface_traffic_record.get('comment'): - interface_traffic_record['name'] = interface_traffic_record['comment'] if router_metric.router_entry.use_comments_over_names \ + interface_traffic_record['name'] = interface_traffic_record['comment'] if router_entry.config_entry.use_comments_over_names \ else f"{interface_traffic_record['name']} ({interface_traffic_record['comment']})" rx_byte_metric = BaseCollector.counter_collector('interface_rx_byte', 'Number of received bytes', interface_traffic_records, 'rx_byte', ['name']) diff --git a/mktxp/collectors/mktxp_collector.py b/mktxp/collectors/mktxp_collector.py index bace25b..edce76a 100644 --- a/mktxp/collectors/mktxp_collector.py +++ b/mktxp/collectors/mktxp_collector.py @@ -11,14 +11,17 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.mktxp_ds import MKTXPMetricsDataSource + class MKTXPCollector(BaseCollector): ''' System Identity Metrics collector ''' @staticmethod - def collect(router_metric): - mktxp_records = router_metric.mktxp_records() + def collect(router_entry): + mktxp_records = MKTXPMetricsDataSource.metric_records(router_entry) if mktxp_records: mktxp_duration_metric = BaseCollector.counter_collector('collection_time', 'Total time spent collecting metrics in milliseconds', mktxp_records, 'duration', ['name']) yield mktxp_duration_metric diff --git a/mktxp/collectors/monitor_collector.py b/mktxp/collectors/monitor_collector.py index 85ff534..46e42af 100644 --- a/mktxp/collectors/monitor_collector.py +++ b/mktxp/collectors/monitor_collector.py @@ -11,16 +11,19 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.collectors.base_collector import BaseCollector from mktxp.cli.output.base_out import BaseOutputProcessor +from mktxp.datasources.interface_ds import InterfaceMonitorMetricsDataSource + class MonitorCollector(BaseCollector): ''' Ethernet Interface Monitor Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): monitor_labels = ('status', 'rate', 'full_duplex', 'name') - monitor_records = router_metric.interface_monitor_records(monitor_labels, include_comments = True) + monitor_records = InterfaceMonitorMetricsDataSource.metric_records(router_entry, metric_labels = monitor_labels, include_comments = True) if monitor_records: # translate records to appropriate values for monitor_record in monitor_records: diff --git a/mktxp/collectors/pool_collector.py b/mktxp/collectors/pool_collector.py index 1d23df3..12085a5 100644 --- a/mktxp/collectors/pool_collector.py +++ b/mktxp/collectors/pool_collector.py @@ -11,28 +11,31 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.cli.config.config import MKTXPConfigKeys from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.pool_ds import PoolMetricsDataSource, PoolUsedMetricsDataSource + class PoolCollector(BaseCollector): ''' IP Pool Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): # initialize all pool counts, including those currently not used - pool_records = router_metric.pool_records(['name']) + pool_records = PoolMetricsDataSource.metric_records(router_entry, metric_labels = ['name']) if pool_records: pool_used_labels = ['pool'] pool_used_counts = {pool_record['name']: 0 for pool_record in pool_records} # for pools in usage, calculate the current numbers - pool_used_records = router_metric.pool_used_records(pool_used_labels) + pool_used_records = PoolUsedMetricsDataSource.metric_records(router_entry, metric_labels = pool_used_labels) for pool_used_record in pool_used_records: pool_used_counts[pool_used_record['pool']] = pool_used_counts.get(pool_used_record['pool'], 0) + 1 # compile used-per-pool records - used_per_pool_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], - MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], + used_per_pool_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], + MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], 'pool': key, 'count': value} for key, value in pool_used_counts.items()] # yield used-per-pool metrics diff --git a/mktxp/collectors/resource_collector.py b/mktxp/collectors/resource_collector.py index 744e38a..d815f4c 100644 --- a/mktxp/collectors/resource_collector.py +++ b/mktxp/collectors/resource_collector.py @@ -11,20 +11,23 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.collectors.base_collector import BaseCollector from mktxp.cli.output.base_out import BaseOutputProcessor +from mktxp.datasources.system_resource_ds import SystemResourceMetricsDataSource + class SystemResourceCollector(BaseCollector): ''' System Resource Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): resource_labels = ['uptime', 'version', 'free_memory', 'total_memory', 'cpu', 'cpu_count', 'cpu_frequency', 'cpu_load', 'free_hdd_space', 'total_hdd_space', 'architecture_name', 'board_name'] - - resource_records = router_metric.system_resource_records(resource_labels) + + resource_records = SystemResourceMetricsDataSource.metric_records(router_entry, metric_labels = resource_labels) if resource_records: # translate records to appropriate values translated_fields = ['uptime'] diff --git a/mktxp/collectors/route_collector.py b/mktxp/collectors/route_collector.py index 57f909f..37cd4c4 100644 --- a/mktxp/collectors/route_collector.py +++ b/mktxp/collectors/route_collector.py @@ -11,21 +11,24 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.cli.config.config import MKTXPConfigKeys from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.route_ds import RouteMetricsDataSource + class RouteCollector(BaseCollector): ''' IP Route Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): route_labels = ['connect', 'dynamic', 'static', 'bgp', 'ospf'] - route_records = router_metric.route_records(route_labels) + route_records = RouteMetricsDataSource.metric_records(router_entry, metric_labels = route_labels) if route_records: # compile total routes records total_routes = len(route_records) - total_routes_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], - MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], + total_routes_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], + MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], 'count': total_routes }] total_routes_metrics = BaseCollector.gauge_collector('routes_total_routes', 'Overall number of routes in RIB', total_routes_records, 'count') @@ -40,8 +43,8 @@ class RouteCollector(BaseCollector): routes_per_protocol[route_label] += 1 # compile route-per-protocol records - route_per_protocol_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], - MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_metric.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], + route_per_protocol_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME], + MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS], 'protocol': key, 'count': value} for key, value in routes_per_protocol.items()] # yield route-per-protocol metrics diff --git a/mktxp/collectors/wlan_collector.py b/mktxp/collectors/wlan_collector.py index d7a0663..c14e5dc 100644 --- a/mktxp/collectors/wlan_collector.py +++ b/mktxp/collectors/wlan_collector.py @@ -11,16 +11,21 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from mktxp.cli.output.base_out import BaseOutputProcessor from mktxp.collectors.base_collector import BaseCollector +from mktxp.datasources.dhcp_ds import DHCPMetricsDataSource +from mktxp.datasources.wireless_ds import WirelessMetricsDataSource +from mktxp.datasources.interface_ds import InterfaceMonitorMetricsDataSource + class WLANCollector(BaseCollector): ''' Wireless Metrics collector ''' @staticmethod - def collect(router_metric): + def collect(router_entry): monitor_labels = ['channel', 'noise_floor', 'overall_tx_ccq', 'registered_clients'] - monitor_records = router_metric.interface_monitor_records(monitor_labels, 'wireless') + monitor_records = InterfaceMonitorMetricsDataSource.metric_records(router_entry, metric_labels = monitor_labels, kind = 'wireless') if monitor_records: # sanitize records for relevant labels noise_floor_records = [monitor_record for monitor_record in monitor_records if monitor_record.get('noise_floor')] @@ -40,15 +45,15 @@ class WLANCollector(BaseCollector): yield registered_clients_metrics # the client info metrics - if router_metric.router_entry.wireless_clients: + if router_entry.config_entry.wireless_clients: registration_labels = ['interface', 'ssid', 'mac_address', 'tx_rate', 'rx_rate', 'uptime', 'bytes', 'signal_to_noise', 'tx_ccq', 'signal_strength'] - registration_records = router_metric.wireless_registration_table_records(registration_labels) + registration_records = WirelessMetricsDataSource.metric_records(router_entry, metric_labels = registration_labels) if registration_records: dhcp_lease_labels = ['mac_address', 'address', 'host_name', 'comment'] - dhcp_lease_records = router_metric.dhcp_lease_records(dhcp_lease_labels) + dhcp_lease_records = DHCPMetricsDataSource.metric_records(router_entry, metric_labels = dhcp_lease_labels) for registration_record in registration_records: - BaseOutputProcessor.augment_record(router_metric, registration_record, dhcp_lease_records) + BaseOutputProcessor.augment_record(router_entry, registration_record, dhcp_lease_records) tx_byte_metrics = BaseCollector.counter_collector('wlan_clients_tx_bytes', 'Number of sent packet bytes', registration_records, 'tx_bytes', ['dhcp_name']) yield tx_byte_metrics diff --git a/mktxp/collectors_handler.py b/mktxp/collectors_handler.py index 36e10b5..ea48892 100644 --- a/mktxp/collectors_handler.py +++ b/mktxp/collectors_handler.py @@ -11,6 +11,7 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + from timeit import default_timer from mktxp.collectors.dhcp_collector import DHCPCollector from mktxp.collectors.interface_collector import InterfaceCollector @@ -30,73 +31,73 @@ from mktxp.collectors.mktxp_collector import MKTXPCollector class CollectorsHandler: ''' MKTXP Collectors Handler ''' - def __init__(self, metrics_handler): - self.metrics_handler = metrics_handler + def __init__(self, entries_handler): + self.entries_handler = entries_handler self.bandwidthCollector = BandwidthCollector() def collect(self): # process mktxp internal metrics yield from self.bandwidthCollector.collect() - for router_metric in self.metrics_handler.router_metrics: - if not router_metric.api_connection.is_connected(): + for router_entry in self.entries_handler.router_entries: + if not router_entry.api_connection.is_connected(): # let's pick up on things in the next run - router_metric.api_connection.connect() + router_entry.api_connection.connect() continue start = default_timer() - yield from IdentityCollector.collect(router_metric) - router_metric.time_spent['IdentityCollector'] += default_timer() - start + yield from IdentityCollector.collect(router_entry) + router_entry.time_spent['IdentityCollector'] += default_timer() - start start = default_timer() - yield from SystemResourceCollector.collect(router_metric) - router_metric.time_spent['SystemResourceCollector'] += default_timer() - start + yield from SystemResourceCollector.collect(router_entry) + router_entry.time_spent['SystemResourceCollector'] += default_timer() - start start = default_timer() - yield from HealthCollector.collect(router_metric) - router_metric.time_spent['HealthCollector'] += default_timer() - start + yield from HealthCollector.collect(router_entry) + router_entry.time_spent['HealthCollector'] += default_timer() - start - if router_metric.router_entry.dhcp: + if router_entry.config_entry.dhcp: start = default_timer() - yield from DHCPCollector.collect(router_metric) - router_metric.time_spent['DHCPCollector'] += default_timer() - start + yield from DHCPCollector.collect(router_entry) + router_entry.time_spent['DHCPCollector'] += default_timer() - start - if router_metric.router_entry.pool: + if router_entry.config_entry.pool: start = default_timer() - yield from PoolCollector.collect(router_metric) - router_metric.time_spent['PoolCollector'] += default_timer() - start + yield from PoolCollector.collect(router_entry) + router_entry.time_spent['PoolCollector'] += default_timer() - start - if router_metric.router_entry.interface: + if router_entry.config_entry.interface: start = default_timer() - yield from InterfaceCollector.collect(router_metric) - router_metric.time_spent['InterfaceCollector'] += default_timer() - start + yield from InterfaceCollector.collect(router_entry) + router_entry.time_spent['InterfaceCollector'] += default_timer() - start - if router_metric.router_entry.firewall: + if router_entry.config_entry.firewall: start = default_timer() - yield from FirewallCollector.collect(router_metric) - router_metric.time_spent['FirewallCollector'] += default_timer() - start + yield from FirewallCollector.collect(router_entry) + router_entry.time_spent['FirewallCollector'] += default_timer() - start - if router_metric.router_entry.monitor: + if router_entry.config_entry.monitor: start = default_timer() - yield from MonitorCollector.collect(router_metric) - router_metric.time_spent['MonitorCollector'] += default_timer() - start + yield from MonitorCollector.collect(router_entry) + router_entry.time_spent['MonitorCollector'] += default_timer() - start - if router_metric.router_entry.route: + if router_entry.config_entry.route: start = default_timer() - yield from RouteCollector.collect(router_metric) - router_metric.time_spent['RouteCollector'] += default_timer() - start + yield from RouteCollector.collect(router_entry) + router_entry.time_spent['RouteCollector'] += default_timer() - start - if router_metric.router_entry.wireless: + if router_entry.config_entry.wireless: start = default_timer() - yield from WLANCollector.collect(router_metric) - router_metric.time_spent['WLANCollector'] += default_timer() - start + yield from WLANCollector.collect(router_entry) + router_entry.time_spent['WLANCollector'] += default_timer() - start - if router_metric.router_entry.capsman: + if router_entry.config_entry.capsman: start = default_timer() - yield from CapsmanCollector.collect(router_metric) - router_metric.time_spent['CapsmanCollector'] += default_timer() - start + yield from CapsmanCollector.collect(router_entry) + router_entry.time_spent['CapsmanCollector'] += default_timer() - start - yield from MKTXPCollector.collect(router_metric) + yield from MKTXPCollector.collect(router_entry) diff --git a/mktxp/datasources/__init__.py b/mktxp/datasources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mktxp/datasources/base_ds.py b/mktxp/datasources/base_ds.py new file mode 100644 index 0000000..22bfff7 --- /dev/null +++ b/mktxp/datasources/base_ds.py @@ -0,0 +1,39 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +class BaseDSProcessor: + ''' Base Metrics DataSource processing + ''' + + @staticmethod + def trimmed_records(router_entry, *, router_records = [], metric_labels = [], add_router_id = True, translation_table = {}): + if len(metric_labels) == 0 and len(router_records) > 0: + metric_labels = router_records[0].keys() + metric_labels = set(metric_labels) + + labeled_records = [] + dash2_ = lambda x : x.replace('-', '_') + for router_record in router_records: + translated_record = {dash2_(key): value for (key, value) in router_record.items() if dash2_(key) in metric_labels} + + if add_router_id: + for key, value in router_entry.router_id.items(): + translated_record[key] = value + + # translate fields if needed + for key, func in translation_table.items(): + translated_record[key] = func(translated_record.get(key)) + labeled_records.append(translated_record) + + return labeled_records diff --git a/mktxp/datasources/capsman_ds.py b/mktxp/datasources/capsman_ds.py new file mode 100644 index 0000000..522cbfc --- /dev/null +++ b/mktxp/datasources/capsman_ds.py @@ -0,0 +1,41 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class CapsmanCapsMetricsDataSource: + ''' Caps Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + remote_caps_records = router_entry.api_connection.router_api().get_resource('/caps-man/remote-cap').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = remote_caps_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting caps-man remote caps info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + + +class CapsmanRegistrationsMetricsDataSource: + ''' Capsman Registrations Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = [], add_router_id = True): + try: + registration_table_records = router_entry.api_connection.router_api().get_resource('/caps-man/registration-table').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = registration_table_records, metric_labels = metric_labels, add_router_id = add_router_id) + except Exception as exc: + print(f'Error getting caps-man registration table info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None diff --git a/mktxp/datasources/dhcp_ds.py b/mktxp/datasources/dhcp_ds.py new file mode 100644 index 0000000..c92eb99 --- /dev/null +++ b/mktxp/datasources/dhcp_ds.py @@ -0,0 +1,43 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class DHCPMetricsDataSource: + ''' DHCP Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = [], add_router_id = True): + try: + #dhcp_lease_records = router_entry.api_connection.router_api().get_resource('/ip/dhcp-server/lease').get(status='bound') + dhcp_lease_records = router_entry.api_connection.router_api().get_resource('/ip/dhcp-server/lease').call('print', {'active':''}) + + # translation rules + translation_table = {} + if 'comment' in metric_labels: + translation_table['comment'] = lambda c: c if c else '' + if 'host_name' in metric_labels: + translation_table['host_name'] = lambda c: c if c else '' + if 'expires_after' in metric_labels: + translation_table['expires_after'] = lambda c: c if c else '' + if 'active_address' in metric_labels: + translation_table['active_address'] = lambda c: c if c else '' + + return BaseDSProcessor.trimmed_records(router_entry, router_records = dhcp_lease_records, metric_labels = metric_labels, add_router_id = add_router_id, translation_table = translation_table) + except Exception as exc: + print(f'Error getting dhcp info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + + diff --git a/mktxp/datasources/firewall_ds.py b/mktxp/datasources/firewall_ds.py new file mode 100644 index 0000000..122af44 --- /dev/null +++ b/mktxp/datasources/firewall_ds.py @@ -0,0 +1,39 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class FirewallMetricsDataSource: + ''' Firewall Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = [], raw = False, matching_only = True): + try: + filter_path = '/ip/firewall/filter' if not raw else '/ip/firewall/raw' + firewall_records = router_entry.api_connection.router_api().get_resource(filter_path).call('print', {'stats':'', 'all':''}) + if matching_only: + firewall_records = [record for record in firewall_records if int(record.get('bytes', '0')) > 0] + + # translation rules + translation_table = {} + if 'comment' in metric_labels: + translation_table['comment'] = lambda c: c if c else '' + + return BaseDSProcessor.trimmed_records(router_entry, router_records = firewall_records, metric_labels = metric_labels, translation_table = translation_table) + except Exception as exc: + print(f'Error getting firewall filters info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + + diff --git a/mktxp/datasources/health_ds.py b/mktxp/datasources/health_ds.py new file mode 100644 index 0000000..a7905f3 --- /dev/null +++ b/mktxp/datasources/health_ds.py @@ -0,0 +1,28 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class HealthMetricsDataSource: + ''' Health Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + health_records = router_entry.api_connection.router_api().get_resource('/system/health').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = health_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting system health info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None diff --git a/mktxp/datasources/identity_ds.py b/mktxp/datasources/identity_ds.py new file mode 100644 index 0000000..239b8cc --- /dev/null +++ b/mktxp/datasources/identity_ds.py @@ -0,0 +1,28 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class IdentityMetricsDataSource: + ''' Identity Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + identity_records = router_entry.api_connection.router_api().get_resource('/system/identity').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = identity_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting system identity info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None diff --git a/mktxp/datasources/interface_ds.py b/mktxp/datasources/interface_ds.py new file mode 100644 index 0000000..6b2d7e6 --- /dev/null +++ b/mktxp/datasources/interface_ds.py @@ -0,0 +1,59 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class InterfaceTrafficMetricsDataSource: + ''' Interface Traffic Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + traffic_records = router_entry.api_connection.router_api().get_resource('/interface').get(running='yes', disabled='no') + return BaseDSProcessor.trimmed_records(router_entry, router_records = traffic_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting interface traffic info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + + +class InterfaceMonitorMetricsDataSource: + ''' Interface Monitor Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = [], kind = 'ethernet', include_comments = False, running_only = True): + try: + interfaces = router_entry.api_connection.router_api().get_resource(f'/interface/{kind}').get() + interface_names = [(interface['name'], interface.get('comment'), interface.get('running')) for interface in interfaces] + + interface_monitor_records = [] + for int_num, interface_name in enumerate(interface_names): + interface_monitor_record = {} + if not running_only or interface_name[2] == 'true': + interface_monitor_record = router_entry.api_connection.router_api().get_resource(f'/interface/{kind}').call('monitor', {'once':'', 'numbers':f'{int_num}'})[0] + else: + # unless explicitly requested, no need to do a monitor call for not running interfaces + interface_monitor_record = {'name': interface_name[0], 'status': 'no-link'} + + if include_comments and interface_name[1]: + # combines names with comments + interface_monitor_record['name'] = interface_name[1] if router_entry.config_entry.use_comments_over_names else \ + f"{interface_name[0]} ({interface_name[1]})" + interface_monitor_records.append(interface_monitor_record) + + return BaseDSProcessor.trimmed_records(router_entry, router_records = interface_monitor_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting {kind} interface monitor info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + diff --git a/mktxp/datasources/mktxp_ds.py b/mktxp/datasources/mktxp_ds.py new file mode 100644 index 0000000..d7138d4 --- /dev/null +++ b/mktxp/datasources/mktxp_ds.py @@ -0,0 +1,29 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class MKTXPMetricsDataSource: + ''' MKTXP Metrics data provider + ''' + @staticmethod + def metric_records(router_entry): + mktxp_records = [] + for key in router_entry.time_spent.keys(): + mktxp_records.append({'name': key, 'duration': router_entry.time_spent[key]}) + + # translation rules + translation_table = {'duration': lambda d: d*1000} + return BaseDSProcessor.trimmed_records(router_entry, router_records = mktxp_records, translation_table = translation_table) diff --git a/mktxp/datasources/pool_ds.py b/mktxp/datasources/pool_ds.py new file mode 100644 index 0000000..b446651 --- /dev/null +++ b/mktxp/datasources/pool_ds.py @@ -0,0 +1,41 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class PoolMetricsDataSource: + ''' Pool Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + pool_records = router_entry.api_connection.router_api().get_resource('/ip/pool').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = pool_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting pool info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + + +class PoolUsedMetricsDataSource: + ''' Pool/Used Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + pool_used_records = router_entry.api_connection.router_api().get_resource('/ip/pool/used').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = pool_used_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting pool used info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None \ No newline at end of file diff --git a/mktxp/datasources/route_ds.py b/mktxp/datasources/route_ds.py new file mode 100644 index 0000000..e8f3314 --- /dev/null +++ b/mktxp/datasources/route_ds.py @@ -0,0 +1,28 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class RouteMetricsDataSource: + ''' Routes Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + route_records = router_entry.api_connection.router_api().get_resource('/ip/route').get(active='yes') + return BaseDSProcessor.trimmed_records(router_entry, router_records = route_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting routes info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None diff --git a/mktxp/datasources/routerboard_ds.py b/mktxp/datasources/routerboard_ds.py new file mode 100644 index 0000000..2944e33 --- /dev/null +++ b/mktxp/datasources/routerboard_ds.py @@ -0,0 +1,29 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class RouterboardMetricsDataSource: + ''' Routerboard Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + routerboard_records = router_entry.api_connection.router_api().get_resource('/system/routerboard').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = routerboard_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting system routerboard info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + diff --git a/mktxp/datasources/system_resource_ds.py b/mktxp/datasources/system_resource_ds.py new file mode 100644 index 0000000..d50fd83 --- /dev/null +++ b/mktxp/datasources/system_resource_ds.py @@ -0,0 +1,28 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class SystemResourceMetricsDataSource: + ''' System Resource Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = []): + try: + system_resource_records = router_entry.api_connection.router_api().get_resource('/system/resource').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = system_resource_records, metric_labels = metric_labels) + except Exception as exc: + print(f'Error getting system resource info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None diff --git a/mktxp/datasources/wireless_ds.py b/mktxp/datasources/wireless_ds.py new file mode 100644 index 0000000..4214a1c --- /dev/null +++ b/mktxp/datasources/wireless_ds.py @@ -0,0 +1,30 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.datasources.base_ds import BaseDSProcessor + + +class WirelessMetricsDataSource: + ''' Wireless Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = [], add_router_id = True): + try: + registration_table_records = router_entry.api_connection.router_api().get_resource('/interface/wireless/registration-table').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = registration_table_records, metric_labels = metric_labels, add_router_id = add_router_id) + except Exception as exc: + print(f'Error getting wireless registration table info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + + diff --git a/mktxp/router_connection.py b/mktxp/router_connection.py index c6251fc..16f7e86 100644 --- a/mktxp/router_connection.py +++ b/mktxp/router_connection.py @@ -11,12 +11,14 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + import ssl import socket from datetime import datetime from routeros_api import RouterOsApiPool from mktxp.cli.config.config import config_handler + class RouterAPIConnectionError(Exception): pass @@ -24,24 +26,24 @@ class RouterAPIConnectionError(Exception): class RouterAPIConnection: ''' Base wrapper interface for the routeros_api library ''' - def __init__(self, router_name, router_entry): + def __init__(self, router_name, config_entry): self.router_name = router_name - self.router_entry = router_entry + self.config_entry = config_entry self.last_failure_timestamp = self.successive_failure_count = 0 ctx = None - if self.router_entry.use_ssl and self.router_entry.no_ssl_certificate: + if self.config_entry.use_ssl and self.config_entry.no_ssl_certificate: ctx = ssl.create_default_context() ctx.set_ciphers('ADH:@SECLEVEL=0') self.connection = RouterOsApiPool( - host = self.router_entry.hostname, - username = self.router_entry.username, - password = self.router_entry.password, - port = self.router_entry.port, + host = self.config_entry.hostname, + username = self.config_entry.username, + password = self.config_entry.password, + port = self.config_entry.port, plaintext_login = True, - use_ssl = self.router_entry.use_ssl, - ssl_verify = self.router_entry.ssl_certificate_verify, + use_ssl = self.config_entry.use_ssl, + ssl_verify = self.config_entry.ssl_certificate_verify, ssl_context = ctx) self.connection.socket_timeout = config_handler._entry().socket_timeout @@ -62,7 +64,7 @@ class RouterAPIConnection: if self.is_connected() or self._in_connect_timeout(connect_time.timestamp()): return try: - print(f'Connecting to router {self.router_name}@{self.router_entry.hostname}') + print(f'Connecting to router {self.router_name}@{self.config_entry.hostname}') self.api = self.connection.get_api() self._set_connect_state(success = True, connect_time = connect_time) except (socket.error, socket.timeout, Exception) as exc: @@ -78,11 +80,11 @@ class RouterAPIConnection: connect_delay = self._connect_delay() if (connect_timestamp - self.last_failure_timestamp) < connect_delay: if not quiet: - print(f'{self.router_name}@{self.router_entry.hostname}: in connect timeout, {int(connect_delay - (connect_timestamp - self.last_failure_timestamp))}secs remaining') + print(f'{self.router_name}@{self.config_entry.hostname}: in connect timeout, {int(connect_delay - (connect_timestamp - self.last_failure_timestamp))}secs remaining') print(f'Successive failure count: {self.successive_failure_count}') return True if not quiet: - print(f'{self.router_name}@{self.router_entry.hostname}: OK to connect') + print(f'{self.router_name}@{self.config_entry.hostname}: OK to connect') if self.last_failure_timestamp > 0: print(f'Seconds since last failure: {connect_timestamp - self.last_failure_timestamp}') print(f'Prior successive failure count: {self.successive_failure_count}') @@ -98,12 +100,12 @@ class RouterAPIConnection: if success: self.last_failure_timestamp = 0 self.successive_failure_count = 0 - print(f'{connect_time.strftime("%Y-%m-%d %H:%M:%S")} Connection to router {self.router_name}@{self.router_entry.hostname} has been established') + print(f'{connect_time.strftime("%Y-%m-%d %H:%M:%S")} Connection to router {self.router_name}@{self.config_entry.hostname} has been established') else: self.api = None self.successive_failure_count += 1 self.last_failure_timestamp = connect_time.timestamp() - print(f'{connect_time.strftime("%Y-%m-%d %H:%M:%S")} Connection to router {self.router_name}@{self.router_entry.hostname} has failed: {exc}') + print(f'{connect_time.strftime("%Y-%m-%d %H:%M:%S")} Connection to router {self.router_name}@{self.config_entry.hostname} has failed: {exc}') diff --git a/mktxp/metrics_handler.py b/mktxp/router_entries_handler.py similarity index 77% rename from mktxp/metrics_handler.py rename to mktxp/router_entries_handler.py index 634c1c6..d9992bc 100644 --- a/mktxp/metrics_handler.py +++ b/mktxp/router_entries_handler.py @@ -13,28 +13,28 @@ from mktxp.cli.config.config import config_handler -from mktxp.router_metric import RouterMetric +from mktxp.router_entry import RouterEntry -class RouterMetricsHandler: +class RouterEntriesHandler: ''' Handles RouterOS entries defined in MKTXP config ''' def __init__(self): - self.router_metrics = [] + self.router_entries = [] for router_name in config_handler.registered_entries(): entry = config_handler.entry(router_name) if entry.enabled: - self.router_metrics.append(RouterMetric(router_name)) + self.router_entries.append(RouterEntry(router_name)) @staticmethod - def router_metric(entry_name, enabled_only = False): - router_metric = None + def router_entry(entry_name, enabled_only = False): + router_entry = None for router_name in config_handler.registered_entries(): if router_name == entry_name: if enabled_only: entry = config_handler.entry(router_name) if not entry.enabled: break - router_metric = RouterMetric(router_name) + router_entry = RouterEntry(router_name) break - return router_metric + return router_entry diff --git a/mktxp/router_entry.py b/mktxp/router_entry.py new file mode 100644 index 0000000..3bb222f --- /dev/null +++ b/mktxp/router_entry.py @@ -0,0 +1,41 @@ +# coding=utf8 +## Copyright (c) 2020 Arseniy Kuznetsov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + + +from mktxp.cli.config.config import config_handler, MKTXPConfigKeys +from mktxp.router_connection import RouterAPIConnection + + +class RouterEntry: + ''' RouterOS Entry + ''' + def __init__(self, router_name): + self.router_name = router_name + self.config_entry = config_handler.entry(router_name) + self.api_connection = RouterAPIConnection(router_name, self.config_entry) + self.router_id = { + MKTXPConfigKeys.ROUTERBOARD_NAME: self.router_name, + MKTXPConfigKeys.ROUTERBOARD_ADDRESS: self.config_entry.hostname + } + self.time_spent = { 'IdentityCollector': 0, + 'SystemResourceCollector': 0, + 'HealthCollector': 0, + 'DHCPCollector': 0, + 'PoolCollector': 0, + 'InterfaceCollector': 0, + 'FirewallCollector': 0, + 'MonitorCollector': 0, + 'RouteCollector': 0, + 'WLANCollector': 0, + 'CapsmanCollector': 0 + } diff --git a/mktxp/router_metric.py b/mktxp/router_metric.py deleted file mode 100644 index 9e399fa..0000000 --- a/mktxp/router_metric.py +++ /dev/null @@ -1,201 +0,0 @@ -# coding=utf8 -## Copyright (c) 2020 Arseniy Kuznetsov -## -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; either version 2 -## of the License, or (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. - - -from mktxp.cli.config.config import config_handler, MKTXPConfigKeys -from mktxp.router_connection import RouterAPIConnection - -class RouterMetric: - ''' RouterOS Metrics data provider - ''' - def __init__(self, router_name): - self.router_name = router_name - self.router_entry = config_handler.entry(router_name) - self.api_connection = RouterAPIConnection(router_name, self.router_entry) - self.router_id = { - MKTXPConfigKeys.ROUTERBOARD_NAME: self.router_name, - MKTXPConfigKeys.ROUTERBOARD_ADDRESS: self.router_entry.hostname - } - self.time_spent = { 'IdentityCollector': 0, - 'SystemResourceCollector': 0, - 'HealthCollector': 0, - 'DHCPCollector': 0, - 'PoolCollector': 0, - 'InterfaceCollector': 0, - 'FirewallCollector': 0, - 'MonitorCollector': 0, - 'RouteCollector': 0, - 'WLANCollector': 0, - 'CapsmanCollector': 0 - } - - def identity_records(self, identity_labels = []): - try: - identity_records = self.api_connection.router_api().get_resource('/system/identity').get() - return self._trimmed_records(identity_records, identity_labels) - except Exception as exc: - print(f'Error getting system identity info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def routerboard_records(self, routerboard_labels = []): - try: - routerboard_records = self.api_connection.router_api().get_resource('/system/routerboard').get() - return self._trimmed_records(routerboard_records, routerboard_labels) - except Exception as exc: - print(f'Error getting system routerboard info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def health_records(self, health_labels = []): - try: - health_records = self.api_connection.router_api().get_resource('/system/health').get() - return self._trimmed_records(health_records, health_labels) - except Exception as exc: - print(f'Error getting system health info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def system_resource_records(self, resource_labels = []): - try: - system_resource_records = self.api_connection.router_api().get_resource('/system/resource').get() - return self._trimmed_records(system_resource_records, resource_labels) - except Exception as exc: - print(f'Error getting system resource info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def dhcp_lease_records(self, dhcp_lease_labels = [], add_router_id = True): - try: - #dhcp_lease_records = self.api_connection.router_api().get_resource('/ip/dhcp-server/lease').get(status='bound') - dhcp_lease_records = self.api_connection.router_api().get_resource('/ip/dhcp-server/lease').call('print', {'active':''}) - return self._trimmed_records(dhcp_lease_records, dhcp_lease_labels, add_router_id) - except Exception as exc: - print(f'Error getting dhcp info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def interface_traffic_records(self, interface_traffic_labels = []): - try: - traffic_records = self.api_connection.router_api().get_resource('/interface').get(running='yes', disabled='no') - return self._trimmed_records(traffic_records, interface_traffic_labels) - except Exception as exc: - print(f'Error getting interface traffic info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def interface_monitor_records(self, interface_monitor_labels = [], kind = 'ethernet', include_comments = False): - try: - interfaces = self.api_connection.router_api().get_resource(f'/interface/{kind}').get() - interface_names = [(interface['name'], interface.get('comment'), interface.get('running')) for interface in interfaces] - - interface_monitor = lambda int_num : self.api_connection.router_api().get_resource(f'/interface/{kind}').call('monitor', {'once':'', 'numbers':f'{int_num}'}) - interface_monitor_records = [interface_monitor(int_num)[0] for int_num in range(len(interface_names))] - - if include_comments: - # combine interfaces names with comments - for interface_monitor_record in interface_monitor_records: - for interface_name in interface_names: - if interface_name[1] and interface_name[0] == interface_monitor_record['name']: - interface_monitor_record['name'] = interface_name[1] if self.router_entry.use_comments_over_names else \ - f"{interface_name[0]} ({interface_name[1]})" - return self._trimmed_records(interface_monitor_records, interface_monitor_labels) - except Exception as exc: - print(f'Error getting {kind} interface monitor info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def pool_records(self, pool_labels = []): - try: - pool_records = self.api_connection.router_api().get_resource('/ip/pool').get() - return self._trimmed_records(pool_records, pool_labels) - except Exception as exc: - print(f'Error getting pool info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def pool_used_records(self, pool_used_labels = []): - try: - pool_used_records = self.api_connection.router_api().get_resource('/ip/pool/used').get() - return self._trimmed_records(pool_used_records, pool_used_labels) - except Exception as exc: - print(f'Error getting pool used info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def route_records(self, route_labels = []): - try: - route_records = self.api_connection.router_api().get_resource('/ip/route').get(active='yes') - return self._trimmed_records(route_records, route_labels) - except Exception as exc: - print(f'Error getting routes info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def wireless_registration_table_records(self, registration_table_labels = [], add_router_id = True): - try: - registration_table_records = self.api_connection.router_api().get_resource('/interface/wireless/registration-table').get() - return self._trimmed_records(registration_table_records, registration_table_labels, add_router_id) - except Exception as exc: - print(f'Error getting wireless registration table info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def capsman_remote_caps_records(self, remote_caps_labels = []): - try: - remote_caps_records = self.api_connection.router_api().get_resource('/caps-man/remote-cap').get() - return self._trimmed_records(remote_caps_records, remote_caps_labels) - except Exception as exc: - print(f'Error getting caps-man remote caps info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def capsman_registration_table_records(self, registration_table_labels = [], add_router_id = True): - try: - registration_table_records = self.api_connection.router_api().get_resource('/caps-man/registration-table').get() - return self._trimmed_records(registration_table_records, registration_table_labels, add_router_id) - except Exception as exc: - print(f'Error getting caps-man registration table info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def firewall_records(self, firewall_labels = [], raw = False, matching_only = True): - try: - filter_path = '/ip/firewall/filter' if not raw else '/ip/firewall/raw' - firewall_records = self.api_connection.router_api().get_resource(filter_path).call('print', {'stats':'', 'all':''}) - if matching_only: - firewall_records = [record for record in firewall_records if int(record.get('bytes', '0')) > 0] - # translation rules - translation_table = {} -# if 'id' in firewall_labels: -# translation_table['id'] = lambda id: str(int(id[1:], 16) - 1) - if 'comment' in firewall_labels: - translation_table['comment'] = lambda c: c if c else '' - return self._trimmed_records(firewall_records, firewall_labels, translation_table = translation_table) - except Exception as exc: - print(f'Error getting firewall filters info from router{self.router_name}@{self.router_entry.hostname}: {exc}') - return None - - def mktxp_records(self): - mktxp_records = [] - for key in self.time_spent.keys(): - mktxp_records.append({'name': key, 'duration': self.time_spent[key]}) - # translation rules - translation_table = {'duration': lambda d: d*1000} - return self._trimmed_records(mktxp_records, translation_table = translation_table) - - # Helpers - def _trimmed_records(self, router_records, metric_labels = [], add_router_id = True, translation_table = {}): - if len(metric_labels) == 0 and len(router_records) > 0: - metric_labels = router_records[0].keys() - metric_labels = set(metric_labels) - - labeled_records = [] - dash2_ = lambda x : x.replace('-', '_') - for router_record in router_records: - translated_record = {dash2_(key): value for (key, value) in router_record.items() if dash2_(key) in metric_labels} - if add_router_id: - for key, value in self.router_id.items(): - translated_record[key] = value - # translate fields if needed - for key, func in translation_table.items(): - translated_record[key] = func(translated_record.get(key)) - labeled_records.append(translated_record) - return labeled_records diff --git a/mktxp/utils/utils.py b/mktxp/utils/utils.py index 28a813e..6bfb9e7 100755 --- a/mktxp/utils/utils.py +++ b/mktxp/utils/utils.py @@ -11,6 +11,7 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. + import os, sys, shlex, tempfile, shutil, re import subprocess, hashlib from timeit import default_timer diff --git a/setup.py b/setup.py index 291cb91..ce0a50e 100755 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ with open(path.join(pkg_dir, 'README.md'), encoding='utf-8') as f: setup( name='mktxp', - version='0.23', + version='0.24', url='https://github.com/akpw/mktxp',