Skip to content

Commit e8ba94e

Browse files
committed
fix(statuspal): replace flatdict with recursive approach (fix #1044)
1 parent 1503cb9 commit e8ba94e

2 files changed

Lines changed: 38 additions & 59 deletions

File tree

check-plugins/statuspal/statuspal

Lines changed: 32 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,8 @@ import lib.url # pylint: disable=C0413
2424
from lib.globals import (STATE_CRIT, STATE_OK, # pylint: disable=C0413
2525
STATE_UNKNOWN, STATE_WARN)
2626

27-
try:
28-
import flatdict # pylint: disable=C0413
29-
except ImportError as e:
30-
print('Python module "flatdict" is not installed.')
31-
sys.exit(STATE_UNKNOWN)
32-
33-
3427
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
35-
__version__ = '2025101701'
28+
__version__ = '2026033101'
3629

3730
DESCRIPTION = """Statuspal is a status page provider from Germany. This check plugin gets
3831
the summary of a Statuspal status page, checks its status, services,
@@ -110,37 +103,28 @@ def parse_args():
110103
return args
111104

112105

113-
def concat_values(mydict, hierarchy):
114-
"""Concat the values of the same hierarchy level in a dict.
115-
116-
>>> mydict = {
117-
'services:0:name': 'Global',
118-
'services:0:description': 'Lorem ipsum',
119-
'services:0:children:0:name': 'DNS'
120-
'services:0:children:0:id': '4711'
121-
'services:0:children:0:children:1:name': 'Server01'
122-
}
123-
>>> concat_values(mydict, 'services:0:name')
124-
>>> 'Global'
125-
>>> concat_values(mydict, 'services:0:children:0:name')
126-
>>> 'Global.DNS'
127-
>>> concat_values(mydict, 'services:0:children:0:children:1:name')
128-
>>> 'Global.DNS.Server01'
106+
def flatten_services(services, parent_name=''):
107+
"""Recursively flatten the services tree into a list of dicts with
108+
'name' (dotted hierarchy) and 'current_incident_type'.
109+
110+
>>> services = [{'name': 'Global', 'current_incident_type': None, 'children': [
111+
... {'name': 'DNS', 'current_incident_type': None, 'children': []}
112+
... ]}]
113+
>>> flatten_services(services)
114+
[{'name': 'Global', 'current_incident_type': None},
115+
{'name': 'Global.DNS', 'current_incident_type': None}]
129116
"""
130-
result = ''
131-
hierarchy = hierarchy.split(':')
132-
for i, item in enumerate(hierarchy): # pylint: disable=W0612
133-
# testing
134-
# * services:name
135-
# * services:0:name
136-
# * services:0:children:name
137-
# * services:0:children:0:name
138-
# etc.
139-
key = ':'.join(hierarchy[0:i+1]) + ':' + hierarchy[-1]
140-
if key in mydict:
141-
result += mydict[key] + '.'
142-
if result.endswith('.'):
143-
return result[:-1]
117+
result = []
118+
for service in services:
119+
if not service.get('name'):
120+
continue
121+
full_name = '{}.{}'.format(parent_name, service['name']) if parent_name else service['name']
122+
result.append({
123+
'name': full_name,
124+
'current_incident_type': service.get('current_incident_type'),
125+
})
126+
for child in service.get('children') or []:
127+
result.extend(flatten_services([child], full_name))
144128
return result
145129

146130

@@ -227,21 +211,16 @@ def main():
227211
msg += ' (see {})'.format(result['incidents'][0]['url']) if result['incidents'][0]['url'] else '' # pylint: disable=C0301
228212

229213
# services - search for any incidents in services
230-
flattened_result = flatdict.FlatterDict(result['services'])
231-
item = {}
232-
for key, value in flattened_result.items():
233-
if key.endswith(':name'):
234-
item['name'] = concat_values(flattened_result, key)
235-
if key.endswith(':current_incident_type'):
236-
item['state'] = statuspalstate2state(value)
237-
if item['state'] == STATE_WARN:
238-
cnt_warn += 1
239-
if item['state'] == STATE_CRIT:
240-
cnt_crit += 1
241-
item['state'] = lib.base.state2str(item['state'], empty_ok=False)
242-
if len(item) == 2:
243-
table_data.append(item)
244-
item = {}
214+
for service in flatten_services(result['services']):
215+
service_state = statuspalstate2state(service['current_incident_type'])
216+
if service_state == STATE_WARN:
217+
cnt_warn += 1
218+
if service_state == STATE_CRIT:
219+
cnt_crit += 1
220+
table_data.append({
221+
'name': service['name'],
222+
'state': lib.base.state2str(service_state, empty_ok=False),
223+
})
245224
if table_data:
246225
msg += '\n\n'
247226
msg += lib.base.get_table(

check-plugins/statuspal/unit-test/run

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,10 @@ class TestCheck(unittest.TestCase):
138138
self.assertIn('PFM.PFM - API Production ! [OK] ', stdout)
139139
self.assertIn('PFM.PFM - API Integration ! [OK] ', stdout)
140140
self.assertIn('Vision ! [OK] ', stdout)
141-
self.assertIn('PEAK ! [OK] ', stdout)
141+
self.assertIn('PEAK ! [CRITICAL]', stdout)
142142
self.assertIn('PEAK.PEAK - Customer API Integration ! [CRITICAL]', stdout)
143143
self.assertIn('PEAK.PEAK - Customer API Production ! [CRITICAL]', stdout)
144-
self.assertIn('Global IT Monitoring ! [CRITICAL]', stdout)
144+
self.assertIn('Global IT Monitoring ! [OK]', stdout)
145145
self.assertEqual(stderr, '')
146146
self.assertEqual(retc, STATE_CRIT)
147147

@@ -267,7 +267,7 @@ class TestCheck(unittest.TestCase):
267267
self.assertIn('CH-GVA-2.Network Internet Transit Connectivity ! [OK] ', stdout)
268268
self.assertIn('CH-GVA-2.Network Load Balancer NLB ! [OK] ', stdout)
269269
self.assertIn('CH-GVA-2.Object Storage SOS ! [OK] ', stdout)
270-
self.assertIn('CH-DK-2 ! [OK] ', stdout)
270+
self.assertIn('CH-DK-2 ! [WARNING]', stdout)
271271
self.assertIn('CH-DK-2.API ! [OK] ', stdout)
272272
self.assertIn('CH-DK-2.Block Storage ! [OK] ', stdout)
273273
self.assertIn('CH-DK-2.Compute ! [OK] ', stdout)
@@ -277,7 +277,7 @@ class TestCheck(unittest.TestCase):
277277
self.assertIn('CH-DK-2.Network Internet Transit Connectivity ! [WARNING]', stdout)
278278
self.assertIn('CH-DK-2.Network Load Balancer NLB ! [OK] ', stdout)
279279
self.assertIn('CH-DK-2.Object Storage SOS ! [OK] ', stdout)
280-
self.assertIn('DE-FRA-1 ! [WARNING]', stdout)
280+
self.assertIn('DE-FRA-1 ! [OK] ', stdout)
281281
self.assertIn('DE-FRA-1.API ! [OK] ', stdout)
282282
self.assertIn('DE-FRA-1.Block Storage ! [OK] ', stdout)
283283
self.assertIn('DE-FRA-1.Compute ! [OK] ', stdout)
@@ -396,7 +396,7 @@ class TestCheck(unittest.TestCase):
396396
self.assertIn('AT-VIE-1.Network Internet Transit Connectivity ! [OK] ', stdout)
397397
self.assertIn('AT-VIE-1.Network Load Balancer NLB ! [OK] ', stdout)
398398
self.assertIn('AT-VIE-1.Object Storage SOS ! [OK] ', stdout)
399-
self.assertIn('AT-VIE-2 ! [OK] ', stdout)
399+
self.assertIn('AT-VIE-2 ! [CRITICAL]', stdout)
400400
self.assertIn('AT-VIE-2.API ! [CRITICAL]', stdout)
401401
self.assertIn('AT-VIE-2.Block Storage ! [CRITICAL]', stdout)
402402
self.assertIn('AT-VIE-2.Compute ! [CRITICAL]', stdout)
@@ -406,7 +406,7 @@ class TestCheck(unittest.TestCase):
406406
self.assertIn('AT-VIE-2.Network Internet Transit Connectivity ! [CRITICAL]', stdout)
407407
self.assertIn('AT-VIE-2.Network Load Balancer NLB ! [CRITICAL]', stdout)
408408
self.assertIn('AT-VIE-2.Object Storage SOS ! [CRITICAL]', stdout)
409-
self.assertIn('BG-SOF-1 ! [CRITICAL]', stdout)
409+
self.assertIn('BG-SOF-1 ! [OK] ', stdout)
410410
self.assertIn('BG-SOF-1.API ! [OK] ', stdout)
411411
self.assertIn('BG-SOF-1.Block Storage ! [OK] ', stdout)
412412
self.assertIn('BG-SOF-1.Compute ! [OK] ', stdout)

0 commit comments

Comments
 (0)