Skip to content

Commit b51ae1f

Browse files
committed
fix(redfish-*): surface missing or broken --test fixtures as STATE_UNKNOWN instead of a traceback
Wrap the json.loads(stdout) calls in the --test walk behind a load_test_fixture() helper that checks for the fixture file and catches JSONDecodeError, exiting with a readable STATE_UNKNOWN message via lib.base.cu(). Add a regression test per plugin that exercises the missing-fixture path.
1 parent 9fa418a commit b51ae1f

8 files changed

Lines changed: 125 additions & 54 deletions

File tree

check-plugins/redfish-drives/redfish-drives

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import argparse
1414
import base64
1515
import json
16+
import os
1617
import sys
1718

1819
import lib.args
@@ -24,7 +25,7 @@ import lib.url
2425
from lib.globals import STATE_CRIT, STATE_OK, STATE_UNKNOWN, STATE_WARN
2526

2627
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
27-
__version__ = '2026042303'
28+
__version__ = '2026042401'
2829

2930
DESCRIPTION = """Checks the state of all physical drives and their storage controllers in a
3031
Redfish-compatible server via the Redfish API. Alerts when any drive or storage controller
@@ -112,6 +113,21 @@ def parse_args():
112113
return args
113114

114115

116+
def load_test_fixture(test_args, path):
117+
# Replace the first element of args.TEST with the walk-specific
118+
# fixture path, read it via lib.lftest.test() and return the parsed
119+
# JSON. On a missing file or malformed JSON, exit STATE_UNKNOWN with
120+
# a helpful message instead of letting json.loads raise a traceback.
121+
if not os.path.isfile(path):
122+
lib.base.cu(f'Test fixture not found: "{path}".')
123+
test_args[0] = path
124+
stdout, _, _ = lib.lftest.test(test_args)
125+
try:
126+
return json.loads(stdout)
127+
except (json.JSONDecodeError, ValueError) as e:
128+
lib.base.cu(f'Test fixture "{path}" does not contain valid JSON: {e}')
129+
130+
115131
def main():
116132
"""The main function. This is where the magic happens."""
117133

@@ -146,9 +162,7 @@ def main():
146162
# file names describe what they contain (systems, system,
147163
# storages, storage, drive-N).
148164
test_base = args.TEST[0]
149-
args.TEST[0] = f'{test_base}-systems'
150-
stdout, _, _ = lib.lftest.test(args.TEST)
151-
result = json.loads(stdout)
165+
result = load_test_fixture(args.TEST, f'{test_base}-systems')
152166
# "Members": [
153167
# {
154168
# "@odata.id": "/redfish/v1/Systems/437XR1138R2"
@@ -179,9 +193,7 @@ def main():
179193
)
180194
)
181195
else:
182-
args.TEST[0] = f'{test_base}-system'
183-
stdout, _, _ = lib.lftest.test(args.TEST)
184-
systems = json.loads(stdout)
196+
systems = load_test_fixture(args.TEST, f'{test_base}-system')
185197
systems = lib.redfish.get_systems(systems)
186198
if systems['Status_State'] not in ['Enabled', 'Quiesced']:
187199
continue
@@ -217,9 +229,7 @@ def main():
217229
)
218230
)
219231
else:
220-
args.TEST[0] = f'{test_base}-storages'
221-
stdout, _, _ = lib.lftest.test(args.TEST)
222-
storages = json.loads(stdout)
232+
storages = load_test_fixture(args.TEST, f'{test_base}-storages')
223233
table_data = []
224234
table_data_drive = []
225235
for storage in storages.get('Members', []):
@@ -235,9 +245,7 @@ def main():
235245
)
236246
)
237247
else:
238-
args.TEST[0] = f'{test_base}-storage'
239-
stdout, _, _ = lib.lftest.test(args.TEST)
240-
storage_data = json.loads(stdout)
248+
storage_data = load_test_fixture(args.TEST, f'{test_base}-storage')
241249

242250
# get drives attached to the storage member
243251
for drive_idx, drive in enumerate(storage_data.get('Drives', [])):
@@ -253,9 +261,10 @@ def main():
253261
)
254262
)
255263
else:
256-
args.TEST[0] = f'{test_base}-drive-{drive_idx}'
257-
stdout, _, _ = lib.lftest.test(args.TEST)
258-
drive_data = json.loads(stdout)
264+
drive_data = load_test_fixture(
265+
args.TEST,
266+
f'{test_base}-drive-{drive_idx}',
267+
)
259268
drive_data = lib.redfish.get_systems_storage_drives(drive_data)
260269
if drive_data['Status_State'] not in ['Enabled', 'Quiesced']:
261270
continue

check-plugins/redfish-drives/unit-test/run

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ sys.path.insert(0, '..')
1515
import unittest
1616

1717
import lib.lftest
18-
from lib.globals import STATE_CRIT, STATE_OK, STATE_WARN
18+
from lib.globals import STATE_CRIT, STATE_OK, STATE_UNKNOWN, STATE_WARN
1919

2020
# The plugin appends `-systems` (Systems collection), `-system` (member),
2121
# `-storages` (Storage collection), `-storage` (Storage member), and
@@ -52,6 +52,14 @@ TESTS = [
5252
'[CRITICAL]',
5353
],
5454
},
55+
{
56+
# A missing fixture must surface as STATE_UNKNOWN with a
57+
# readable message, not as a Python traceback.
58+
'id': 'unknown-missing-fixture',
59+
'test': 'stdout/does-not-exist,,0',
60+
'assert-retc': STATE_UNKNOWN,
61+
'assert-in': ['Test fixture not found'],
62+
},
5563
]
5664

5765

check-plugins/redfish-sel/redfish-sel

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import argparse
1414
import base64
1515
import json
16+
import os
1617
import sys
1718

1819
import lib.args
@@ -24,7 +25,7 @@ import lib.url
2425
from lib.globals import STATE_CRIT, STATE_OK, STATE_UNKNOWN, STATE_WARN
2526

2627
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
27-
__version__ = '2026042302'
28+
__version__ = '2026042401'
2829

2930
DESCRIPTION = """Checks the System Event Log (SEL) of Redfish-compatible servers via the Redfish API.
3031
Alerts based on the severity of log entries. Entries can be filtered by regex."""
@@ -109,6 +110,21 @@ def parse_args():
109110
return args
110111

111112

113+
def load_test_fixture(test_args, path):
114+
# Replace the first element of args.TEST with the walk-specific
115+
# fixture path, read it via lib.lftest.test() and return the parsed
116+
# JSON. On a missing file or malformed JSON, exit STATE_UNKNOWN with
117+
# a helpful message instead of letting json.loads raise a traceback.
118+
if not os.path.isfile(path):
119+
lib.base.cu(f'Test fixture not found: "{path}".')
120+
test_args[0] = path
121+
stdout, _, _ = lib.lftest.test(test_args)
122+
try:
123+
return json.loads(stdout)
124+
except (json.JSONDecodeError, ValueError) as e:
125+
lib.base.cu(f'Test fixture "{path}" does not contain valid JSON: {e}')
126+
127+
112128
def main():
113129
"""The main function. This is where the magic happens."""
114130

@@ -155,14 +171,9 @@ def main():
155171
# file names describe what they contain (root, managers, sel)
156172
# instead of being a string-appended chain.
157173
test_base = args.TEST[0]
158-
args.TEST[0] = f'{test_base}-root'
159-
stdout, _, _ = lib.lftest.test(args.TEST)
160-
result = json.loads(stdout)
174+
result = load_test_fixture(args.TEST, f'{test_base}-root')
161175
vendor = lib.redfish.get_vendor(result)
162-
163-
args.TEST[0] = f'{test_base}-managers'
164-
stdout, _, _ = lib.lftest.test(args.TEST)
165-
result = json.loads(stdout)
176+
result = load_test_fixture(args.TEST, f'{test_base}-managers')
166177

167178
# "Members": [
168179
# {
@@ -217,9 +228,7 @@ def main():
217228
)
218229
)
219230
else:
220-
args.TEST[0] = f'{test_base}-sel'
221-
stdout, _, _ = lib.lftest.test(args.TEST)
222-
sel = json.loads(stdout)
231+
sel = load_test_fixture(args.TEST, f'{test_base}-sel')
223232
member_msg, member_state = lib.redfish.get_manager_logservices_sel_entries(sel)
224233
if member_msg:
225234
# build the message

check-plugins/redfish-sel/unit-test/run

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ sys.path.insert(0, '..')
1515
import unittest
1616

1717
import lib.lftest
18-
from lib.globals import STATE_OK
18+
from lib.globals import STATE_OK, STATE_UNKNOWN
1919

2020
# The plugin appends `-v1`, `-managers`, `-vendor` to the --test path
2121
# as it walks the Redfish API, so each scenario requires three fixture
@@ -27,6 +27,14 @@ TESTS = [
2727
'assert-retc': STATE_OK,
2828
'assert-in': ['Everything is ok, checked SEL on 1 member.'],
2929
},
30+
{
31+
# A missing fixture must surface as STATE_UNKNOWN with a
32+
# readable message, not as a Python traceback.
33+
'id': 'unknown-missing-fixture',
34+
'test': 'stdout/does-not-exist,,0',
35+
'assert-retc': STATE_UNKNOWN,
36+
'assert-in': ['Test fixture not found'],
37+
},
3038
]
3139

3240

check-plugins/redfish-sensor/redfish-sensor

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import argparse
1414
import base64
1515
import json
16+
import os
1617
import sys
1718

1819
import lib.args
@@ -24,7 +25,7 @@ import lib.url
2425
from lib.globals import STATE_CRIT, STATE_OK, STATE_UNKNOWN, STATE_WARN
2526

2627
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
27-
__version__ = '2026042302'
28+
__version__ = '2026042401'
2829

2930
DESCRIPTION = """Checks hardware sensor readings (temperature, voltage, fan speed, power) from the
3031
Redfish Chassis collection via the Redfish API. Alerts when any sensor reports a
@@ -110,6 +111,21 @@ def parse_args():
110111
return args
111112

