Skip to content

Commit 18a6d32

Browse files
committed
finish #306 routerboard inventory collector
1 parent 3690567 commit 18a6d32

9 files changed

Lines changed: 153 additions & 3 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ The default configuration file comes with a sample configuration, making it easy
8383
ssl_ca_file = "" # path to the certificate authority file to validate against, leave empty to use system store
8484
plaintext_login = True # for legacy RouterOS versions below 6.43 use False
8585
86+
routerboard = False # RouterBOARD inventory / firmware metrics
8687
health = True # System Health metrics
8788
installed_packages = True # Installed packages
8889
dhcp = True # DHCP general metrics
@@ -531,6 +532,20 @@ Let's go check on that in the dashboard, or just get the info right from the com
531532
```
532533
*A few quick checks show all of the destination IPs relate to AWS instances, so supposedly it's legit... but let's remain vigilant, to know better :)*
533534

535+
### RouterBOARD inventory and firmware
536+
RouterBOARD inventory and firmware status can be exported with:
537+
538+
```
539+
routerboard = False # RouterBOARD inventory / firmware metrics
540+
```
541+
542+
This enables:
543+
544+
- `mktxp_routerboard_info`
545+
- `mktxp_routerboard_firmware_upgrade_available`
546+
547+
The option is disabled by default. On devices without `/system/routerboard` support, enabling it may result in no data or scrape errors.
548+
534549

535550
### Parallel routers fetch
536551
Concurrent exports across multiple devices can considerably speed up things for slow network connections. This feature can be turned on and configured with the following [system options](https://github.com/akpw/mktxp/blob/main/README.md#mktxp-system-configuration):

deploy/kubernetes/secret.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ stringData:
9090
connection_stats_destinations = False # Set to True to track individual destination IPs/ports (Warning: High Cardinality)
9191
9292
interface = True # Interfaces traffic metrics
93+
routerboard = False # RouterBOARD inventory / firmware metrics
9394
wireguard_peers = False # Wireguard peers metrics
9495
bridge_vlan = False # Bridge VLAN metrics
9596

mktxp/cli/config/config.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
class CollectorKeys:
2828
IDENTITY_COLLECTOR = 'IdentityCollector'
2929
SYSTEM_RESOURCE_COLLECTOR = 'SystemResourceCollector'
30+
ROUTERBOARD_COLLECTOR = 'RouterboardCollector'
3031
HEALTH_COLLECTOR = 'HealthCollector'
3132
PUBLIC_IP_ADDRESS_COLLECTOR = 'PublicIPAddressCollector'
3233
NEIGHBOR_COLLECTOR = 'NeighborCollector'
@@ -84,6 +85,7 @@ class MKTXPConfigKeys:
8485
SSL_CA_FILE = 'ssl_ca_file'
8586
PLAINTEXT_LOGIN_KEY = 'plaintext_login'
8687

88+
FE_ROUTERBOARD_KEY = 'routerboard'
8789
FE_HEALTH_KEY = 'health'
8890
FE_PACKAGE_KEY = 'installed_packages'
8991
FE_DHCP_KEY = 'dhcp'
@@ -213,8 +215,8 @@ class MKTXPConfigKeys:
213215
DEFAULT_MKTXP_PROBE_CONNECTION_POOL_MAX_SIZE = 128
214216

215217

216-
BOOLEAN_KEYS_NO = {ENABLED_KEY, SSL_KEY, NO_SSL_CERTIFICATE, FE_CHECK_FOR_UPDATES, FE_KID_CONTROL_DEVICE, FE_KID_CONTROL_DYNAMIC,FE_WG_PEER_KEY,
217-
SSL_CERTIFICATE_VERIFY, FE_IPV6_ROUTE_KEY, FE_IPV6_DHCP_POOL_KEY, FE_IPV6_FIREWALL_KEY, FE_IPV6_NEIGHBOR_KEY, FE_CONNECTION_STATS_KEY, FE_CONNECTION_STATS_DESTINATIONS_KEY, FE_BFD_KEY, FE_BGP_KEY,
218+
BOOLEAN_KEYS_NO = {ENABLED_KEY, SSL_KEY, NO_SSL_CERTIFICATE, FE_CHECK_FOR_UPDATES, FE_KID_CONTROL_DEVICE, FE_KID_CONTROL_DYNAMIC, FE_WG_PEER_KEY,
219+
FE_ROUTERBOARD_KEY, SSL_CERTIFICATE_VERIFY, FE_IPV6_ROUTE_KEY, FE_IPV6_DHCP_POOL_KEY, FE_IPV6_FIREWALL_KEY, FE_IPV6_NEIGHBOR_KEY, FE_CONNECTION_STATS_KEY, FE_CONNECTION_STATS_DESTINATIONS_KEY, FE_BFD_KEY, FE_BGP_KEY,
218220
FE_EOIP_KEY, FE_GRE_KEY, FE_IPIP_KEY, FE_IPSEC_KEY, FE_LTE_KEY, FE_SWITCH_PORT_KEY, FE_ROUTING_STATS_KEY, FE_CERTIFICATE_KEY, FE_DNS_KEY, FE_CONTAINER_KEY, FE_W60G_KEY, FE_MODULE_ONLY_KEY, FE_BRIDGE_VLAN_KEY}
219221

220222
# Feature keys enabled by default
@@ -247,7 +249,7 @@ class ConfigEntry:
247249
MKTXPConfigEntry = namedtuple('MKTXPConfigEntry', [MKTXPConfigKeys.ENABLED_KEY, MKTXPConfigKeys.HOST_KEY, MKTXPConfigKeys.PORT_KEY,
248250
MKTXPConfigKeys.USER_KEY, MKTXPConfigKeys.PASSWD_KEY, MKTXPConfigKeys.CREDENTIALS_FILE_KEY,
249251
MKTXPConfigKeys.SSL_KEY, MKTXPConfigKeys.NO_SSL_CERTIFICATE, MKTXPConfigKeys.SSL_CERTIFICATE_VERIFY, MKTXPConfigKeys.SSL_CHECK_HOSTNAME, MKTXPConfigKeys.SSL_CA_FILE, MKTXPConfigKeys.PLAINTEXT_LOGIN_KEY,
250-
MKTXPConfigKeys.FE_DHCP_KEY, MKTXPConfigKeys.FE_HEALTH_KEY, MKTXPConfigKeys.FE_PACKAGE_KEY, MKTXPConfigKeys.FE_DHCP_LEASE_KEY, MKTXPConfigKeys.FE_INTERFACE_KEY,MKTXPConfigKeys.FE_WG_PEER_KEY,
252+
MKTXPConfigKeys.FE_DHCP_KEY, MKTXPConfigKeys.FE_ROUTERBOARD_KEY, MKTXPConfigKeys.FE_HEALTH_KEY, MKTXPConfigKeys.FE_PACKAGE_KEY, MKTXPConfigKeys.FE_DHCP_LEASE_KEY, MKTXPConfigKeys.FE_INTERFACE_KEY, MKTXPConfigKeys.FE_WG_PEER_KEY,
251253
MKTXPConfigKeys.FE_MONITOR_KEY, MKTXPConfigKeys.FE_W60G_KEY, MKTXPConfigKeys.FE_WIRELESS_KEY, MKTXPConfigKeys.FE_WIRELESS_CLIENTS_KEY,
252254
MKTXPConfigKeys.FE_IP_CONNECTIONS_KEY, MKTXPConfigKeys.FE_CONNECTION_STATS_KEY, MKTXPConfigKeys.FE_CONNECTION_STATS_DESTINATIONS_KEY, MKTXPConfigKeys.FE_CAPSMAN_KEY, MKTXPConfigKeys.FE_CAPSMAN_CLIENTS_KEY, MKTXPConfigKeys.FE_POE_KEY,
253255
MKTXPConfigKeys.FE_NETWATCH_KEY, MKTXPConfigKeys.FE_INTERFACE_NAME_FORMAT, MKTXPConfigKeys.FE_PUBLIC_IP_KEY,

mktxp/cli/config/mktxp.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
connection_stats_destinations = False # Set to True to track individual destination IPs/ports (Warning: High Cardinality)
4747

4848
interface = True # Interfaces traffic metrics
49+
routerboard = False # RouterBOARD inventory / firmware metrics
4950
wireguard_peers = False # Wireguard peers metrics
5051
bridge_vlan = False # Bridge VLAN metrics
5152

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# coding=utf8
2+
## Copyright (c) 2020 Arseniy Kuznetsov
3+
##
4+
## This program is free software; you can redistribute it and/or
5+
## modify it under the terms of the GNU General Public License
6+
## as published by the Free Software Foundation; either version 2
7+
## of the License, or (at your option) any later version.
8+
##
9+
## This program is distributed in the hope that it will be useful,
10+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
## GNU General Public License for more details.
13+
14+
from mktxp.collector.base_collector import BaseCollector
15+
from mktxp.datasource.routerboard_ds import RouterboardMetricsDataSource
16+
17+
18+
class RouterboardCollector(BaseCollector):
19+
"""RouterBOARD inventory + firmware metrics collector"""
20+
21+
@staticmethod
22+
def collect(router_entry):
23+
if not router_entry.config_entry.routerboard:
24+
return
25+
26+
rb_labels = [
27+
"routerboard",
28+
"model",
29+
"serial_number",
30+
"firmware_type",
31+
"factory_firmware",
32+
"current_firmware",
33+
"upgrade_firmware",
34+
]
35+
36+
rb_records = RouterboardMetricsDataSource.metric_records(router_entry, metric_labels=rb_labels)
37+
if not rb_records:
38+
return
39+
40+
yield BaseCollector.info_collector(
41+
"routerboard",
42+
"RouterBOARD inventory and firmware information",
43+
rb_records,
44+
rb_labels,
45+
)
46+
47+
for r in rb_records:
48+
cur = r.get("current_firmware")
49+
upg = r.get("upgrade_firmware")
50+
r["firmware_upgrade_available"] = 1 if (cur and upg and cur != upg) else 0
51+
52+
yield BaseCollector.gauge_collector(
53+
"routerboard_firmware_upgrade_available",
54+
"Whether RouterBOARD firmware upgrade is available (current_firmware != upgrade_firmware)",
55+
rb_records,
56+
"firmware_upgrade_available",
57+
["current_firmware", "upgrade_firmware"],
58+
)

mktxp/flow/collector_registry.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from mktxp.collector.certificate_collector import CertificateCollector
5454
from mktxp.collector.dns_collector import DNSCollector
5555
from mktxp.collector.container_collector import ContainerCollector
56+
from mktxp.collector.routerboard_collector import RouterboardCollector
5657

5758
class CollectorRegistry:
5859
''' MKTXP Collectors Registry
@@ -65,6 +66,7 @@ def __init__(self):
6566

6667
self.register(CollectorKeys.IDENTITY_COLLECTOR, IdentityCollector.collect)
6768
self.register(CollectorKeys.SYSTEM_RESOURCE_COLLECTOR, SystemResourceCollector.collect)
69+
self.register(CollectorKeys.ROUTERBOARD_COLLECTOR, RouterboardCollector.collect)
6870
self.register(CollectorKeys.HEALTH_COLLECTOR, HealthCollector.collect)
6971
self.register(CollectorKeys.PUBLIC_IP_ADDRESS_COLLECTOR, PublicIPAddressCollector.collect)
7072

mktxp/flow/router_entry.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def __init__(self, router_name):
5252

5353
self.time_spent = { CollectorKeys.IDENTITY_COLLECTOR: 0,
5454
CollectorKeys.SYSTEM_RESOURCE_COLLECTOR: 0,
55+
CollectorKeys.ROUTERBOARD_COLLECTOR: 0,
5556
CollectorKeys.HEALTH_COLLECTOR: 0,
5657
CollectorKeys.PUBLIC_IP_ADDRESS_COLLECTOR: 0,
5758
CollectorKeys.NEIGHBOR_COLLECTOR: 0,

tests/cli/config/test_config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,7 @@ def test_system_config_key_in_new_section(tmpdir):
161161
def test_wireguard_peer_key_registration():
162162
assert MKTXPConfigKeys.FE_WG_PEER_KEY == 'wireguard_peers'
163163
assert MKTXPConfigKeys.FE_WG_PEER_KEY in MKTXPConfigKeys.BOOLEAN_KEYS_NO
164+
165+
def test_routerboard_key_registration():
166+
assert MKTXPConfigKeys.FE_ROUTERBOARD_KEY == 'routerboard'
167+
assert MKTXPConfigKeys.FE_ROUTERBOARD_KEY in MKTXPConfigKeys.BOOLEAN_KEYS_NO
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# coding=utf8
2+
## Copyright (c) 2020 Arseniy Kuznetsov
3+
##
4+
## This program is free software; you can redistribute it and/or
5+
## modify it under the terms of the GNU General Public License
6+
## as published by the Free Software Foundation; either version 2
7+
## of the License, or (at your option) any later version.
8+
##
9+
## This program is distributed in the hope that it will be useful,
10+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
## GNU General Public License for more details.
13+
14+
from unittest.mock import Mock, patch
15+
16+
from mktxp.collector.routerboard_collector import RouterboardCollector
17+
18+
19+
def test_routerboard_collector_yields_inventory_and_upgrade_metrics():
20+
mock_router_entry = Mock()
21+
mock_router_entry.config_entry.routerboard = True
22+
23+
mock_records = [{
24+
'routerboard': 'true',
25+
'model': 'RB5009',
26+
'serial_number': 'ABC123',
27+
'firmware_type': 'routerboot',
28+
'factory_firmware': '7.12',
29+
'current_firmware': '7.14',
30+
'upgrade_firmware': '7.16',
31+
}]
32+
33+
with patch('mktxp.collector.routerboard_collector.RouterboardMetricsDataSource.metric_records') as mock_ds, \
34+
patch('mktxp.collector.base_collector.BaseCollector.info_collector') as mock_info, \
35+
patch('mktxp.collector.base_collector.BaseCollector.gauge_collector') as mock_gauge:
36+
mock_ds.return_value = mock_records
37+
mock_info.return_value = 'routerboard-info'
38+
mock_gauge.return_value = 'routerboard-upgrade'
39+
40+
results = list(RouterboardCollector.collect(mock_router_entry))
41+
42+
mock_ds.assert_called_once_with(
43+
mock_router_entry,
44+
metric_labels=[
45+
'routerboard',
46+
'model',
47+
'serial_number',
48+
'firmware_type',
49+
'factory_firmware',
50+
'current_firmware',
51+
'upgrade_firmware',
52+
]
53+
)
54+
assert mock_records[0]['firmware_upgrade_available'] == 1
55+
assert results == ['routerboard-info', 'routerboard-upgrade']
56+
57+
58+
def test_routerboard_collector_skips_when_disabled():
59+
mock_router_entry = Mock()
60+
mock_router_entry.config_entry.routerboard = False
61+
62+
with patch('mktxp.collector.routerboard_collector.RouterboardMetricsDataSource.metric_records') as mock_ds:
63+
results = list(RouterboardCollector.collect(mock_router_entry))
64+
65+
mock_ds.assert_not_called()
66+
assert results == []

0 commit comments

Comments
 (0)