Skip to content

Commit 81df5f2

Browse files
committed
Corrected logic between null and None, updated backend
1 parent 5a9fe89 commit 81df5f2

5 files changed

+359
-266
lines changed

RedfishInteropValidator.py

+45-45
Original file line numberDiff line numberDiff line change
@@ -305,11 +305,12 @@ def validateURITree(URI, uriName, profile, expectedType=None, expectedSchema=Non
305305

306306
# interop service level checks
307307
finalResults = OrderedDict()
308-
for left in objRes:
308+
if URI != "/redfish/v1":
309+
resultEnum = commonInterop.sEnum.WARN
310+
traverseLogger.info("We are not validating root, warn only")
311+
else:
309312
resultEnum = commonInterop.sEnum.FAIL
310-
if URI != "/redfish/v1":
311-
resultEnum = commonInterop.sEnum.WARN
312-
traverseLogger.info("We are not validating root, warn only")
313+
for left in objRes:
313314
if not objRes[left].get('mark', False):
314315
req = objRes[left].get("ReadRequirement", "Mandatory")
315316
rmessages.append(
@@ -351,81 +352,78 @@ def main(arglist=None, direct_parser=None):
351352
"""
352353
Main program
353354
"""
354-
argget = argparse.ArgumentParser(description='tool for testing services against an interoperability profile')
355+
argget = argparse.ArgumentParser(description='tool for testing services against an interoperability profile, version {}'.format(tool_version))
355356

356357
# config
357-
argget.add_argument('-c', '--config', type=str, help='config file (overrides other params)')
358+
argget.add_argument('-c', '--config', type=str, help='config file')
358359

359360
# tool
360361
argget.add_argument('--desc', type=str, default='No desc', help='sysdescription for identifying logs')
361362
argget.add_argument('--payload', type=str, help='mode to validate payloads [Tree, Single, SingleFile, TreeFile] followed by resource/filepath', nargs=2)
362-
argget.add_argument('-v', action='store_true', help='verbose log output to stdout')
363+
argget.add_argument('-v', action='store_const', const=True, default=None, help='verbose log output to stdout (parameter-only)')
363364
argget.add_argument('--logdir', type=str, default='./logs', help='directory for log files')
364365
argget.add_argument('--debug_logging', action="store_const", const=logging.DEBUG, default=logging.INFO,
365-
help='Output debug statements to text log, otherwise it only uses INFO')
366+
help='Output debug statements to text log, otherwise it only uses INFO (parameter-only)')
366367
argget.add_argument('--verbose_checks', action="store_const", const=VERBO_NUM, default=logging.INFO,
367-
help='Show all checks in logging')
368-
argget.add_argument('--nooemcheck', action='store_true', help='Don\'t check OEM items')
368+
help='Show all checks in logging (parameter-only)')
369+
argget.add_argument('--nooemcheck', action='store_const', const=True, default=None, help='Don\'t check OEM items')
369370