112113

114+
def load_test_fixture(test_args, path):
115+
# Replace the first element of args.TEST with the walk-specific
116+
# fixture path, read it via lib.lftest.test() and return the parsed
117+
# JSON. On a missing file or malformed JSON, exit STATE_UNKNOWN with
118+
# a helpful message instead of letting json.loads raise a traceback.
119+
if not os.path.isfile(path):
120+
lib.base.cu(f'Test fixture not found: "{path}".')
121+
test_args[0] = path
122+
stdout, _, _ = lib.lftest.test(test_args)
123+
try:
124+
return json.loads(stdout)
125+
except (json.JSONDecodeError, ValueError) as e:
126+
lib.base.cu(f'Test fixture "{path}" does not contain valid JSON: {e}')
127+
128+
113129
def main():
114130
"""The main function. This is where the magic happens."""
115131

@@ -144,9 +160,7 @@ def main():
144160
# file names describe what they contain (chassises, chassis,
145161
# sensors, sensor-N, thermal).
146162
test_base = args.TEST[0]
147-
args.TEST[0] = f'{test_base}-chassises'
148-
stdout, _, _ = lib.lftest.test(args.TEST)
149-
result = json.loads(stdout)
163+
result = load_test_fixture(args.TEST, f'{test_base}-chassises')
150164
# "Members": [
151165
# {
152166
# "@odata.id": "/redfish/v1/Chassis/1U"
@@ -176,9 +190,7 @@ def main():
176190
)
177191
)
178192
else:
179-
args.TEST[0] = f'{test_base}-chassis'
180-
stdout, _, _ = lib.lftest.test(args.TEST)
181-
chassis = json.loads(stdout)
193+
chassis = load_test_fixture(args.TEST, f'{test_base}-chassis')
182194
chassis = lib.redfish.get_chassis(chassis)
183195
if chassis['Status_State'] not in ['Enabled', 'Quiesced']:
184196
continue
@@ -214,9 +226,7 @@ def main():
214226
)
215227
)
216228
else:
217-
args.TEST[0] = f'{test_base}-sensors'
218-
stdout, _, _ = lib.lftest.test(args.TEST)
219-
sensors = json.loads(stdout)
229+
sensors = load_test_fixture(args.TEST, f'{test_base}-sensors')
220230
table_data = []
221231
for sensor_idx, sensor in enumerate(sensors.get('Members', [])):
222232
if args.TEST is None:
@@ -230,9 +240,10 @@ def main():
230240
)
231241
)
232242
else:
233-
args.TEST[0] = f'{test_base}-sensor-{sensor_idx}'
234-
stdout, _, _ = lib.lftest.test(args.TEST)
235-
sensor_data = json.loads(stdout)
243+
sensor_data = load_test_fixture(
244+
args.TEST,
245+
f'{test_base}-sensor-{sensor_idx}',
246+
)
236247
sensor_data = lib.redfish.get_chassis_sensors(sensor_data)
237248
if sensor_data['Status_State'] not in ['Enabled', 'Quiesced']:
238249
continue
@@ -274,9 +285,7 @@ def main():
274285
)
275286
)
276287
else:
277-
args.TEST[0] = f'{test_base}-thermal'
278-
stdout, _, _ = lib.lftest.test(args.TEST)
279-
thermal = json.loads(stdout)
288+
thermal = load_test_fixture(args.TEST, f'{test_base}-thermal')
280289

