redfish-interop-validator 2.3.0__tar.gz → 2.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {redfish_interop_validator-2.3.0/redfish_interop_validator.egg-info → redfish_interop_validator-2.3.2}/PKG-INFO +1 -1
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/RedfishInteropValidator.py +62 -86
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/config.py +2 -3
- redfish_interop_validator-2.3.0/redfish_interop_validator/redfish.py → redfish_interop_validator-2.3.2/redfish_interop_validator/helper.py +3 -4
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/interop.py +70 -91
- redfish_interop_validator-2.3.2/redfish_interop_validator/logger.py +129 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/profile.py +5 -5
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/session.py +8 -8
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/tohtml.py +110 -63
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/traverseInterop.py +54 -61
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/validateResource.py +29 -96
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2/redfish_interop_validator.egg-info}/PKG-INFO +1 -1
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/SOURCES.txt +2 -1
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/setup.py +1 -1
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/AUTHORS.md +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/LICENSE.md +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/README.md +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/RedfishLogo.py +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/__init__.py +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/dependency_links.txt +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/entry_points.txt +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/requires.txt +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/top_level.txt +0 -0
- {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/setup.cfg +0 -0
|
@@ -16,36 +16,10 @@ from collections import Counter
|
|
|
16
16
|
import redfish_interop_validator.traverseInterop as traverseInterop
|
|
17
17
|
from redfish_interop_validator.profile import getProfiles, checkProfileAgainstSchema, hashProfile
|
|
18
18
|
from redfish_interop_validator.validateResource import validateSingleURI, validateURITree
|
|
19
|
+
from redfish_interop_validator.interop import testResultEnum
|
|
20
|
+
from redfish_interop_validator import logger
|
|
19
21
|
|
|
20
|
-
tool_version = '2.3.
|
|
21
|
-
|
|
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
|
-
#####################################################
|
|
22
|
+
tool_version = '2.3.2'
|
|
49
23
|
|
|
50
24
|
def main(argslist=None, configfile=None):
|
|
51
25
|
"""Main command
|
|
@@ -93,25 +67,27 @@ def main(argslist=None, configfile=None):
|
|
|
93
67
|
start_tick = datetime.now()
|
|
94
68
|
|
|
95
69
|
# Set logging file
|
|
96
|
-
|
|
70
|
+
logger.set_standard_out(logger.Level.INFO - args.verbose if args.verbose < 3 else logger.Level.DEBUG)
|
|
97
71
|
|
|
98
72
|
logpath = args.logdir
|
|
99
73
|
|
|
100
74
|
if not os.path.isdir(logpath):
|
|
101
75
|
os.makedirs(logpath)
|
|
102
76
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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)
|
|
108
84
|
|
|
109
85
|
# Begin of log
|
|
110
86
|
my_logger.info("Redfish Interop Validator, version {}".format(tool_version))
|
|
111
87
|
my_logger.info("")
|
|
112
88
|
|
|
113
89
|
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')
|
|
115
91
|
argget.print_help()
|
|
116
92
|
return 1, None, 'Configuration Incomplete'
|
|
117
93
|
|
|
@@ -129,15 +105,15 @@ def main(argslist=None, configfile=None):
|
|
|
129
105
|
# Check if our URL is consistent
|
|
130
106
|
scheme, netloc, _, _, _, _ = urlparse(args.ip)
|
|
131
107
|
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')
|
|
133
109
|
return 1, None, 'IP Incomplete'
|
|
134
110
|
|
|
135
111
|
if netloc == '':
|
|
136
|
-
my_logger.error('IP is missing ip/host')
|
|
112
|
+
my_logger.error('Configuration Error: IP is missing ip/host')
|
|
137
113
|
return 1, None, 'IP Incomplete'
|
|
138
114
|
|
|
139
115
|
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)')
|
|
141
117
|
return 1, None, 'Collection Limit Incomplete'
|
|
142
118
|
|
|
143
119
|
# Start printing config details, remove redundant/private info from print
|
|
@@ -152,7 +128,7 @@ def main(argslist=None, configfile=None):
|
|
|
152
128
|
currentService = traverseInterop.startService(vars(args))
|
|
153
129
|
except Exception as ex:
|
|
154
130
|
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))
|
|
156
132
|
return 1, None, 'Service Exception'
|
|
157
133
|
|
|
158
134
|
# Create a description of our service if there is none given
|
|
@@ -203,7 +179,7 @@ def main(argslist=None, configfile=None):
|
|
|
203
179
|
|
|
204
180
|
if pmode not in ['tree', 'single', 'singlefile', 'treefile', 'default']:
|
|
205
181
|
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')
|
|
207
183
|
if 'file' in pmode:
|
|
208
184
|
if ppath is not None and os.path.isfile(ppath):
|
|
209
185
|
with open(ppath) as f:
|
|
@@ -242,14 +218,14 @@ def main(argslist=None, configfile=None):
|
|
|
242
218
|
if processing_profile_name not in processed_profiles:
|
|
243
219
|
processed_profiles.add(profile_name)
|
|
244
220
|
else:
|
|
245
|
-
my_logger.
|
|
221
|
+
my_logger.warning("Import Warning: Profile {} already processed".format({}))
|
|
246
222
|
|
|
247
223
|
if 'single' in pmode:
|
|
248
|
-
success,
|
|
224
|
+
success, new_results, _, _ = validateSingleURI(ppath, profile_to_process, 'Target', expectedJson=jsonData)
|
|
249
225
|
elif 'tree' in pmode:
|
|
250
|
-
success,
|
|
226
|
+
success, new_results, _, _ = validateURITree(ppath, profile_to_process, 'Target', expectedJson=jsonData)
|
|
251
227
|
else:
|
|
252
|
-
success,
|
|
228
|
+
success, new_results, _, _ = validateURITree('/redfish/v1/', profile_to_process, 'ServiceRoot', expectedJson=jsonData)
|
|
253
229
|
if results is None:
|
|
254
230
|
results = new_results
|
|
255
231
|
else:
|
|
@@ -257,8 +233,6 @@ def main(argslist=None, configfile=None):
|
|
|
257
233
|
for x in item['messages']:
|
|
258
234
|
x.name = profile_name + ' -- ' + x.name
|
|
259
235
|
if item_name in results:
|
|
260
|
-
inner_counts = results[item_name]['counts']
|
|
261
|
-
inner_counts.update(item['counts'])
|
|
262
236
|
results[item_name]['messages'].extend(item['messages'])
|
|
263
237
|
else:
|
|
264
238
|
results[item_name] = item
|
|
@@ -273,63 +247,65 @@ def main(argslist=None, configfile=None):
|
|
|
273
247
|
try:
|
|
274
248
|
currentService.close()
|
|
275
249
|
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))
|
|
277
251
|
|
|
278
|
-
final_counts = Counter()
|
|
279
252
|
now_tick = datetime.now()
|
|
280
253
|
my_logger.info('Elapsed time: {}'.format(str(now_tick - start_tick).rsplit('.', 1)[0]))
|
|
281
254
|
|
|
282
|
-
|
|
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()
|
|
304
256
|
|
|
305
|
-
|
|
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'])
|
|
306
272
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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]))
|
|
314
278
|
|
|
315
|
-
|
|
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)
|
|
316
288
|
|
|
317
289
|
lastResultsPage = datetime.strftime(start_tick, os.path.join(logpath, "InteropHtmlLog_%m_%d_%Y_%H%M%S.html"))
|
|
318
290
|
|
|
319
291
|
tohtml.writeHtml(html_str, lastResultsPage)
|
|
320
292
|
|
|
321
|
-
|
|
322
|
-
my_logger.info("
|
|
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
|
+
]))
|
|
323
300
|
|
|
324
|
-
|
|
325
|
-
my_logger.debug('callResourceURI() -> {}'.format(currentService.callResourceURI.cache_info()))
|
|
301
|
+
success = final_counts['error'] == 0
|
|
326
302
|
|
|
327
303
|
if not success:
|
|
328
|
-
my_logger.error("Validation has failed: {} problems found".format(
|
|
304
|
+
my_logger.error("Validation has failed: {} problems found".format(final_counts['error']))
|
|
329
305
|
else:
|
|
330
306
|
my_logger.info("Validation has succeeded.")
|
|
331
307
|
status_code = 0
|
|
332
|
-
|
|
308
|
+
|
|
333
309
|
return status_code, lastResultsPage, 'Validation done'
|
|
334
310
|
|
|
335
311
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import configparser
|
|
6
6
|
import logging
|
|
7
7
|
|
|
8
|
-
my_logger = logging.getLogger()
|
|
8
|
+
my_logger = logging.getLogger('rsv')
|
|
9
9
|
my_logger.setLevel(logging.DEBUG)
|
|
10
10
|
|
|
11
11
|
config_struct = {
|
|
@@ -26,7 +26,6 @@ def convert_args_to_config(args):
|
|
|
26
26
|
my_var = vars(args)[option]
|
|
27
27
|
if isinstance(my_var, list):
|
|
28
28
|
my_var = ' '.join(my_var)
|
|
29
|
-
print(my_var)
|
|
30
29
|
my_config.set(section, option, str(my_var) if my_var else '')
|
|
31
30
|
else:
|
|
32
31
|
my_config.set(section, option, '******')
|
|
@@ -47,7 +46,7 @@ def convert_config_to_args(args, config):
|
|
|
47
46
|
for option in my_config[section]:
|
|
48
47
|
if option.lower() not in config_options:
|
|
49
48
|
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"})
|
|
51
50
|
elif my_config[section][option] not in ['', None]:
|
|
52
51
|
if option.lower() == 'payload' or option.lower() == 'collectionlimit':
|
|
53
52
|
setattr(args, option, my_config[section][option].split(' '))
|
|
@@ -43,7 +43,6 @@ def compareMinVersion(version, min_version):
|
|
|
43
43
|
# use array comparison, which compares each sequential number
|
|
44
44
|
return min_split < payload_split
|
|
45
45
|
|
|
46
|
-
|
|
47
46
|
def navigateJsonFragment(decoded, URILink):
|
|
48
47
|
if '#' in URILink:
|
|
49
48
|
URIfragless, frag = tuple(URILink.rsplit('#', 1))
|
|
@@ -55,14 +54,14 @@ def navigateJsonFragment(decoded, URILink):
|
|
|
55
54
|
decoded = decoded.get(item)
|
|
56
55
|
elif isinstance(decoded, list):
|
|
57
56
|
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))
|
|
59
58
|
return None
|
|
60
59
|
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))
|
|
62
61
|
return None
|
|
63
62
|
decoded = decoded[int(item)]
|
|
64
63
|
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))
|
|
66
65
|
return None
|
|
67
66
|
return decoded
|
|
68
67
|
|