370371
# service
371372
argget.add_argument('-i', '--ip', type=str, help='ip to test on [host:port]')
372-
argget.add_argument('-u', '--user', default='', type=str, help='user for basic auth')
373-
argget.add_argument('-p', '--passwd', default='', type=str, help='pass for basic auth')
373+
argget.add_argument('-u', '--user', type=str, help='user for basic auth')
374+
argget.add_argument('-p', '--passwd', type=str, help='pass for basic auth')
374375
argget.add_argument('--linklimit', type=str, help='Limit the amount of links in collections, formatted TypeName:## TypeName:## ..., default LogEntry:20 ', nargs='*')
375-
argget.add_argument('--sample', type=int, default=0, help='sample this number of members from large collections for validation; default is to validate all members')
376-
argget.add_argument('--timeout', type=int, default=30, help='requests timeout in seconds')
377-
argget.add_argument('--nochkcert', action='store_true', help='ignore check for certificate')
378-
argget.add_argument('--nossl', action='store_true', help='use http instead of https')
379-
argget.add_argument('--forceauth', action='store_true', help='force authentication on unsecure connections')
380-
argget.add_argument('--authtype', type=str, default='Basic', help='authorization type (None|Basic|Session|Token)')
381-
argget.add_argument('--localonly', action='store_true', help='only use locally stored schema on your harddrive')
382-
argget.add_argument('--preferonline', action='store_true', help='use online schema')
383-
argget.add_argument('--service', action='store_true', help='only use uris within the service')
384-
argget.add_argument('--ca_bundle', default="", type=str, help='path to Certificate Authority bundle file or directory')
385-
argget.add_argument('--token', default="", type=str, help='bearer token for authtype Token')
386-
argget.add_argument('--http_proxy', type=str, default='', help='URL for the HTTP proxy')
387-
argget.add_argument('--https_proxy', type=str, default='', help='URL for the HTTPS proxy')
388-
argget.add_argument('--cache', type=str, help='cache mode [Off, Fallback, Prefer] followed by directory', nargs=2)
389-
argget.add_argument('--uri_check', action='store_true', help='Check for URI if schema supports it')
376+
argget.add_argument('--sample', type=int, help='sample this number of members from large collections for validation; default is to validate all members')
377+
argget.add_argument('--timeout', type=int, help='requests timeout in seconds')
378+
argget.add_argument('--nochkcert', action='store_const', const=True, default=None, help='ignore check for certificate')
379+
argget.add_argument('--nossl', action='store_const', const=True, default=None, help='use http instead of https')
380+
argget.add_argument('--forceauth', action='store_const', const=True, default=None, help='force authentication on unsecure connections')
381+
argget.add_argument('--authtype', type=str, help='authorization type (None|Basic|Session|Token)')
382+
argget.add_argument('--localonly', action='store_const', const=True, default=None, help='only use locally stored schema on your harddrive')
383+
argget.add_argument('--preferonline', action='store_const', const=True, default=None, help='use online schema')
384+
argget.add_argument('--service', action='store_const', const=True, default=None, help='only use uris within the service')
385+
argget.add_argument('--ca_bundle', type=str, help='path to Certificate Authority bundle file or directory')
386+
argget.add_argument('--token', type=str, help='bearer token for authtype Token')
387+
argget.add_argument('--http_proxy', type=str, help='URL for the HTTP proxy')
388+
argget.add_argument('--https_proxy', type=str, help='URL for the HTTPS proxy')
389+
argget.add_argument('--cache', type=str, help='cache mode [Off, Fallback, Prefer] followed by directory to fallback or override problem service JSON payloads', nargs=2)
390+
argget.add_argument('--uri_check', action='store_const', const=True, default=None, help='Check for URI if schema supports it')
391+
argget.add_argument('--version_check', type=str, help='Change default tool configuration based on the version provided (default use target version)')
390392

391393
# metadata
392-
argget.add_argument('--schemadir', type=str, default='./SchemaFiles/metadata', help='directory for local schema files')
393-
argget.add_argument('--schema_pack', type=str, default='', help='Deploy DMTF schema from zip distribution, for use with --localonly (Specify url or type "latest", overwrites current schema)')
394-
argget.add_argument('--suffix', type=str, default='_v1.xml', help='suffix of local schema files (for version differences)')
394+
argget.add_argument('--schemadir', type=str, help='directory for local schema files')
395+
argget.add_argument('--schema_pack', type=str, help='Deploy DMTF schema from zip distribution, for use with --localonly (Specify url or type "latest", overwrites current schema)')
396+
argget.add_argument('--suffix', type=str, help='suffix of local schema files (for version differences)')
395397

396398
# Config information unique to Interop Validator
397399
argget.add_argument('profile', type=str, default='sample.json', help='interop profile with which to validate service against')
398400
argget.add_argument('--schema', type=str, default=None, help='schema with which to validate interop profile against')
399401
argget.add_argument('--warnrecommended', action='store_true', help='warn on recommended instead of pass')
400402

