Skip to content

Commit ae75475

Browse files
authored
Merge pull request #229 from DMTF/log-update
Adjusted log and messages to match Redfish Service Validator changes
2 parents 9cafaac + b962b65 commit ae75475

File tree

11 files changed

+473
-411
lines changed

11 files changed

+473
-411
lines changed

RedfishInteropValidator.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
# Copyright 2017-2025 DMTF. All rights reserved.
33
# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Interop-Validator/blob/master/LICENSE.md
44

5-
from redfish_interop_validator.RedfishInteropValidator import main, my_logger
5+
import logging
66
import sys
77

8+
from redfish_interop_validator.RedfishInteropValidator import main
9+
10+
my_logger = logging.getLogger('rsv')
11+
my_logger.setLevel(logging.DEBUG)
12+
813
if __name__ == '__main__':
914
try:
1015
status_code, lastResultsPage, exit_string = main()

redfish_interop_validator/RedfishInteropValidator.py

Lines changed: 61 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,11 @@
1616
import redfish_interop_validator.traverseInterop as traverseInterop
1717
from redfish_interop_validator.profile import getProfiles, checkProfileAgainstSchema, hashProfile
1818
from redfish_interop_validator.validateResource import validateSingleURI, validateURITree
19+
from redfish_interop_validator.interop import testResultEnum
20+
from redfish_interop_validator import logger
1921

2022
tool_version = '2.3.1'
2123

