Skip to content

Commit 069cec9

Browse files
committed
add DHCP ip_pool_total metric, #99
1 parent aaddf61 commit 069cec9

3 files changed

Lines changed: 75 additions & 8 deletions

File tree

mktxp/collector/pool_collector.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
class PoolCollector(BaseCollector):
2121
''' IP Pool Metrics collector
22-
'''
22+
'''
2323
@staticmethod
2424
def collect(router_entry):
2525
# ~*~*~*~*~*~ IPv4 ~*~*~*~*~*~
@@ -36,21 +36,60 @@ def _process_ip_stack(router_entry, ipv6=False):
3636
ip_stack = 'ipv6' if ipv6 else 'ipv4'
3737

3838
# initialize all pool counts, including those currently not used
39-
pool_records = PoolMetricsDataSource.metric_records(router_entry, metric_labels = ['name'], ipv6=ipv6)
39+
pool_records = PoolMetricsDataSource.metric_records(router_entry, metric_labels = ['name', 'ranges', 'total'], ipv6=ipv6)
4040
if pool_records:
4141
pool_used_labels = ['pool']
4242
pool_used_counts = {pool_record['name']: 0 for pool_record in pool_records}
43+
pool_size_counts = {pool_record['name']: int(pool_record.get('total', PoolCollector._calculate_pool_size(pool_record.get('ranges', '')))) for pool_record in pool_records}
4344

4445
# for pools in usage, calculate the current numbers
45-
pool_used_records = PoolUsedMetricsDataSource.metric_records(router_entry, metric_labels = pool_used_labels, ipv6=ipv6)
46+
pool_used_records = PoolUsedMetricsDataSource.metric_records(router_entry, metric_labels = pool_used_labels, ipv6=ipv6)
4647
for pool_used_record in pool_used_records:
4748
pool_used_counts[pool_used_record['pool']] = pool_used_counts.get(pool_used_record['pool'], 0) + 1
4849

4950
# compile used-per-pool records
5051
used_per_pool_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME],
5152
MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS],
5253
'pool': key, 'count': value} for key, value in pool_used_counts.items()]
53-
54+
5455
# yield used-per-pool metrics
5556
used_per_pool_metrics = BaseCollector.gauge_collector(f'ip_pool_used{"_ipv6" if ipv6 else ""}', f'Number of used addresses per IP pool ({ip_stack.upper()})', used_per_pool_records, 'count', ['pool'])
5657
yield used_per_pool_metrics
58+
59+
# compile total-per-pool records
60+
total_per_pool_records = [{ MKTXPConfigKeys.ROUTERBOARD_NAME: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_NAME],
61+
MKTXPConfigKeys.ROUTERBOARD_ADDRESS: router_entry.router_id[MKTXPConfigKeys.ROUTERBOARD_ADDRESS],
62+
'pool': key, 'count': value} for key, value in pool_size_counts.items()]
63+
64+
# yield total-per-pool metrics
65+
total_per_pool_metrics = BaseCollector.gauge_collector(f'ip_pool_total{"_ipv6" if ipv6 else ""}', f'Total capacity of IP pool ({ip_stack.upper()})', total_per_pool_records, 'count', ['pool'])
66+
yield total_per_pool_metrics
67+
68+
@staticmethod
69+
def _calculate_pool_size(ranges_str):
70+
import ipaddress
71+
total = 0
72+
if not ranges_str:
73+
return total
74+
for r in ranges_str.split(','):
75+
r = r.strip()
76+
if not r:
77+
continue
78+
if '-' in r:
79+
start, end = r.split('-', 1)
80+
try:
81+
total += int(ipaddress.ip_address(end.strip())) - int(ipaddress.ip_address(start.strip())) + 1
82+
except Exception:
83+
pass
84+
elif '/' in r:
85+
try:
86+
total += ipaddress.ip_network(r, strict=False).num_addresses
87+
except Exception:
88+
pass
89+
else:
90+
try:
91+
ipaddress.ip_address(r)
92+
total += 1
93+
except Exception:
94+
pass
95+
return total

mktxp/datasource/pool_ds.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717

1818
class PoolMetricsDataSource:
1919
''' Pool Metrics data provider
20-
'''
20+
'''
2121
@staticmethod
2222
def metric_records(router_entry, *, metric_labels = None, ipv6 = False):
2323
ip_stack = 'ipv6' if ipv6 else 'ip'
2424
if metric_labels is None:
25-
metric_labels = []
25+
metric_labels = []
2626
try:
2727
pool_records = router_entry.api_connection.router_api().get_resource(f'/{ip_stack}/pool').get()
2828
return BaseDSProcessor.trimmed_records(router_entry, router_records = pool_records, metric_labels = metric_labels)
@@ -33,12 +33,12 @@ def metric_records(router_entry, *, metric_labels = None, ipv6 = False):
3333

3434
class PoolUsedMetricsDataSource:
3535
''' Pool/Used Metrics data provider
36-
'''
36+
'''
3737
@staticmethod
3838
def metric_records(router_entry, *, metric_labels = None, ipv6 = False):
3939
ip_stack = 'ipv6' if ipv6 else 'ip'
4040
if metric_labels is None:
41-
metric_labels = []
41+
metric_labels = []
4242
try:
4343
pool_used_records = router_entry.api_connection.router_api().get_resource(f'/{ip_stack}/pool/used').get()
4444
return BaseDSProcessor.trimmed_records(router_entry, router_records = pool_used_records, metric_labels = metric_labels)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import pytest
2+
from mktxp.collector.pool_collector import PoolCollector
3+
4+
def test_calculate_pool_size_empty():
5+
assert PoolCollector._calculate_pool_size('') == 0
6+
assert PoolCollector._calculate_pool_size(None) == 0
7+
8+
def test_calculate_pool_size_single_range():
9+
# 10.20.10.10 to 10.20.10.254 is exactly 245 IPs
10+
assert PoolCollector._calculate_pool_size('10.20.10.10-10.20.10.254') == 245
11+
12+
def test_calculate_pool_size_multiple_ranges():
13+
assert PoolCollector._calculate_pool_size('192.168.1.10-192.168.1.19, 192.168.1.100-192.168.1.109') == 20
14+
15+
def test_calculate_pool_size_cidr_ipv4():
16+
# /24 has 256 addresses
17+
assert PoolCollector._calculate_pool_size('192.168.1.0/24') == 256
18+
19+
def test_calculate_pool_size_cidr_ipv6():
20+
# /127 has 2 addresses
21+
assert PoolCollector._calculate_pool_size('2001:db8::/127') == 2
22+
23+
def test_calculate_pool_size_single_ip():
24+
assert PoolCollector._calculate_pool_size('192.168.1.100') == 1
25+
26+
def test_calculate_pool_size_invalid_input():
27+
# Should ignore invalid chunks safely
28+
assert PoolCollector._calculate_pool_size('192.168.1.10-192.168.1.20, invalid-range') == 11

0 commit comments

Comments
 (0)