401-
rsvLogger.info("Redfish Interop Validator, version {}".format(tool_version))
402403
args = argget.parse_args(arglist)
403404

404-
# clear cache from any other runs
405-
rst.callResourceURI.cache_clear()
406-
rst.rfSchema.getSchemaDetails.cache_clear()
407-
408405
# set up config
406+
rst.ch.setLevel(args.verbose_checks if not args.v else logging.DEBUG)
409407
if direct_parser is not None:
410408
try:
411409
cdict = rst.convertConfigParserToDict(direct_parser)
412-
rst.setConfig(cdict)
410+
config, default_list = rst.setConfig(cdict)
413411
except Exception as ex:
414-
rsvLogger.exception("Something went wrong")
412+
rsvLogger.debug('Exception caught while parsing configuration', exc_info=1)
413+
rsvLogger.error('Unable to parse configuration: {}'.format(repr(ex)))
415414
return 1, None, 'Config Parser Exception'
416415
elif args.config is None and args.ip is None:
417416
rsvLogger.info('No ip or config specified.')
418417
argget.print_help()
419418
return 1, None, 'Config Incomplete'
420419
else:
421420
try:
422-
rst.setByArgparse(args)
423-
except Exception:
424-
rsvLogger.exception("Something went wrong")
421+
config, default_list = rst.setByArgparse(args)
422+
except Exception as ex:
423+
rsvLogger.debug('Exception caught while parsing configuration', exc_info=1)
424+
rsvLogger.error('Unable to parse configuration: {}'.format(repr(ex)))
425425
return 1, None, 'Config Exception'
426426

427-
config = rst.config
428-
429427
# Set interop config items
430428
config['WarnRecommended'] = rst.config.get('warnrecommended', args.warnrecommended)
431429
commonInterop.config['WarnRecommended'] = config['WarnRecommended']
@@ -453,9 +451,11 @@ def main(arglist=None, direct_parser=None):
453451
rsvLogger.addHandler(fh)
454452

455453
# Then start service
454+
rsvLogger.info("Redfish Interop Validator, version {}".format(tool_version))
456455
try:
457-
currentService = rst.startService()
456+
currentService = rst.startService(config, default_list)
458457
except Exception as ex:
458+
rsvLogger.debug('Exception caught while creating Service', exc_info=1)
459459
rsvLogger.error("Service could not be started: {}".format(ex))
460460
return 1, None, 'Service Exception'
461461

@@ -467,7 +467,7 @@ def main(arglist=None, direct_parser=None):
467467
rsvLogger.info('System Info: ' + sysDescription)
468468
rsvLogger.info('Profile:' + config['profile'])
469469
rsvLogger.info('\n'.join(
470-
['{}: {}'.format(x, config[x]) for x in sorted(list(config.keys() - set(['systeminfo', 'targetip', 'password', 'description', 'profile'])))]))
470+
['{}: {}'.format(x, config[x]) for x in sorted(list(config.keys() - set(['systeminfo', 'targetip', 'password', 'description']))) if config[x] not in ['', None]]))
471471
rsvLogger.info('Start time: ' + startTick.strftime('%x - %X'))
472472

473473
# Interop Profile handling
@@ -505,7 +505,7 @@ def main(arglist=None, direct_parser=None):
505505
for profile in profiles:
506506
profileName = profile.get('ProfileName')
507507
if 'Single' in rst.config.get('payloadmode'):
508-
success, counts, resultsNew, xlinks, topobj = validateSingleURI(rst.config.get('payloadfilepath'), 'Target', profile, expectedJson=jsonData)
508+
success, counts, resultsNew, xlinks, topobj = validateSingleURI(rst.config.get('payloadfilepath'), profile, 'Target', expectedJson=jsonData)
509509
elif 'Tree' in rst.config.get('payloadmode'):
510510
success, counts, resultsNew, xlinks, topobj = validateURITree(rst.config.get('payloadfilepath'), 'Target', profile, expectedJson=jsonData)
511511
else:

