diff --git a/README.md b/README.md index b9370f8..31b2b6e 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ The default configuration file comes with a sample configuration, making it easy user = True # Active Users metrics queue = True # Queues metrics + + bgp = False # BGP sessions metrics remote_dhcp_entry = None # An MKTXP entry for remote DHCP info resolution (capsman/wireless) diff --git a/mktxp/cli/config/config.py b/mktxp/cli/config/config.py index 7315042..53ad070 100755 --- a/mktxp/cli/config/config.py +++ b/mktxp/cli/config/config.py @@ -46,6 +46,7 @@ class CollectorKeys: QUEUE_SIMPLE_COLLECTOR = 'QueueSimpleCollector' KID_CONTROL_DEVICE_COLLECTOR = 'KidControlCollector' USER_COLLECTOR = 'UserCollector' + BGP_COLLECTOR = 'BGPCollector' MKTXP_COLLECTOR = 'MKTXPCollector' @@ -87,6 +88,8 @@ class MKTXPConfigKeys: FE_USER_KEY = 'user' FE_QUEUE_KEY = 'queue' + FE_BGP_KEY = 'bgp' + FE_REMOTE_DHCP_ENTRY = 'remote_dhcp_entry' FE_CHECK_FOR_UPDATES = 'check_for_updates' @@ -133,7 +136,7 @@ class MKTXPConfigKeys: BOOLEAN_KEYS_NO = {ENABLED_KEY, SSL_KEY, NO_SSL_CERTIFICATE, FE_CHECK_FOR_UPDATES, FE_KID_CONTROL_DEVICE, - SSL_CERTIFICATE_VERIFY, FE_IPV6_FIREWALL_KEY, FE_IPV6_NEIGHBOR_KEY, FE_CONNECTION_STATS_KEY} + SSL_CERTIFICATE_VERIFY, FE_IPV6_FIREWALL_KEY, FE_IPV6_NEIGHBOR_KEY, FE_CONNECTION_STATS_KEY, FE_BGP_KEY} # Feature keys enabled by default BOOLEAN_KEYS_YES = {FE_DHCP_KEY, FE_PACKAGE_KEY, FE_DHCP_LEASE_KEY, FE_DHCP_POOL_KEY, FE_IP_CONNECTIONS_KEY, FE_INTERFACE_KEY, FE_FIREWALL_KEY, @@ -162,7 +165,7 @@ class ConfigEntry: MKTXPConfigKeys.FE_FIREWALL_KEY, MKTXPConfigKeys.FE_MONITOR_KEY, MKTXPConfigKeys.FE_ROUTE_KEY, MKTXPConfigKeys.FE_WIRELESS_KEY, MKTXPConfigKeys.FE_WIRELESS_CLIENTS_KEY, MKTXPConfigKeys.FE_IP_CONNECTIONS_KEY, MKTXPConfigKeys.FE_CONNECTION_STATS_KEY, MKTXPConfigKeys.FE_CAPSMAN_KEY, MKTXPConfigKeys.FE_CAPSMAN_CLIENTS_KEY, MKTXPConfigKeys.FE_POE_KEY, MKTXPConfigKeys.FE_NETWATCH_KEY, MKTXPConfigKeys.MKTXP_USE_COMMENTS_OVER_NAMES, MKTXPConfigKeys.FE_PUBLIC_IP_KEY, MKTXPConfigKeys.FE_IPV6_FIREWALL_KEY, MKTXPConfigKeys.FE_IPV6_NEIGHBOR_KEY, - MKTXPConfigKeys.FE_USER_KEY, MKTXPConfigKeys.FE_QUEUE_KEY, MKTXPConfigKeys.FE_REMOTE_DHCP_ENTRY, MKTXPConfigKeys.FE_CHECK_FOR_UPDATES, MKTXPConfigKeys.FE_KID_CONTROL_DEVICE, + MKTXPConfigKeys.FE_USER_KEY, MKTXPConfigKeys.FE_QUEUE_KEY, MKTXPConfigKeys.FE_REMOTE_DHCP_ENTRY, MKTXPConfigKeys.FE_CHECK_FOR_UPDATES, MKTXPConfigKeys.FE_KID_CONTROL_DEVICE, MKTXPConfigKeys.FE_BGP_KEY, ]) MKTXPSystemEntry = namedtuple('MKTXPSystemEntry', [MKTXPConfigKeys.PORT_KEY, MKTXPConfigKeys.MKTXP_SOCKET_TIMEOUT, MKTXPConfigKeys.MKTXP_INITIAL_DELAY, MKTXPConfigKeys.MKTXP_MAX_DELAY, diff --git a/mktxp/cli/config/mktxp.conf b/mktxp/cli/config/mktxp.conf index 18b9af3..1527f7e 100644 --- a/mktxp/cli/config/mktxp.conf +++ b/mktxp/cli/config/mktxp.conf @@ -52,6 +52,8 @@ user = True # Active Users metrics queue = True # Queues metrics + + bgp = False # BGP sessions metrics remote_dhcp_entry = None # An MKTXP entry for remote DHCP info resolution (capsman/wireless) diff --git a/mktxp/collector/bgp_collector.py b/mktxp/collector/bgp_collector.py new file mode 100644 index 0000000..cb55ebb --- /dev/null +++ b/mktxp/collector/bgp_collector.py @@ -0,0 +1,79 @@ +# 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.collector.base_collector import BaseCollector +from mktxp.flow.processor.output import BaseOutputProcessor +from mktxp.datasource.bgp_ds import BGPMetricsDataSource + + +class BGPCollector(BaseCollector): + '''BGP collector''' + @staticmethod + def collect(router_entry): + if not router_entry.config_entry.bgp: + return + + bgp_labels = ['name', 'remote.address', 'remote.as', 'local.as', 'remote.afi', 'local.afi', 'remote.messages', 'remote.bytes', 'local.messages', 'local.bytes', 'prefix_count', 'established', 'uptime'] + bgp_records = BGPMetricsDataSource.metric_records(router_entry, metric_labels=bgp_labels) + + + if bgp_records: + # translate records to appropriate values + translated_fields = ['established', 'uptime'] + for bgp_record in bgp_records: + for translated_field in translated_fields: + value = bgp_record.get(translated_field, None) + if value: + bgp_record[translated_field] = BGPCollector._translated_values(translated_field, value) + + session_id_labes = ['name', 'remote.address', 'remote.as', 'local.as', 'remote.afi', 'local.afi'] + bgp_sessions_metrics = BaseCollector.info_collector('bgp_sessions_info', 'BGP sessions info', bgp_records, session_id_labes) + yield bgp_sessions_metrics + + remote_messages_metrics = BaseCollector.counter_collector('bgp_remote_messages', 'Number of remote messages', bgp_records, 'remote.messages', session_id_labes) + yield remote_messages_metrics + + + local_messages_metrics = BaseCollector.counter_collector('bgp_local_messages', 'Number of local messages', bgp_records, 'local.messages', session_id_labes) + yield local_messages_metrics + + + remote_bytes_metrics = BaseCollector.counter_collector('bgp_remote_bytes', 'Number of remote bytes', bgp_records, 'remote.bytes', session_id_labes) + yield remote_bytes_metrics + + + local_bytes_metrics = BaseCollector.counter_collector('bgp_local_bytes', 'Number of local bytes', bgp_records, 'local.bytes', session_id_labes) + yield local_bytes_metrics + + + prefix_count_metrics = BaseCollector.gauge_collector('bgp_prefix_count', 'BGP prefix count', bgp_records, 'prefix_count', session_id_labes) + yield prefix_count_metrics + + + established_metrics = BaseCollector.gauge_collector('bgp_established', 'BGP established', bgp_records, 'established', session_id_labes) + yield established_metrics + + + uptime_metrics = BaseCollector.gauge_collector('bgp_uptime', 'BGP uptime in milliseconds', bgp_records, 'uptime', session_id_labes) + yield uptime_metrics + + + # Helpers + @staticmethod + def _translated_values(translated_field, value): + return { + 'established': lambda value: '1' if value=='true' else '0', + 'uptime': lambda value: BaseOutputProcessor.parse_timedelta_milliseconds(value) + }[translated_field](value) + diff --git a/mktxp/datasource/bgp_ds.py b/mktxp/datasource/bgp_ds.py new file mode 100644 index 0000000..78740be --- /dev/null +++ b/mktxp/datasource/bgp_ds.py @@ -0,0 +1,31 @@ +# 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.datasource.base_ds import BaseDSProcessor + + +class BGPMetricsDataSource: + ''' Wireless Metrics data provider + ''' + @staticmethod + def metric_records(router_entry, *, metric_labels = None, add_router_id = True): + if metric_labels is None: + metric_labels = [] + try: + bgp_records = router_entry.api_connection.router_api().get_resource('/routing/bgp/session').get() + return BaseDSProcessor.trimmed_records(router_entry, router_records = bgp_records, metric_labels = metric_labels, add_router_id = add_router_id) + except Exception as exc: + print(f'Error getting BGP sessions info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + diff --git a/mktxp/flow/collector_registry.py b/mktxp/flow/collector_registry.py index 2212f7d..028d110 100644 --- a/mktxp/flow/collector_registry.py +++ b/mktxp/flow/collector_registry.py @@ -37,6 +37,7 @@ from mktxp.collector.user_collector import UserCollector from mktxp.collector.queue_collector import QueueTreeCollector from mktxp.collector.queue_collector import QueueSimpleCollector from mktxp.collector.kid_control_device_collector import KidDeviceCollector +from mktxp.collector.bgp_collector import BGPCollector class CollectorRegistry: ''' MKTXP Collectors Registry @@ -74,6 +75,8 @@ class CollectorRegistry: self.register(CollectorKeys.QUEUE_SIMPLE_COLLECTOR, QueueSimpleCollector.collect) self.register(CollectorKeys.KID_CONTROL_DEVICE_COLLECTOR, KidDeviceCollector.collect) + self.register(CollectorKeys.BGP_COLLECTOR, BGPCollector.collect) + self.register(CollectorKeys.MKTXP_COLLECTOR, MKTXPCollector.collect) diff --git a/mktxp/flow/processor/output.py b/mktxp/flow/processor/output.py index 8fb43eb..1af33c5 100644 --- a/mktxp/flow/processor/output.py +++ b/mktxp/flow/processor/output.py @@ -117,7 +117,7 @@ class BaseOutputProcessor: def parse_timedelta(time): duration_interval_rgx = config_handler.re_compiled.get('duration_interval_rgx') if not duration_interval_rgx: - duration_interval_rgx = re.compile(r'((?P\d+)w)?((?P\d+)d)?((?P\d+)h)?((?P\d+)m)?((?P\d+)s)?') + duration_interval_rgx = re.compile(r'((?P\d+)w)?((?P\d+)d)?((?P\d+)h)?((?P\d+)m)?((?P\d+)s)?((?P\d+)ms)?') config_handler.re_compiled['duration_interval_rgx'] = duration_interval_rgx time_dict = duration_interval_rgx.match(time).groupdict() return timedelta(**{key: int(value) for key, value in time_dict.items() if value}) @@ -126,6 +126,10 @@ class BaseOutputProcessor: def parse_timedelta_seconds(time): return BaseOutputProcessor.parse_timedelta(time).total_seconds() + @staticmethod + def parse_timedelta_milliseconds(time): + return BaseOutputProcessor.parse_timedelta(time) / timedelta(milliseconds=1) + @staticmethod def parse_signal_strength(signal_strength): wifi_signal_strength_rgx = config_handler.re_compiled.get('wifi_signal_strength_rgx') diff --git a/mktxp/flow/router_entry.py b/mktxp/flow/router_entry.py index 36a4837..907347a 100644 --- a/mktxp/flow/router_entry.py +++ b/mktxp/flow/router_entry.py @@ -66,6 +66,7 @@ class RouterEntry: CollectorKeys.QUEUE_SIMPLE_COLLECTOR: 0, CollectorKeys.KID_CONTROL_DEVICE_COLLECTOR: 0, CollectorKeys.USER_COLLECTOR: 0, + CollectorKeys.BGP_COLLECTOR: 0, CollectorKeys.MKTXP_COLLECTOR: 0 } self._dhcp_entry = None diff --git a/setup.py b/setup.py index 44a8a58..25cbd47 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='1.2.2', + version='1.2.3', url='https://github.com/akpw/mktxp',