Skip to content

Commit

Permalink
Split status and running metric, use comment as name
Browse files Browse the repository at this point in the history
* Splitting the status and running metrics
  * interface_status is the link status
  * interface_running is the running state reported by RouterOS devices
  * interface_disabled is the disabled state
* Move the handling of the 'use_comments_over_names' setting to the
  DS, now it is also supported for GRE, IPIP, EoIP and other interfaces
  • Loading branch information
phibos committed Dec 23, 2024
1 parent b373038 commit 5d821f7
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 83 deletions.
117 changes: 84 additions & 33 deletions mktxp/collector/interface_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,56 +23,107 @@ class InterfaceCollector(BaseCollector):
def collect(router_entry):
if not router_entry.config_entry.interface:
return

interface_traffic_labels = ['disabled', 'name', 'comment', 'rx_byte', 'tx_byte', 'rx_packet', 'tx_packet', 'rx_error', 'tx_error', 'rx_drop', 'tx_drop', 'link_downs', 'running']

interface_traffic_labels = ['disabled', 'name', 'comment', 'rx_byte', 'tx_byte', 'rx_packet', 'tx_packet',
'rx_error', 'tx_error', 'rx_drop', 'tx_drop', 'link_downs', 'running']
interface_traffic_translation_table = {
'running': lambda value: '1' if value == 'true' else '0',
'disabled': lambda value: '1' if value == 'true' else '0'
}

