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.
Files changed (24) hide show
  1. {redfish_interop_validator-2.3.0/redfish_interop_validator.egg-info → redfish_interop_validator-2.3.2}/PKG-INFO +1 -1
  2. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/RedfishInteropValidator.py +62 -86
  3. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/config.py +2 -3
  4. redfish_interop_validator-2.3.0/redfish_interop_validator/redfish.py → redfish_interop_validator-2.3.2/redfish_interop_validator/helper.py +3 -4
  5. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/interop.py +70 -91
  6. redfish_interop_validator-2.3.2/redfish_interop_validator/logger.py +129 -0
  7. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/profile.py +5 -5
  8. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/session.py +8 -8
  9. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/tohtml.py +110 -63
  10. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/traverseInterop.py +54 -61
  11. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/validateResource.py +29 -96
  12. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2/redfish_interop_validator.egg-info}/PKG-INFO +1 -1
  13. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/SOURCES.txt +2 -1
  14. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/setup.py +1 -1
  15. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/AUTHORS.md +0 -0
  16. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/LICENSE.md +0 -0
  17. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/README.md +0 -0
  18. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/RedfishLogo.py +0 -0
  19. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator/__init__.py +0 -0
  20. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/dependency_links.txt +0 -0
  21. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/entry_points.txt +0 -0
  22. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/requires.txt +0 -0
  23. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/redfish_interop_validator.egg-info/top_level.txt +0 -0
  24. {redfish_interop_validator-2.3.0 → redfish_interop_validator-2.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redfish_interop_validator
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Redfish Interop Validator
5
5
  Home-page: https://github.com/DMTF/Redfish-Interop-Validator
6
6
  Author: DMTF, https://www.dmtf.org/standards/feedback
@@ -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.0'
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
- 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)
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
- 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)
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.warn("Profile {} already processed".format({}))
221
+ my_logger.warning("Import Warning: Profile {} already processed".format({}))
246
222
 
247
223
  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)
249
225
  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)
251
227
  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)
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
- 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()
304
256
 
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'])
306
272
 
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]))
314
278
 
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)
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
- 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
+ ]))
323
300
 
324
- # dump cache info to debug log
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(fails))
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