281290
# redundancy
282291
table_data = []

check-plugins/redfish-sensor/unit-test/run

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ sys.path.insert(0, '..')
1515
import unittest
1616

1717
import lib.lftest
18-
from lib.globals import STATE_OK
18+
from lib.globals import STATE_OK, STATE_UNKNOWN
1919

2020
# The plugin appends `-chassises` (Chassis collection), `-chassis` (member),
2121
# `-sensors` (Sensors collection), `-sensor-N` (each Sensor, indexed) and
@@ -29,6 +29,14 @@ TESTS = [
2929
'Everything is ok, checked sensors on 1 member.',
3030
],
3131
},
32+
{
33+
# A missing fixture must surface as STATE_UNKNOWN with a
34+
# readable message, not as a Python traceback.
35+
'id': 'unknown-missing-fixture',
36+
'test': 'stdout/does-not-exist,,0',
37+
'assert-retc': STATE_UNKNOWN,
38+
'assert-in': ['Test fixture not found'],
39+
},
3240
]
3341

3442

check-plugins/redfish-system/redfish-system

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import argparse
1414
import base64
1515
import json
16+
import os
1617
import sys
1718

1819
import lib.args
@@ -24,7 +25,7 @@ import lib.url
2425
from lib.globals import STATE_CRIT, STATE_OK, STATE_UNKNOWN, STATE_WARN
2526