interface_traffic_records = InterfaceTrafficMetricsDataSource.metric_stats_records(
interface_traffic_records = InterfaceTrafficMetricsDataSource.metric_records(
router_entry,
metric_labels=interface_traffic_labels,
translation_table=interface_traffic_translation_table,
)

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_entry.config_entry.use_comments_over_names \
else f"{interface_traffic_record['name']} ({interface_traffic_record['comment']})"
if not interface_traffic_records:
return

yield BaseCollector.gauge_collector(
'interface_status',
'Current running status of the interface',
interface_traffic_records,
metric_key='running',
metric_labels=['name', 'disabled']
)
yield BaseCollector.gauge_collector(
'interface_running',
'Current running status of the interface',
interface_traffic_records,
metric_key='running',
metric_labels=['name']
)

rx_byte_metric = BaseCollector.counter_collector('interface_rx_byte', 'Number of received bytes', interface_traffic_records, 'rx_byte', ['name'])
yield rx_byte_metric
yield BaseCollector.gauge_collector(
'interface_disabled',
'Current disabled status of the interface',
interface_traffic_records,
metric_key='disabled',
metric_labels=['name']
)

tx_byte_metric = BaseCollector.counter_collector('interface_tx_byte', 'Number of transmitted bytes', interface_traffic_records, 'tx_byte', ['name'])
yield tx_byte_metric
yield BaseCollector.counter_collector(
'interface_rx_byte',
'Number of received bytes',
interface_traffic_records,
'rx_byte',
['name']
)

yield BaseCollector.counter_collector(
'interface_tx_byte',
'Number of transmitted bytes',
interface_traffic_records,
'tx_byte',
['name']
)

rx_packet_metric = BaseCollector.counter_collector('interface_rx_packet', 'Number of packets received', interface_traffic_records, 'rx_packet', ['name'])
yield rx_packet_metric
yield BaseCollector.counter_collector(
'interface_rx_packet',
'Number of packets received',
interface_traffic_records,
'rx_packet',
['name']
)

tx_packet_metric = BaseCollector.counter_collector('interface_tx_packet', 'Number of transmitted packets', interface_traffic_records, 'tx_packet', ['name'])
yield tx_packet_metric
yield BaseCollector.counter_collector(
'interface_tx_packet',
'Number of transmitted packets',
interface_traffic_records,
'tx_packet',
['name']
)

rx_error_metric = BaseCollector.counter_collector('interface_rx_error', 'Number of packets received with an error', interface_traffic_records, 'rx_error', ['name'])
yield rx_error_metric
yield BaseCollector.counter_collector(
'interface_rx_error',
'Number of packets received with an error',
interface_traffic_records,
'rx_error',
['name']
)

tx_error_metric = BaseCollector.counter_collector('interface_tx_error', 'Number of packets transmitted with an error', interface_traffic_records, 'tx_error', ['name'])
yield tx_error_metric
yield BaseCollector.counter_collector(
'interface_tx_error',
'Number of packets transmitted with an error',
interface_traffic_records,
'tx_error',
['name']
)

rx_drop_metric = BaseCollector.counter_collector('interface_rx_drop', 'Number of received packets being dropped', interface_traffic_records, 'rx_drop', ['name'])
yield rx_drop_metric
yield BaseCollector.counter_collector(
'interface_rx_drop',
'Number of received packets being dropped',
interface_traffic_records,
'rx_drop',
['name']
)

tx_drop_metric = BaseCollector.counter_collector('interface_tx_drop', 'Number of transmitted packets being dropped', interface_traffic_records, 'tx_drop', ['name'])
yield tx_drop_metric
yield BaseCollector.counter_collector(
'interface_tx_drop',
'Number of transmitted packets being dropped',
interface_traffic_records,
'tx_drop',
['name']
)

link_downs_metric = BaseCollector.counter_collector('link_downs', 'Number of times link went down', interface_traffic_records, 'link_downs', ['name'])
yield link_downs_metric
yield BaseCollector.counter_collector(
'link_downs',
'Number of times link went down',
interface_traffic_records,
'link_downs',
['name']
)
64 changes: 45 additions & 19 deletions mktxp/collector/monitor_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,66 @@


class MonitorCollector(BaseCollector):
''' Ethernet Interface Monitor Metrics collector
'''
""" Ethernet Interface Monitor Metrics collector
"""
@staticmethod
def collect(router_entry):
if not router_entry.config_entry.monitor:
return

monitor_labels = ['rate', 'full_duplex', 'name', 'sfp_temperature', 'sfp_module_present', 'sfp_wavelength', 'sfp_tx_power', 'sfp_rx_power']
monitor_labels = ['status', 'rate', 'full_duplex', 'name', 'sfp_temperature', 'sfp_module_present',
'sfp_wavelength', 'sfp_tx_power', 'sfp_rx_power']
translation_table = {
'status': lambda value: '1' if value == 'link-ok' else '0',
'rate': lambda value: MonitorCollector._rates(value) if value else '0',
'full_duplex': lambda value: '1' if value == 'true' else '0',
'name': lambda value: value if value else '',
'sfp_module_present': lambda value: '1' if value == 'true' else '0',
'sfp_temperature': lambda value: value if value else '0'
}
monitor_records = InterfaceMonitorMetricsDataSource.metric_records(router_entry, metric_labels = monitor_labels,
translation_table=translation_table, include_comments = True)
if monitor_records:
# limit records according to the relevant metrics
rate_records = [monitor_record for monitor_record in monitor_records if monitor_record.get('rate', None)]
monitor_rates_metrics = BaseCollector.gauge_collector('interface_rate', 'Actual interface connection data rate', rate_records, 'rate', ['name'])
yield monitor_rates_metrics
monitor_records = InterfaceMonitorMetricsDataSource.metric_records(
router_entry,
metric_labels=monitor_labels,
translation_table=translation_table,
include_comments=True
)

full_duplex_records = [monitor_record for monitor_record in monitor_records if monitor_record.get('full_duplex', None)]
monitor_rates_metrics = BaseCollector.gauge_collector('interface_full_duplex', 'Full duplex data transmission', full_duplex_records, 'full_duplex', ['name'])
yield monitor_rates_metrics
if not monitor_records:
return

yield BaseCollector.gauge_collector(
'interface_status',
'Current interface link status',
monitor_records,
'status',
['name']
)

# limit records according to the relevant metrics
rate_records = [monitor_record for monitor_record in monitor_records if monitor_record.get('rate', None)]
yield BaseCollector.gauge_collector(
'interface_rate',
'Actual interface connection data rate',
rate_records,
'rate',
['name']
)

sfp_metrics = [record for record in monitor_records if int(record.get("sfp_module_present"))]
if sfp_metrics:
yield BaseCollector.gauge_collector('interface_sfp_temperature', 'Current SFP Temperature', sfp_metrics, 'sfp_temperature', ['name'])
yield BaseCollector.gauge_collector('interface_sfp_wavelength', 'Current SFP Wavelength',sfp_metrics, 'sfp_wavelength', ['name'])
yield BaseCollector.gauge_collector('interface_sfp_tx_power', 'Current SFP TX Power', sfp_metrics, 'sfp_tx_power', ['name'])
yield BaseCollector.gauge_collector('interface_sfp_rx_power', 'Current SFP RX Power', sfp_metrics, 'sfp_rx_power', ['name'])
full_duplex_records = [monitor_record for monitor_record in monitor_records if monitor_record.get('full_duplex', None)]
yield BaseCollector.gauge_collector(
'interface_full_duplex',
'Full duplex data transmission',
full_duplex_records,
'full_duplex',
['name']
)

sfp_metrics = [record for record in monitor_records if int(record.get("sfp_module_present"))]
if sfp_metrics:
yield BaseCollector.gauge_collector('interface_sfp_temperature', 'Current SFP Temperature', sfp_metrics, 'sfp_temperature', ['name'])
yield BaseCollector.gauge_collector('interface_sfp_wavelength', 'Current SFP Wavelength',sfp_metrics, 'sfp_wavelength', ['name'])
yield BaseCollector.gauge_collector('interface_sfp_tx_power', 'Current SFP TX Power', sfp_metrics, 'sfp_tx_power', ['name'])
yield BaseCollector.gauge_collector('interface_sfp_rx_power', 'Current SFP RX Power', sfp_metrics, 'sfp_rx_power', ['name'])

@staticmethod
def _rates(rate_option):
Expand Down
71 changes: 40 additions & 31 deletions mktxp/datasource/interface_ds.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,25 @@
from mktxp.utils.utils import routerOS7_version


class InterfaceMetricsDataSource:
''' Interface Monitor Metrics data provider
'''
class BaseInterfaceDataSource:
@staticmethod
def metric_records(router_entry, *, metric_labels = None, kind = 'ethernet', additional_proplist=None, translation_table=None):
def rewrite_interface_names(router_entry, metric_records):
for metric_record in metric_records:
if metric_record.get('comment'):
if router_entry.config_entry.use_comments_over_names:
metric_record['name'] = metric_record['comment']
else:
metric_record['name'] = f"{metric_record['name']} ({metric_record['comment']})"

return metric_records


class InterfaceMetricsDataSource(BaseInterfaceDataSource):
""" Interface Monitor Metrics data provider
"""
@staticmethod
def metric_records(router_entry, *, metric_labels=None, kind='ethernet', additional_proplist=None,
translation_table=None):
if metric_labels is None:
metric_labels = []

Expand All @@ -33,46 +47,41 @@ def metric_records(router_entry, *, metric_labels = None, kind = 'ethernet', add
}

try:

interface_records = router_entry.api_connection.router_api().get_resource(
f'/interface/{kind}'
).call(
'print',
call_params
)
return BaseDSProcessor.trimmed_records(router_entry,
router_records=interface_records,
metric_labels=metric_labels,
translation_table=translation_table)
interface_records = BaseInterfaceDataSource.rewrite_interface_names(router_entry, interface_records)
return BaseDSProcessor.trimmed_records(
router_entry,
router_records=interface_records,
metric_labels=metric_labels,
translation_table=translation_table
)
except Exception as exc:
print(f'Error getting {kind} interface info from router {router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}')
return None


class InterfaceTrafficMetricsDataSource:
''' Interface Traffic Metrics data provider
'''
class InterfaceTrafficMetricsDataSource(BaseInterfaceDataSource):
""" Interface Traffic Metrics data provider
"""
@staticmethod
def metric_records(router_entry, *, metric_labels = None):
if metric_labels is None:
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

''' Interface Traffic Stats data provider
'''
@staticmethod
def metric_stats_records(router_entry, *, metric_labels, translation_table=None):
def metric_records(router_entry, *, metric_labels, translation_table=None):
metric_labels = metric_labels or []
try:
# get stats for all existing interfaces
metric_stats_records = router_entry.api_connection.router_api().get_resource('/interface').call('print', {'stats': 'detail'})
metric_stats_records = router_entry.api_connection.router_api().get_resource(
'/interface'
).call(
'print',
{'stats': 'detail'}
)
metric_stats_records = BaseInterfaceDataSource.rewrite_interface_names(router_entry, metric_stats_records)
return BaseDSProcessor.trimmed_records(
router_entry,
router_entry=router_entry,
router_records=metric_stats_records,
metric_labels=metric_labels,
translation_table=translation_table,
Expand All @@ -81,9 +90,10 @@ def metric_stats_records(router_entry, *, metric_labels, translation_table=None)
print(f'Error getting interface traffic stats info from router {router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}')
return None


class InterfaceMonitorMetricsDataSource:
''' Interface Monitor Metrics data provider
'''
""" Interface Monitor Metrics data provider
"""
@staticmethod
def metric_records(router_entry, *, metric_labels = None, translation_table = None, kind = 'ethernet', include_comments = False, running_only = True):
if metric_labels is None:
Expand Down Expand Up @@ -121,4 +131,3 @@ def metric_records(router_entry, *, metric_labels = None, translation_table = No
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

0 comments on commit 5d821f7

Please sign in to comment.