Skip to content

Commit e6ac802

Browse files
authored
Merge pull request #173 from DMTF/interop-usecases
Implement UseCases for top-level resources
2 parents 76fa6fc + 05b77cf commit e6ac802

File tree

3 files changed

+114
-28
lines changed

3 files changed

+114
-28
lines changed

common/interop.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Copyright 2016 DMTF. All rights reserved.
44
# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Interop-Validator/blob/master/LICENSE.md
55

6-
import re
6+
import re, copy
77
from enum import Enum
88
from collections import Counter
99

@@ -37,6 +37,7 @@ def __init__(self, name, profile_entry, expected, actual, success):
3737
self.success = success
3838
self.parent_results = None
3939

40+
4041
def validateComparisonAnyOfAllOf(profile_entry, property_path="Unspecified"):
4142
"""
4243
Gather comparison information after processing all Resources on system
@@ -335,7 +336,7 @@ def validateMinVersion(version, profile_entry):
335336

336337
# use string comparison, given version numbering is accurate to regex
337338
my_logger.debug('\tpass ' + str(paramPass))
338-
return msgInterop('MinVersion', '{} ({})'.format(profile_entry, payload_split), '<=', version, paramPass),\
339+
return msgInterop('MinVersion', profile_entry, '<=', version, paramPass),\
339340
paramPass
340341

341342

@@ -680,6 +681,54 @@ def validateInteropResource(propResourceObj, interop_profile, rf_payload):
680681
counts = Counter()
681682
# rf_payload_tuple provides the chain of dicts containing dicts, needed for CompareProperty
682683
rf_payload_tuple = (rf_payload, None)
684+
685+
if "UseCases" in interop_profile:
686+
for use_case in interop_profile['UseCases']:
687+
entry_title = use_case.get("UseCaseTitle", "NoName").replace(' ','_')
688+
my_logger.debug('UseCase {}'.format(entry_title))
689+
690+
# Check if we have a valid UseCase
691+
if 'URIs' not in use_case and 'UseCaseKeyProperty' not in use_case:
692+
my_logger.error('UseCase does not have URIs or UseCaseKeyProperty...')
693+
694+
if 'UseCaseKeyProperty' in use_case:
695+
entry_key, entry_comparison, entry_values = use_case['UseCaseKeyProperty'], use_case['UseCaseComparison'], use_case['UseCaseKeyValues']
696+
697+
_, use_case_applies = checkComparison(rf_payload.get(entry_key), entry_comparison, entry_values)
698+
699+
# Check if URI applies to this usecase as well
700+
if 'URIs' in use_case:
701+
use_case_applies = checkInteropURI(propResourceObj, use_case['URIs']) and use_case_applies
702+
703+
elif 'URIs' in use_case:
704+
use_case_applies = checkInteropURI(propResourceObj, use_case['URIs'])
705+
706+
else:
707+
use_case_applies = False
708+
709+
if use_case_applies:
710+
my_msg = msgInterop("UseCase.{}".format(entry_title), '-', '-', '-', sEnum.OK)
711+
712+
msgs.append(my_msg)
713+
714+
my_logger.info('Validating using UseCase {}'.format(entry_title))
715+
716+
# Remove URIs
717+
new_case = {key: val for key, val in use_case.items() if key not in ['URIs']}
718+
719+
new_msgs, new_counts = validateInteropResource(propResourceObj, new_case, rf_payload)
720+
721+
if any([msg.success == sEnum.FAIL for msg in new_msgs]):
722+
my_msg.success = sEnum.FAIL
723+
724+
msgs.extend(new_msgs)
725+
counts.update(new_counts)
726+
727+
else:
728+
my_logger.info('UseCase {} does not apply'.format(entry_title))
729+
730+
return msgs, counts
731+
683732
if "URIs" in interop_profile:
684733
# Check if the profile requirements apply to this particular instance
685734
if not checkInteropURI(propResourceObj, interop_profile['URIs']):

tests/interoptests.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010

1111
import common.interop as riv
1212

13+
import logging
14+
15+
logging.Logger.verbose1 = logging.Logger.debug
16+
logging.Logger.verbose2 = logging.Logger.debug
17+
1318
class ValidatorTest(TestCase):
1419

1520
# can we test writeable, find_prop, conditional
@@ -20,7 +25,7 @@ def test_no_test(self):
2025

2126
def test_requirement(self):
2227
entries = ['Mandatory', 'Recommended', 'Mandatory', 'Recommended']
23-
vals = ['Ok', 'DNE', 'DNE', 'Ok']
28+
vals = ['Ok', riv.REDFISH_ABSENT, rif.REDFISH_ABSENT, 'Ok']
2429
boolist = [True, True, False, True]
2530
for e, v, b in zip(entries, vals, boolist):
2631
self.assertTrue(riv.validateRequirement(e, v)[1] == b, str(e + ' ' + v))
@@ -70,7 +75,7 @@ def test_action(self):
7075
"[email protected]": ["On", "ForceOff"],
7176
"target": "/redfish/v1/Chassis/System.Embedded.1/Actions/Chassis.Reset"}
7277
vals = [interopdict,
73-
'DNE', 'DNE', interopdict, {}]
78+
riv.REDFISH_ABSENT, riv.REDFISH_ABSENT, interopdict, {}]
7479
entries = [{
7580
"ReadRequirement": "Mandatory",
7681
"Parameters": {

validateResource.py

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import traverseInterop
1010
import common.interop as interop
1111
from common.redfish import getType, getNamespace
12+
from common.interop import REDFISH_ABSENT
1213

1314
my_logger = logging.getLogger()
1415
my_logger.setLevel(logging.DEBUG)
@@ -245,6 +246,7 @@ def validateURITree(URI, profile, uriName, expectedType=None, expectedSchema=Non
245246
"Writeable": False,
246247
"URIsFound": [URI.rstrip('/')],
247248
"SubordinateTo": set(),
249+
"UseCasesFound": set()
248250
}
249251

250252
# parent first, then child execution
@@ -299,25 +301,33 @@ def validateURITree(URI, profile, uriName, expectedType=None, expectedSchema=Non
299301
subordinate_tree.append(parentType)
300302
current_parent = current_parent.parent
301303

304+
# Search for UseCase.USECASENAME
305+
usecases_found = [msg.name.split('.')[-1] for msg in linkResults[linkName]['messages'] if 'UseCase' == msg.name.split('.')[0]]
306+
302307
if resource_stats.get(SchemaType) is None:
303308
resource_stats[SchemaType] = {
304309
"Exists": True,
305310
"Writeable": False,
306311
"URIsFound": [link.rstrip('/')],
307312
"SubordinateTo": set([tuple(reversed(subordinate_tree))]),
313+
"UseCasesFound": set(usecases_found),
308314
}
309315
else:
310316
resource_stats[SchemaType]['Exists'] = True
311317
resource_stats[SchemaType]['URIsFound'].append(link.rstrip('/'))
312318
resource_stats[SchemaType]['SubordinateTo'].add(tuple(reversed(subordinate_tree)))
313-
319+
resource_stats[SchemaType]['UseCasesFound'].union(usecases_found)
314320

315321
if refLinks is not currentLinks and len(newLinks) == 0 and len(refLinks) > 0:
316322
currentLinks = refLinks
317323
else:
318324
currentLinks = newLinks
325+
326+
my_logger.info('Service Level Checks')
327+
# NOTE: readrequirements will likely be errors when using --payload outside of root
319328

320329
# For every resource check ReadRequirement
330+
# TODO: verify if IfImplemented should report a fail if any fails exist. Also verify the same for Recommended
321331
resources_in_profile = profile.get('Resources', [])
322332
for resource_type in resources_in_profile:
323333
profile_entry = resources_in_profile[resource_type]
@@ -326,68 +336,90 @@ def validateURITree(URI, profile, uriName, expectedType=None, expectedSchema=Non
326336
msgs = interop.validateComparisonAnyOfAllOf(profile_entry['PropertyRequirements'], resource_type)
327337
message_list.extend(msgs)
328338

329-
apply_requirement, expected_requirement = False, None
339+
does_resource_exist, expected_requirement = False, None
340+
341+
resource_exists, uris_found, subs_found = False, [], []
330342

331343
# If exist and for what URIs...
332344
if resource_type in resource_stats:
333345
resource_exists = resource_stats[resource_type]['Exists']
334346
uris_found = resource_stats[resource_type]['URIsFound']
335347
subs_found = resource_stats[resource_type]['SubordinateTo']
336-
else:
337-
resource_exists = False
338-
uris_found = []
339-
subs_found = []
348+
usecases_found = resource_stats[resource_type]['UseCasesFound']
349+
350+
# Before all else, UseCases takes priority
351+
if 'UseCases' in profile_entry:
352+
# For each use case, apply the Requirement
353+
for use_case in profile_entry['UseCases']:
354+
entry_title = use_case.get("UseCaseTitle", "NoName").replace(' ', '_')
355+
expected_requirement = use_case.get("ReadRequirement", "Mandatory")
356+
uris_applied = use_case.get("URIs")
357+
358+
if uris_applied:
359+
does_resource_exist = any([interop.compareRedfishURI(uris_applied, uri) for uri in uris_found])
360+
else:
361+
does_resource_exist = resource_exists
362+
363+
does_resource_exist = does_resource_exist and entry_title in usecases_found
364+
365+
my_logger.info('Validating UseCase {} of {} ReadRequirement'.format(entry_title, resource_type))
366+
367+
my_msg, _ = interop.validateRequirement(expected_requirement, 'Exists' if does_resource_exist else REDFISH_ABSENT)
368+
my_msg.name = 'UseCase.{}.{}'.format(entry_title, my_msg.name)
369+
if uris_applied:
370+
my_msg.expected = "{} at {}".format(my_msg.expected, ", ".join(uris_applied))
371+
message_list.append(my_msg)
372+
continue
340373

341374
# Check conditionals, if it applies, get its requirement
342-
if "ConditionalRequirements" in profile_entry:
375+
elif "ConditionalRequirements" in profile_entry:
343376
for condition in profile_entry['ConditionalRequirements']:
344377
uris_applied = condition.get("URIs")
345378
subordinate_condition = condition.get("SubordinateToResource")
379+
# Check if we have valid URIs for this conditional
346380
if uris_applied:
347-
apply_requirement = any([interop.compareRedfishURI(uris_applied, uri) for uri in uris_found])
348-
my_logger.info('Checking if any {} in {}: {}'.format(uris_found, uris_applied, apply_requirement))
381+
does_resource_exist = any([interop.compareRedfishURI(uris_applied, uri) for uri in uris_found])
382+
my_logger.info('Checking if any {} in {}: {}'.format(uris_found, uris_applied, does_resource_exist))
383+
# Or check if we are underneath the correct resource chain
349384
elif subordinate_condition:
350-
apply_requirement = any([(tuple((subordinate_condition))) == chain[-len(subordinate_condition):] for chain in subs_found])
351-
my_logger.info('Checking if any {} matches {}: {}'.format([x for x in subs_found], subordinate_condition, apply_requirement))
385+
does_resource_exist = any([(tuple((subordinate_condition))) == chain[-len(subordinate_condition):] for chain in subs_found])
386+
my_logger.info('Checking if any {} matches {}: {}'.format([x for x in subs_found], subordinate_condition, does_resource_exist))
387+
# warn user if Conditional has no appropriate conditions to use
352388
else:
353-
apply_requirement = resource_exists
389+
does_resource_exist = resource_exists
354390
my_logger.warn('This resource {} has no valid Conditional in ConditionalRequirements'.format(resource_type))
355391

392+
# if we have a ReadRequirement...
356393
expected_requirement = condition.get("ReadRequirement")
357394
if expected_requirement:
358395
my_logger.info('Validating {} Conditional ReadRequirement'.format(resource_type))
359-
my_msg, _ = interop.validateRequirement(expected_requirement, 'Exists' if apply_requirement else 'DNE')
396+
my_msg, _ = interop.validateRequirement(expected_requirement, 'Exists' if does_resource_exist else REDFISH_ABSENT)
360397
my_msg.name = '{}.Conditional.{}'.format(resource_type, my_msg.name)
361398
if uris_applied:
362399
my_msg.expected = "{} at {}".format(my_msg.expected, ", ".join(uris_applied))
363400
if subordinate_condition:
364401
my_msg.expected = "{} under {}".format(my_msg.expected, ", ".join(subordinate_condition))
365402
message_list.append(my_msg)
366403

404+
# Outside of ConditionalRequirements, check just for URIs
405+
# TODO: Verify if this should run if ConditionalRequirements exists
367406
expected_requirement = profile_entry.get("ReadRequirement", "Mandatory")
368407
uris_applied = profile_entry.get("URIs")
369408

370409
if uris_applied:
371-
apply_requirement = any([interop.compareRedfishURI(uris_applied, uri) for uri in uris_found])
410+
does_resource_exist = any([interop.compareRedfishURI(uris_applied, uri) for uri in uris_found])
372411
else:
373-
apply_requirement = resource_exists
412+
does_resource_exist = resource_exists
374413

375414
my_logger.info('Validating {} ReadRequirement'.format(resource_type))
376-
my_msg, _ = interop.validateRequirement(expected_requirement, 'Exists' if apply_requirement else 'DNE')
415+
my_msg, _ = interop.validateRequirement(expected_requirement, 'Exists' if does_resource_exist else REDFISH_ABSENT)
377416
my_msg.name = '{}.{}'.format(resource_type, my_msg.name)
378417
if uris_applied:
379418
my_msg.expected = "{} at {}".format(my_msg.expected, ", ".join(uris_applied))
380419
message_list.append(my_msg)
381-
382-
420+
383421
# interop service level checks
384422
finalResults = {}
385-
my_logger.info('Service Level Checks')
386-
if URI not in ["/redfish/v1", "/redfish/v1/"]:
387-
resultEnum = interop.sEnum.WARN
388-
my_logger.info("We are not validating root, warn only")
389-
else:
390-
resultEnum = interop.sEnum.FAIL
391423

392424
for item in message_list:
393425
if item.success == interop.sEnum.WARN:

0 commit comments

Comments
 (0)