diff --git a/redfish_interop_validator/interop.py b/redfish_interop_validator/interop.py index 158ff59..cb465ad 100644 --- a/redfish_interop_validator/interop.py +++ b/redfish_interop_validator/interop.py @@ -767,6 +767,15 @@ def checkInteropURI(r_obj, profile_entry): my_id, my_uri = r_obj.jsondata.get('Id'), r_obj.uri return compareRedfishURI(profile_entry, my_uri) +# Expected Type, Key +entry_type_table = { + 'ChassisType': ("Chassis", "ChassisType"), + 'DriveProtocol': ("Drive", "Protocol"), + 'MemoryType': ("Memory", "MemoryType"), + 'PortProtocol': ("Port", "PortProtocol"), + 'ProcessorType': ("Processor", "ProcessorType"), +} + def validateInteropResource(propResourceObj, interop_profile, rf_payload): """ @@ -783,11 +792,12 @@ def validateInteropResource(propResourceObj, interop_profile, rf_payload): for use_case in interop_profile['UseCases']: entry_title = use_case.get("UseCaseTitle", "NoName").replace(' ', '_') entry_type = use_case.get("UseCaseType", "Normal") + my_parent = propResourceObj.parent my_logger.debug('UseCase {} {}'.format(entry_title, entry_type)) # Check if we have a valid UseCase - if 'URIs' not in use_case and 'UseCaseKeyProperty' not in use_case and entry_type != 'AbsentResource': - my_logger.error('UseCase does not have URIs or UseCaseKeyProperty...') + if ('URIs' not in use_case) and ('UseCaseKeyProperty' not in use_case) and (entry_type not in ['AbsentResource'] + list(entry_type_table.keys())): + my_logger.error('UseCase does not have URIs or valid UseCase...') if entry_type == 'AbsentResource': my_status = rf_payload.get('Status') @@ -797,6 +807,27 @@ def validateInteropResource(propResourceObj, interop_profile, rf_payload): use_case_applies = False if 'URIs' in use_case: use_case_applies = use_case_applies and checkInteropURI(propResourceObj, use_case['URIs']) + + elif entry_type in entry_type_table: + target_type_found = False + target_type, entry_key = entry_type_table[entry_type] + entry_comparison, entry_values = use_case['UseCaseComparison'], use_case['UseCaseKeyValues'] + + # Iterate until we find our target type, if not found then use case cannot apply + while my_parent is not None and not target_type_found: + parent_type = getType(my_parent.jsondata.get('@odata.type', 'NoType')) + target_type_found = parent_type == target_type + if not target_type_found: + my_parent = my_parent.parent + + if target_type_found: + target_payload = my_parent.jsondata + target_value = target_payload.get(entry_key) + + _, use_case_applies = checkComparison(target_payload.get(entry_key, REDFISH_ABSENT), entry_comparison, entry_values) + else: + use_case_applies = False + elif 'UseCaseKeyProperty' in use_case: entry_key, entry_comparison, entry_values = use_case['UseCaseKeyProperty'], use_case['UseCaseComparison'], use_case['UseCaseKeyValues'] diff --git a/redfish_interop_validator/validateResource.py b/redfish_interop_validator/validateResource.py index 832f903..95c5bcc 100644 --- a/redfish_interop_validator/validateResource.py +++ b/redfish_interop_validator/validateResource.py @@ -168,7 +168,7 @@ def validateSingleURI(URI, profile, uriName='', expectedType=None, expectedSchem profile_resources = profile_resources.get(SchemaType) try: propMessages, propCounts = interop.validateInteropResource(resource_obj, profile_resources, jsondata) - messages = messages.extend(propMessages) + messages.extend(propMessages) counts.update(propCounts) my_logger.info('{} of {} tests passed.'.format(counts['pass'] + counts['warn'], counts['totaltests'])) except Exception: @@ -312,8 +312,7 @@ def validateURITree(URI, profile, uriName, expectedType=None, expectedSchema=Non allLinks.add(link.rstrip('/')) results.update(linkResults) - counts.update(linkCounts) - + if not linkSuccess: continue diff --git a/test-profiles/ChassisType_1.json b/test-profiles/ChassisType_1.json new file mode 100644 index 0000000..3d50c95 --- /dev/null +++ b/test-profiles/ChassisType_1.json @@ -0,0 +1,20 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "ChassisType5", + "ProfileVersion": "1.0.0", + "Purpose": "Ensures at least one chassis is an 'Enclosure'. This test should pass.", + "Resources": { + "Thermal": { + "PropertyRequirements": { + "Temperatures": { + "PropertyRequirements": { + "PhysicalContext": { + "Comparison": "AllOf", + "Values": [ "Intake", "CPU" ] + } + } + } + } + } + } +} \ No newline at end of file diff --git a/test-profiles/ChassisType_2.json b/test-profiles/ChassisType_2.json new file mode 100644 index 0000000..f4e2ca4 --- /dev/null +++ b/test-profiles/ChassisType_2.json @@ -0,0 +1,18 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "ChassisType1", + "ProfileVersion": "1.0.0", + "Purpose": "Ensures at least one chassis is an 'Enclosure'. This test should pass (MultiBladeEncl is an Enclosure).", + "Resources": { + "Chassis": { + "PropertyRequirements": { + "ChassisType": { + "Comparison": "AnyOf", + "Values": [ + "Enclosure" + ] + } + } + } + } +} diff --git a/test-profiles/ChassisType_3.json b/test-profiles/ChassisType_3.json new file mode 100644 index 0000000..a5f345d --- /dev/null +++ b/test-profiles/ChassisType_3.json @@ -0,0 +1,19 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "ChassisType2", + "ProfileVersion": "1.0.0", + "Purpose": "Ensures at least one chassis is an 'Enclosure' or a 'Rack'. This test should pass (MultiBladeEncl is an Enclosure).", + "Resources": { + "Chassis": { + "PropertyRequirements": { + "ChassisType": { + "Comparison": "AnyOf", + "Values": [ + "Enclosure", + "Rack" + ] + } + } + } + } +} diff --git a/test-profiles/ChassisType_4.json b/test-profiles/ChassisType_4.json new file mode 100644 index 0000000..0b1cad5 --- /dev/null +++ b/test-profiles/ChassisType_4.json @@ -0,0 +1,19 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "ChassisType3", + "ProfileVersion": "1.0.0", + "Purpose": "Ensures at least one chassis is an 'Enclosure' and a 'Rack'. This test should fail (there are no 'Rack' chassis).", + "Resources": { + "Chassis": { + "PropertyRequirements": { + "ChassisType": { + "Comparison": "AllOf", + "Values": [ + "Enclosure", + "Rack" + ] + } + } + } + } +} diff --git a/test-profiles/ChassisType_5.json b/test-profiles/ChassisType_5.json new file mode 100644 index 0000000..ab5bec2 --- /dev/null +++ b/test-profiles/ChassisType_5.json @@ -0,0 +1,19 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "ChassisType4", + "ProfileVersion": "1.0.0", + "Purpose": "Ensures at least one chassis is an 'Enclosure' and a 'Blade'. This test should pass.", + "Resources": { + "Chassis": { + "PropertyRequirements": { + "ChassisType": { + "Comparison": "AllOf", + "Values": [ + "Enclosure", + "Blade" + ] + } + } + } + } +} diff --git a/test-profiles/ChassisType_6.json b/test-profiles/ChassisType_6.json new file mode 100644 index 0000000..d3f3977 --- /dev/null +++ b/test-profiles/ChassisType_6.json @@ -0,0 +1,18 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "ChassisType5", + "ProfileVersion": "1.0.0", + "Purpose": "Ensures at least one chassis is an 'Enclosure'. This test should pass.", + "Resources": { + "Chassis": { + "PropertyRequirements": { + "ChassisType": { + "Comparison": "AllOf", + "Values": [ + "Enclosure" + ] + } + } + } + } +} diff --git a/test-profiles/UseCaseType_ChassisType.json b/test-profiles/UseCaseType_ChassisType.json new file mode 100644 index 0000000..c1bda91 --- /dev/null +++ b/test-profiles/UseCaseType_ChassisType.json @@ -0,0 +1,23 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "UseCaseType_ChassisType", + "ProfileVersion": "1.0.0", + "Purpose": "Test for UseCaseType 'ChassisType'. Should pass one test for every Sensor in public-rackmount1.", + "Resources": { + "Sensor": { + "UseCases": [ + { + "UseCaseTitle": "Chassis Sensor", + "UseCaseType": "ChassisType", + "UseCaseComparison": "Equal", + "UseCaseKeyValues": [ + "RackMount" + ], + "PropertyRequirements": { + "PhysicalContext": {} + } + } + ] + } + } +} diff --git a/test-profiles/UseCaseType_PortProtocol.json b/test-profiles/UseCaseType_PortProtocol.json new file mode 100644 index 0000000..9b0cc12 --- /dev/null +++ b/test-profiles/UseCaseType_PortProtocol.json @@ -0,0 +1,23 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "UseCaseType_PortProtocol", + "ProfileVersion": "1.0.0", + "Purpose": "Test for UseCaseType 'PortProtocol'. Should pass one test for each PortMetrics in public-acd.", + "Resources": { + "Sensor": { + "UseCases": [ + { + "UseCaseTitle": "Port Metrics", + "UseCaseType": "PortProtocol", + "UseCaseComparison": "Equal", + "UseCaseKeyValues": [ + "RackMount" + ], + "PropertyRequirements": { + "PhysicalContext": {} + } + } + ] + } + } +} diff --git a/test-profiles/UseCaseType_ProcessorType.json b/test-profiles/UseCaseType_ProcessorType.json new file mode 100644 index 0000000..be8d980 --- /dev/null +++ b/test-profiles/UseCaseType_ProcessorType.json @@ -0,0 +1,30 @@ +{ + "SchemaDefinition": "RedfishInteroperabilityProfile.v1_5_0", + "ProfileName": "UseCaseType_PortProtocol", + "ProfileVersion": "1.0.0", + "Purpose": "Test for UseCaseType 'ProcessorType'. Should pass one test for each SubProcessor in public-tower.", + "Resources": { + "Processor": { + "UseCases": [ + { + "UseCaseTitle": "Sub Processor", + "UseCaseType": "ProcessorType", + "UseCaseComparison": "Equal", + "UseCaseKeyValues": [ + "CPU" + ], + "PropertyRequirements": { + "ProcessorType": { + "ReadRequirement": "Mandatory", + "Comparison": "AnyOf", + "Values": [ + "Thread", + "Core" + ] + } + } + } + ] + } + } +} diff --git a/tests/interoptests.py b/tests/interoptests.py index 6e5d471..cd4dc60 100644 --- a/tests/interoptests.py +++ b/tests/interoptests.py @@ -26,10 +26,11 @@ def test_no_test(self): def test_requirement(self): entries = ['Mandatory', 'Recommended', 'Mandatory', 'Recommended'] - vals = ['Ok', riv.REDFISH_ABSENT, riv.REDFISH_ABSENT, 'Ok'] - boolist = [True, True, False, True] + vals = [{}, riv.REDFISH_ABSENT, riv.REDFISH_ABSENT, {}] + boolist = [True, riv.testResultEnum.NA, False, True] for e, v, b in zip(entries, vals, boolist): - self.assertTrue(riv.validateRequirement(e, v)[1] == b, str(e + ' ' + v)) + _msg, result_value = riv.validateRequirement(e, v) + self.assertTrue(result_value == b, str(e + ' ' + str(v))) def test_mincount(self): x = 'x'