2627
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
27-
__version__ = '2026042302'
28+
__version__ = '2026042401'
2829

2930
DESCRIPTION = """Checks the overall system health reported by a Redfish-compatible server via the
3031
Redfish API. Reports every enabled system member with its identification (manufacturer, model,
@@ -112,6 +113,21 @@ def parse_args():
112113
return args
113114

114115

116+
def load_test_fixture(test_args, path):
117+
# Replace the first element of args.TEST with the walk-specific
118+
# fixture path, read it via lib.lftest.test() and return the parsed
119+
# JSON. On a missing file or malformed JSON, exit STATE_UNKNOWN with
120+
# a helpful message instead of letting json.loads raise a traceback.
121+
if not os.path.isfile(path):
122+
lib.base.cu(f'Test fixture not found: "{path}".')
123+
test_args[0] = path
124+
stdout, _, _ = lib.lftest.test(test_args)
125+
try:
126+
return json.loads(stdout)
127+
except (json.JSONDecodeError, ValueError) as e:
128+
lib.base.cu(f'Test fixture "{path}" does not contain valid JSON: {e}')
129+
130+
115131
def main():
116132
"""The main function. This is where the magic happens."""
117133

@@ -145,9 +161,7 @@ def main():
145161
# Redfish walk has an explicit fixture suffix, so the fixture
146162
# file names describe what they contain (systems, system).
147163
test_base = args.TEST[0]
148-
args.TEST[0] = f'{test_base}-systems'
149-
stdout, _, _ = lib.lftest.test(args.TEST)
150-
result = json.loads(stdout)
164+
result = load_test_fixture(args.TEST, f'{test_base}-systems')
151165
# "Members": [
152166
# {
153167
# "@odata.id": "/redfish/v1/Systems/437XR1138R2"
@@ -178,9 +192,7 @@ def main():
178192
)
179193
)
180194
else:
181-
args.TEST[0] = f'{test_base}-system'
182-
stdout, _, _ = lib.lftest.test(args.TEST)
183-
systems = json.loads(stdout)
195+
systems = load_test_fixture(args.TEST, f'{test_base}-system')
184196
systems = lib.redfish.get_systems(systems)
185197
if systems['Status_State'] not in ['Enabled', 'Quiesced']:
186198
continue

0 commit comments

Comments
 (0)