22-
# Set up the custom debug levels
23-
VERBOSE1 = logging.INFO - 1
24-
VERBOSE2 = logging.INFO - 2
25-
26-
logging.addLevelName(VERBOSE1, "VERBOSE1")
27-
logging.addLevelName(VERBOSE2, "VERBOSE2")
28-
29-
def verbose1(self, msg, *args, **kwargs):
30-
if self.isEnabledFor(VERBOSE1):
31-
self._log(VERBOSE1, msg, args, **kwargs)
32-
33-
def verbose2(self, msg, *args, **kwargs):
34-
if self.isEnabledFor(VERBOSE2):
35-
self._log(VERBOSE2, msg, args, **kwargs)
36-
37-
logging.Logger.verbose1 = verbose1
38-
logging.Logger.verbose2 = verbose2
39-
40-
my_logger = logging.getLogger()
41-
my_logger.setLevel(logging.DEBUG)
42-
standard_out = logging.StreamHandler(sys.stdout)
43-
standard_out.setLevel(logging.INFO)
44-
my_logger.addHandler(standard_out)
45-
46-
#####################################################
47-
# Script starts here ##########
48-
#####################################################
49-
5024
def main(argslist=None, configfile=None):
5125
"""Main command
5226
@@ -93,25 +67,27 @@ def main(argslist=None, configfile=None):
9367
start_tick = datetime.now()
9468

9569
# Set logging file
96-
standard_out.setLevel(logging.INFO - args.verbose if args.verbose < 3 else logging.DEBUG)
70+
logger.set_standard_out(logger.Level.INFO - args.verbose if args.verbose < 3 else logger.Level.DEBUG)
9771

9872
logpath = args.logdir
9973

10074
if not os.path.isdir(logpath):
10175
os.makedirs(logpath)
10276

103-
fmt = logging.Formatter('%(levelname)s - %(message)s')
104-
file_handler = logging.FileHandler(datetime.strftime(start_tick, os.path.join(logpath, "InteropLog_%m_%d_%Y_%H%M%S.txt")))
105-
file_handler.setLevel(min(logging.INFO if not args.debugging else logging.DEBUG, standard_out.level))
106-
file_handler.setFormatter(fmt)
107-
my_logger.addHandler(file_handler)
77+
log_level = logger.Level.INFO if not args.debugging else logger.Level.DEBUG
78+
file_name = datetime.strftime(start_tick, os.path.join(logpath, "ConformanceLog_%m_%d_%Y_%H%M%S.txt"))
79+
80+
logger.create_logging_file_handler(log_level, file_name)
81+
82+
my_logger = logging.getLogger('rsv')
83+
my_logger.setLevel(logging.DEBUG)
10884

10985
# Begin of log
11086
my_logger.info("Redfish Interop Validator, version {}".format(tool_version))
11187
my_logger.info("")
11288

11389
if args.ip is None and configfile is None:
114-
my_logger.error('No IP or Config Specified')
90+
my_logger.error('Configuration Error: No IP or Config Specified')
11591
argget.print_help()
11692
return 1, None, 'Configuration Incomplete'
11793

@@ -129,15 +105,15 @@ def main(argslist=None, configfile=None):
129105
# Check if our URL is consistent
130106
scheme, netloc, _, _, _, _ = urlparse(args.ip)
131107
if scheme not in ['http', 'https']:
132-
my_logger.error('IP is missing http or https')
108+
my_logger.error('Configuration Error: IP is missing http or https')
133109
return 1, None, 'IP Incomplete'
134110

135111
if netloc == '':
136-
my_logger.error('IP is missing ip/host')
112+
my_logger.error('Configuration Error: IP is missing ip/host')
137113
return 1, None, 'IP Incomplete'
138114

139115
if len(args.collectionlimit) % 2 != 0:
140-
my_logger.error('Collection Limit requires two arguments per entry (ResourceType Count)')
116+
my_logger.error('Configuration Error: Collection Limit requires two arguments per entry (ResourceType Count)')
141117
return 1, None, 'Collection Limit Incomplete'
142118

143119
# Start printing config details, remove redundant/private info from print
@@ -152,7 +128,7 @@ def main(argslist=None, configfile=None):
152128
currentService = traverseInterop.startService(vars(args))
153129
except Exception as ex:
154130
my_logger.debug('Exception caught while creating Service', exc_info=1)
155-
my_logger.error("Service could not be started: {}".format(ex))
131+
my_logger.error("Service Exception Error: Service could not be started: {}".format(ex))
156132
return 1, None, 'Service Exception'
157133

158134
# Create a description of our service if there is none given
@@ -203,7 +179,7 @@ def main(argslist=None, configfile=None):
203179

204180
if pmode not in ['tree', 'single', 'singlefile', 'treefile', 'default']:
205181
pmode = 'Default'
206-
my_logger.warning('PayloadMode or path invalid, using Default behavior')
182+
my_logger.warning('Configuration Warning: PayloadMode or path invalid, using Default behavior')
207183
if 'file' in pmode:
208184
if ppath is not None and os.path.isfile(ppath):
209185
with open(ppath) as f:
@@ -242,23 +218,21 @@ def main(argslist=None, configfile=None):
242218
if processing_profile_name not in processed_profiles:
243219
processed_profiles.add(profile_name)
244220
else:
245-
my_logger.warn("Profile {} already processed".format({}))
221+
my_logger.warning("Import Warning: Profile {} already processed".format({}))
246222

247223
if 'single' in pmode:
248-
success, _, new_results, _, _ = validateSingleURI(ppath, profile_to_process, 'Target', expectedJson=jsonData)
224+
success, new_results, _, _ = validateSingleURI(ppath, profile_to_process, 'Target', expectedJson=jsonData)
249225
elif 'tree' in pmode:
250-
success, _, new_results, _, _ = validateURITree(ppath, profile_to_process, 'Target', expectedJson=jsonData)
226+
success, new_results, _, _ = validateURITree(ppath, profile_to_process, 'Target', expectedJson=jsonData)
251227
else:
252-
success, _, new_results, _, _ = validateURITree('/redfish/v1/', profile_to_process, 'ServiceRoot', expectedJson=jsonData)
228+
success, new_results, _, _ = validateURITree('/redfish/v1/', profile_to_process, 'ServiceRoot', expectedJson=jsonData)
253229
if results is None:
254230
results = new_results
255231
else:
256232
for item_name, item in new_results.items():
257233
for x in item['messages']:
258234
x.name = profile_name + ' -- ' + x.name
259235
if item_name in results:
260-
inner_counts = results[item_name]['counts']
261-
inner_counts.update(item['counts'])
262236
results[item_name]['messages'].extend(item['messages'])
263237
else:
264238
results[item_name] = item
@@ -273,63 +247,65 @@ def main(argslist=None, configfile=None):
273247
try:
274248
currentService.close()
275249
except Exception as e:
276-
my_logger.error('Failed to log out of service; session may still be active ({})'.format(e))
250+
my_logger.error('Service Closure Error: Failed to log out of service; session may still be active ({})'.format(e))
277251

278-
final_counts = Counter()
279252
now_tick = datetime.now()
280253
my_logger.info('Elapsed time: {}'.format(str(now_tick - start_tick).rsplit('.', 1)[0]))
281254

282-
for item in results:
283-
inner_counts = results[item]['counts']
284-
285-
# detect if there are error messages for this resource, but no failure counts; if so, add one to the innerCounts
286-
counters_all_pass = True
287-
for count_type in sorted(inner_counts.keys()):
288-
if inner_counts.get(count_type) == 0:
289-
continue
290-
if any(x in count_type for x in ['problem', 'fail', 'bad', 'exception']):
291-
counters_all_pass = False
292-
if 'fail' in count_type or 'exception' in count_type:
293-
my_logger.error('{} {} errors in {}'.format(inner_counts[count_type], count_type, results[item]['uri']))
294-
inner_counts[count_type] += 0
295-
error_messages_present = False
296-
if results[item]['errors'] is not None and len(results[item]['errors']) > 0:
297-
error_messages_present = True
298-
if results[item]['warns'] is not None and len(results[item]['warns']) > 0:
299-
inner_counts['warningPresent'] = 1
300-
if counters_all_pass and error_messages_present:
301-
inner_counts['failErrorPresent'] = 1
302-
303-
final_counts.update(results[item]['counts'])
255+
final_counts = Counter()
304256

305-
import redfish_interop_validator.tohtml as tohtml
257+
my_logger.info('\nListing any warnings and errors: ')
258+
259+
for k, my_result in results.items():
260+
261+
for msg in my_result['messages']:
262+
if msg.result in [testResultEnum.PASS]:
263+
final_counts['pass'] += 1
264+
if msg.result in [testResultEnum.NOT_TESTED]:
265+
final_counts['not_tested'] += 1
266+
267+
warns = [x for x in my_result['records'] if x.levelno == logger.Level.WARN]
268+
errors = [x for x in my_result['records'] if x.levelno == logger.Level.ERROR]
269+
if len(warns + errors):
270+
my_logger.info(" ")
271+
my_logger.info(my_result['uri'])
306272

307-
fails = 0
308-
for key in [key for key in final_counts.keys()]:
309-
if final_counts[key] == 0:
310-
del final_counts[key]
311-
continue
312-
if any(x in key for x in ['problem', 'fail', 'bad', 'exception']):
313-
fails += final_counts[key]
273+
if len(warns):
274+
my_logger.info("Warnings")
275+
for record in warns:
276+
final_counts[record.levelname.lower()] += 1
277+
my_logger.log(record.levelno, ", ".join([x for x in [record.msg, record.result] if x]))
314278

315-
html_str = tohtml.renderHtml(results, final_counts, tool_version, start_tick, now_tick, currentService.config)
279+
if len(errors):
280+
my_logger.info("Errors")
281+
for record in errors:
282+
final_counts[record.levelname.lower()] += 1
283+
my_logger.log(record.levelno, ", ".join([x for x in [record.msg, record.result] if x]))
284+
285+
import redfish_interop_validator.tohtml as tohtml
286+
287+
html_str = tohtml.renderHtml(results, tool_version, start_tick, now_tick, currentService)
316288

317289
lastResultsPage = datetime.strftime(start_tick, os.path.join(logpath, "InteropHtmlLog_%m_%d_%Y_%H%M%S.html"))
318290

319291
tohtml.writeHtml(html_str, lastResultsPage)
320292

321-
success = success and not (fails > 0)
322-
my_logger.info("\n".join('{}: {} '.format(x, y) for x, y in sorted(final_counts.items())))
293+
my_logger.info("\nResults Summary:")
294+
my_logger.info(", ".join([
295+
'Pass: {}'.format(final_counts['pass']),
296+
'Fail: {}'.format(final_counts['error']),
297+
'Warning: {}'.format(final_counts['warning']),
298+
'Not Tested: {}'.format(final_counts['nottested']),
299+
]))
323300

324-
# dump cache info to debug log
325-
my_logger.debug('callResourceURI() -> {}'.format(currentService.callResourceURI.cache_info()))
301+
success = final_counts['error'] == 0
326302

327303
if not success:
328-
my_logger.error("Validation has failed: {} problems found".format(fails))
304+
my_logger.error("Validation has failed: {} problems found".format(final_counts['error']))
329305
else:
330306
my_logger.info("Validation has succeeded.")
331307
status_code = 0
332-
308+
333309
return status_code, lastResultsPage, 'Validation done'
334310

335311

redfish_interop_validator/config.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import configparser
66
import logging
77

8-
my_logger = logging.getLogger()
8+
my_logger = logging.getLogger('rsv')
99
my_logger.setLevel(logging.DEBUG)
1010

1111
config_struct = {
@@ -26,7 +26,6 @@ def convert_args_to_config(args):
2626
my_var = vars(args)[option]
2727
if isinstance(my_var, list):
2828
my_var = ' '.join(my_var)
29-
print(my_var)
3029
my_config.set(section, option, str(my_var) if my_var else '')
3130
else:
3231
my_config.set(section, option, '******')
@@ -47,7 +46,7 @@ def convert_config_to_args(args, config):
4746
for option in my_config[section]:
4847
if option.lower() not in config_options:
4948
if option.lower() not in ['version', 'copyright']:
50-
my_logger.error('Option {} not supported!'.format(option))
49+
my_logger.error('Tool Configuration Error: Option {} not supported!'.format(option), extra={"result": "unsupportedOption"})
5150
elif my_config[section][option] not in ['', None]:
5251
if option.lower() == 'payload' or option.lower() == 'collectionlimit':
5352
setattr(args, option, my_config[section][option].split(' '))

redfish_interop_validator/redfish.py renamed to redfish_interop_validator/helper.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def compareMinVersion(version, min_version):
4343
# use array comparison, which compares each sequential number
4444
return min_split < payload_split
4545

46-
4746
def navigateJsonFragment(decoded, URILink):
4847
if '#' in URILink:
4948
URIfragless, frag = tuple(URILink.rsplit('#', 1))
@@ -55,14 +54,14 @@ def navigateJsonFragment(decoded, URILink):
5554
decoded = decoded.get(item)
5655
elif isinstance(decoded, list):
5756
if not item.isdigit():
58-
my_logger.error("This URI ({}) is accessing an array, but this is not an index: {}".format(URILink, item))
57+
my_logger.error("URI Destination Error: This URI ({}) is accessing an array, but this is not an index: {}".format(URILink, item))
5958
return None
6059
if int(item) >= len(decoded):
61-
my_logger.error("This URI ({}) is accessing an array, but the index is too large for an array of size {}: {}".format(URILink, len(decoded), item))
60+
my_logger.error("URI Destination Error: This URI ({}) is accessing an array, but the index is too large for an array of size {}: {}".format(URILink, len(decoded), item))
6261
return None
6362
decoded = decoded[int(item)]
6463
else:
65-
my_logger.error("This URI ({}) has resolved to an invalid object that is neither an array or dictionary".format(URILink))
64+
my_logger.error("URI Destination Error: This URI ({}) has resolved to an invalid object that is neither an array or dictionary".format(URILink))
6665
return None
6766
return decoded
6867

0 commit comments

Comments
 (0)