commonInterop.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import traverseService as rst
77
from enum import Enum
8-
from collections import Counter, OrderedDict
8+
from collections import Counter
99

1010
rsvLogger = rst.getLogger()
1111

@@ -140,9 +140,6 @@ def checkComparison(val, compareType, target):
140140
"""
141141
rsvLogger.info('Testing a comparison \n\t' + str((val, compareType, target)))
142142
vallist = val if isinstance(val, list) else [val]
143-
vallist = [(val if val is not None else 'DNE') for val in vallist]
144-
target = [(val if val is not None else 'DNE') for val in target]
145-
input((vallist, target))
146143
paramPass = False
147144
if compareType is None:
148145
rsvLogger.error('CompareType not available in payload')

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
beautifulsoup4>=4.6.0
22
lxml
33
requests
4+
jsonschema

rfSchema.py

+25-10
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ def __init__(self, typename, schemaObj):
360360
self.actionList = []
361361
self.propPattern = None
362362
self.additional = False
363-
self.expectedURI = ".*"
363+
self.expectedURI = None
364364

365365
# get all properties and actions in Type chain
366366
success, currentSchemaObj, baseType = True, self.schemaObj, self.fulltype
@@ -376,7 +376,7 @@ def __init__(self, typename, schemaObj):
376376
self.parent = PropType(baseType, currentSchemaObj)
377377
if not self.additional:
378378
self.additional = self.parent.additional
379-
if self.expectedURI == '.*':
379+
if self.expectedURI is None:
380380
self.expectedURI = self.parent.expectedURI
381381
except Exception as ex:
382382
rst.traverseLogger.debug('Exception caught while creating new PropType', exc_info=1)
@@ -442,6 +442,26 @@ def getActions(self):
442442
node = node.parent
443443
raise StopIteration
444444

445+
def compareURI(self, uri, my_id):
446+
expected_uris = self.expectedURI
447+
uri = uri.rstrip('/')
448+
if expected_uris is not None:
449+
regex = re.compile(r"{.*?}")
450+
for e in expected_uris:
451+
e_left, e_right = tuple(e.rsplit('/', 1))
452+
e_left = regex.sub('[a-zA-Z0-9_.-]+', e_left)
453+
if regex.match(e_right):
454+
if my_id is None:
455+
rst.traverseLogger.warn('No Id provided by payload')
456+
e_right = str(my_id)
457+
e_compare_to = '/'.join([e_left, e_right])
458+
success = re.match(e_compare_to, uri) is not None
459+
if success:
460+
break
461+
else:
462+
success = True
463+
return success
464+
445465

446466
def getTypeDetails(schemaObj, SchemaAlias):
447467
"""
@@ -516,20 +536,15 @@ def getTypeDetails(schemaObj, SchemaAlias):
516536
PropertyPattern['Type'] = prop_type
517537
additional = True
518538

519-
regex = re.compile(r"{.*?}")
520-
expectedURI = '.*'
539+
expectedURI = None
521540
if uriElement is not None:
522-
expectedURI = ''
523541
try:
524542
all_strings = uriElement.find('Collection').find_all('String')
525-
for e in all_strings:
526-
content = e.contents[0]
527-
expectedURI += '({}/?)|'.format(content.rstrip('/'))
528-
expectedURI = regex.sub('[a-zA-Z0-9_.-]+', expectedURI).rstrip('|')
543+
expectedURI = [e.contents[0].rstrip('/') for e in all_strings]
529544
except Exception as e:
530545
rst.traverseLogger.debug('Exception caught while checking URI', exc_info=1)
531546
rst.traverseLogger.warn('Could not gather info from Redfish.Uris annotation')
532-
expectedURI = '.*'
547+
expectedURI = None
533548

534549
# get properties
535550
usableProperties = element.find_all(['NavigationProperty', 'Property'], recursive=False)

0 commit comments

Comments
 (0)