def validateMinVersion(version, entry): """ Checks for the minimum version of a resource's type """ rsvLogger.debug('Testing minVersion \n\t' + str((version, entry))) # If version doesn't contain version as is, try it as v#_#_# entry_split = entry.split('.') # get version from payload if (re.match('#([a-zA-Z0-9_.-]*\.)+[a-zA-Z0-9_.-]*', version) is not None): v_payload = rst.getNamespace(version).split('.', 1)[-1] v_payload = v_payload.replace('v', '') if ('_' in v_payload): payload_split = v_payload.split('_') else: payload_split = v_payload.split('.') else: payload_split = version.split('.') paramPass = True for a, b in zip(entry_split, payload_split): b = 0 if b is None else b a = 0 if a is None else a if (b > a): break if (b < a): paramPass = False break # use string comparison, given version numbering is accurate to regex rsvLogger.debug('\tpass ' + str(paramPass)) return msgInterop('MinVersion', '{} ({})'.format(entry, payload_split), '<=', version, paramPass),\ paramPass
def validateMinVersion(fulltype, entry): """ Checks for the minimum version of a resource's type """ fulltype = fulltype.replace('#', '') rsvLogger.info('Testing minVersion \n\t' + str((fulltype, entry))) # If fulltype doesn't contain version as is, try it as v#_#_# versionSplit = entry.split('.') versionNew = 'v' for x in versionSplit: versionNew = versionNew + x + '_' versionNew = versionNew[:-1] # get version from payload v_payload = rst.getNamespace(fulltype).split('.', 1)[-1] # use string comparison, given version numbering is accurate to regex paramPass = v_payload >= (versionNew if 'v' in v_payload else entry) rsvLogger.info('\tpass ' + str(paramPass)) if not paramPass: rsvLogger.error('\tNo Pass') return msgInterop('MinVersion', '{} ({})'.format(entry, versionNew), '<=', fulltype, paramPass),\ paramPass
def validateSingleURI(URI, uriName='', expectedType=None, expectedSchema=None, expectedJson=None, parent=None): # rs-assertion: 9.4.1 # Initial startup here lc = setupLoggingCaptures() next(lc) # Start rsvLogger.verboseout("\n*** %s, %s", uriName, URI) rsvLogger.info("\n*** %s", URI) rsvLogger.debug("\n*** %s, %s, %s", expectedType, expectedSchema is not None, expectedJson is not None) counts = Counter() results = OrderedDict() messages = OrderedDict() results[uriName] = { 'uri': URI, 'success': False, 'counts': counts, 'messages': messages, 'errors': '', 'warns': '', 'rtime': '', 'context': '', 'fulltype': '', 'rcode': 0, 'payload': {} } # check for @odata mandatory stuff # check for version numbering problems # check id if its the same as URI # check @odata.context instead of local. Realize that @odata is NOT a "property" # Attempt to get a list of properties if URI is None: if parent is not None: parentURI = parent.uri else: parentURI = 'MissingParent' URI = parentURI + '/Missing URI Link' rsvLogger.warning( 'Tool appears to be missing vital URI information, replacing URI w/: {}' .format(URI)) # Generate dictionary of property info try: if expectedJson is None: success, jsondata, status, rtime = rst.callResourceURI(URI) results[uriName]['payload'] = jsondata else: results[uriName]['payload'] = expectedJson # verify basic odata strings if results[uriName]['payload'] is not None: successPayload, odataMessages = rst.ResourceObj.checkPayloadConformance( results[uriName]['payload'], URI) messages.update(odataMessages) propResourceObj = rst.createResourceObject(uriName, URI, expectedJson, expectedType, expectedSchema, parent) if not propResourceObj: counts['problemResource'] += 1 results[uriName]['warns'], results[uriName]['errors'] = next(lc) return False, counts, results, None, None except AuthenticationError as e: raise # re-raise exception except Exception as e: rsvLogger.debug('Exception caught while creating ResourceObj', exc_info=1) rsvLogger.error('Unable to gather property info for URI {}: {}'.format( URI, repr(e))) counts['exceptionResource'] += 1 results[uriName]['warns'], results[uriName]['errors'] = next(lc) return False, counts, results, None, None counts['passGet'] += 1 # verify odata_id properly resolves to its parent if holding fragment odata_id = propResourceObj.jsondata.get('@odata.id', 'void') if '#' in odata_id: if parent is not None: payload_resolve = rst.navigateJsonFragment(parent.jsondata, URI) if payload_resolve is None: rsvLogger.error( '@odata.id of ReferenceableMember does not contain a valid JSON pointer for this payload: {}' .format(odata_id)) counts['badOdataIdResolution'] += 1 elif payload_resolve != propResourceObj.jsondata: rsvLogger.error( '@odata.id of ReferenceableMember does not point to the correct object: {}' .format(odata_id)) counts['badOdataIdResolution'] += 1 else: rsvLogger.warn( 'No parent found with which to test @odata.id of ReferenceableMember' ) if not successPayload: counts['failPayloadError'] += 1 rsvLogger.error( str(URI) + ': payload error, @odata property non-conformant', ) # if URI was sampled, get the notation text from rst.uri_sample_map sample_string = rst.uri_sample_map.get(URI) sample_string = sample_string + ', ' if sample_string is not None else '' results[uriName]['uri'] = (str(URI)) results[uriName]['samplemapped'] = (str(sample_string)) results[uriName]['rtime'] = propResourceObj.rtime results[uriName]['rcode'] = propResourceObj.status results[uriName]['payload'] = propResourceObj.jsondata results[uriName]['context'] = propResourceObj.context results[uriName]['origin'] = propResourceObj.schemaObj.origin results[uriName]['fulltype'] = propResourceObj.typename results[uriName]['success'] = True rsvLogger.info("\t Type (%s), GET SUCCESS (time: %s)", propResourceObj.typename, propResourceObj.rtime) # If this is an AttributeRegistry, load it for later use if isinstance(propResourceObj.jsondata, dict): odata_type = propResourceObj.jsondata.get('@odata.type') if odata_type is not None: namespace = odata_type.split('.')[0] type_name = odata_type.split('.')[-1] if namespace == '#AttributeRegistry' and type_name == 'AttributeRegistry': loadAttributeRegDict(odata_type, propResourceObj.jsondata) for prop in propResourceObj.getResourceProperties(): try: if not prop.valid and not prop.exists: continue propMessages, propCounts = checkPropertyConformance( propResourceObj.schemaObj, prop.name, prop, propResourceObj.jsondata, parentURI=URI) if '@Redfish.Copyright' in propMessages and 'MessageRegistry' not in propResourceObj.typeobj.fulltype: modified_entry = list(propMessages['@Redfish.Copyright']) modified_entry[-1] = 'FAIL' propMessages['@Redfish.Copyright'] = tuple(modified_entry) rsvLogger.error( '@Redfish.Copyright is only allowed for mockups, and should not be allowed in official implementations' ) if prop.payloadName != prop.propChild: propCounts['invalidName'] += 1 for propMsg in propMessages: modified_entry = list(propMessages[propMsg]) modified_entry[-1] = 'Invalid' propMessages[propMsg] = tuple(modified_entry) if not prop.valid: rsvLogger.error( 'Verifying property that does not belong to this version: {}' .format(prop.name)) for propMsg in propMessages: propCounts['invalidEntry'] += 1 modified_entry = list(propMessages[propMsg]) modified_entry[-1] = 'Invalid' propMessages[propMsg] = tuple(modified_entry) messages.update(propMessages) counts.update(propCounts) except AuthenticationError as e: raise # re-raise exception except Exception as ex: rsvLogger.debug('Exception caught while validating single URI', exc_info=1) rsvLogger.error( '{}: Could not finish check on this property ({})'.format( prop.name, str(ex))) counts['exceptionPropCheck'] += 1 uriName, SchemaFullType, jsonData = propResourceObj.name, propResourceObj.typeobj.fulltype, propResourceObj.jsondata SchemaNamespace, SchemaType = rst.getNamespace( SchemaFullType), rst.getType(SchemaFullType) # List all items checked and unchecked # current logic does not check inside complex types fmt = '%-30s%30s' rsvLogger.verboseout('%s, %s, %s', uriName, SchemaNamespace, SchemaType) for key in jsonData: item = jsonData[key] rsvLogger.verboseout(fmt % (key, messages[key][3] if key in messages else 'Exists, no schema check')) allowAdditional = propResourceObj.typeobj.additional for key in [ k for k in jsonData if k not in messages and k not in propResourceObj.unknownProperties ] + propResourceObj.unknownProperties: # note: extra messages for "unchecked" properties if not allowAdditional: rsvLogger.error( '{} not defined in schema {} (check version, spelling and casing)' .format(key, SchemaNamespace)) counts['failAdditional'] += 1 messages[key] = (displayValue(item), '-', '-', 'FAIL') else: rsvLogger.warn( '{} not defined in schema {} (check version, spelling and casing)' .format(key, SchemaNamespace)) counts['unverifiedAdditional'] += 1 messages[key] = (displayValue(item), '-', '-', 'Additional') for key in messages: if key not in jsonData: rsvLogger.verboseout(fmt % (key, messages[key][3])) results[uriName]['warns'], results[uriName]['errors'] = next(lc) pass_val = len(results[uriName]['errors']) == 0 for key in counts: if any(x in key for x in ['problem', 'fail', 'bad', 'exception']): pass_val = False break rsvLogger.info("\t {}".format('PASS' if pass_val else ' FAIL...')) rsvLogger.verboseout('%s, %s', SchemaFullType, counts) # Get all links available rsvLogger.debug(propResourceObj.links) return True, counts, results, propResourceObj.links, propResourceObj
def validateSingleURI(URI, profile, uriName='', expectedType=None, expectedSchema=None, expectedJson=None, parent=None): """ Validates a single URI that is given, returning its ResourceObject, counts and links """ # rs-assertion: 9.4.1 # Initial startup here lc = setupLoggingCaptures() next(lc) # Start counts = Counter() results = OrderedDict() messages = [] results[uriName] = { 'uri': URI, 'success': False, 'counts': counts, 'messages': messages, 'errors': '', 'warns': '', 'rtime': '', 'context': '', 'fulltype': '' } # check for @odata mandatory stuff # check for version numbering problems # check id if its the same as URI # check @odata.context instead of local. Realize that @odata is NOT a "property" # Attempt to get a list of properties if URI is None: if parent is not None: parentURI = parent.uri else: parentURI = '...' URI = parentURI + '...' if expectedJson is None: successGet, jsondata, status, rtime = rst.callResourceURI(URI) else: successGet, jsondata = True, expectedJson if jsondata is not None: successPayload, odataMessages = rst.ResourceObj.checkPayloadConformance( jsondata, URI) if not successPayload: counts['failPayloadWarn'] += 1 rsvLogger.verboseout( str(URI) + ': payload error, @odata property non-conformant', ) # Generate dictionary of property info try: propResourceObj = rst.createResourceObject(uriName, URI, expectedJson, expectedType, expectedSchema, parent) if not propResourceObj: counts['problemResource'] += 1 results[uriName]['warns'], results[uriName]['errors'] = next(lc) return False, counts, results, None, None except AuthenticationError: raise # re-raise exception except Exception: rsvLogger.exception("") counts['exceptionResource'] += 1 results[uriName]['warns'], results[uriName]['errors'] = next(lc) return False, counts, results, None, None counts['passGet'] += 1 # if URI was sampled, get the notation text from rst.uri_sample_map sample_string = rst.uri_sample_map.get(URI) sample_string = sample_string + ', ' if sample_string is not None else '' results[uriName]['uri'] = (str(URI)) results[uriName]['samplemapped'] = (str(sample_string)) results[uriName]['rtime'] = propResourceObj.rtime results[uriName]['context'] = propResourceObj.context results[uriName]['origin'] = propResourceObj.schemaObj.origin results[uriName]['fulltype'] = propResourceObj.typeobj.fulltype results[uriName]['success'] = True rsvLogger.debug("\t URI {}, Type ({}), GET SUCCESS (time: {})".format( URI, propResourceObj.typeobj.stype, propResourceObj.rtime)) uriName, SchemaFullType, jsondata = propResourceObj.name, propResourceObj.typeobj.fulltype, propResourceObj.jsondata SchemaNamespace, SchemaType = rst.getNamespace( SchemaFullType), rst.getType(SchemaFullType) objRes = profile.get('Resources') if SchemaType not in objRes: rsvLogger.debug('\nNo Such Type in sample {} {}.{}, skipping'.format( URI, SchemaNamespace, SchemaType)) else: rsvLogger.info("\n*** %s, %s", URI, SchemaType) rsvLogger.debug("\n*** %s, %s, %s", expectedType, expectedSchema is not None, expectedJson is not None) objRes = objRes.get(SchemaType) try: propMessages, propCounts = commonInterop.validateInteropResource( propResourceObj, objRes, jsondata) messages = messages.extend(propMessages) counts.update(propCounts) rsvLogger.info('{} of {} tests passed.'.format( counts['pass'] + counts['warn'], counts['totaltests'])) except Exception: rsvLogger.exception("Something went wrong") rsvLogger.error( 'Could not finish validation check on this payload') counts['exceptionProfilePayload'] += 1 rsvLogger.info('%s, %s\n', SchemaFullType, counts) # Get all links available results[uriName]['warns'], results[uriName]['errors'] = next(lc) rsvLogger.debug(propResourceObj.links) return True, counts, results, propResourceObj.links, propResourceObj
def validateEntity(name: str, val: dict, propType: str, propCollectionType: str, schemaObj, autoExpand, parentURI=""): """ Validates an entity based on its uri given """ rsvLogger.debug('validateEntity: name = {}'.format(name)) # check for required @odata.id if '@odata.id' not in val: if autoExpand: default = parentURI + '#/{}'.format( name.replace('[', '/').strip(']')) else: default = parentURI + '/{}'.format(name) rsvLogger.error( "{}: EntityType resource does not contain required @odata.id property, attempting default {}" .format(name, default)) if parentURI == "": return False uri = default else: uri = val['@odata.id'] # check if the entity is truly what it's supposed to be paramPass = False # if not autoexpand, we must grab the resource if not autoExpand: success, data, status, delay = rst.callResourceURI(uri) else: success, data, status, delay = True, val, 200, 0 rsvLogger.debug( '(success, uri, status, delay) = {}, (propType, propCollectionType) = {}, data = {}' .format((success, uri, status, delay), (propType, propCollectionType), data)) # if the reference is a Resource, save us some trouble as most/all basetypes are Resource generics = [ 'Resource.ItemOrCollection', 'Resource.ResourceCollection', 'Resource.Item', 'Resource.Resource' ] if (propCollectionType in generics or propType in generics) and success: return True elif success: # Attempt to grab an appropriate type to test against and its schema # Default lineup: payload type, collection type, property type currentType = data.get('@odata.type', propCollectionType) if currentType is None: currentType = propType soup, refs = schemaObj.soup, schemaObj.refs baseLink = refs.get( rst.getNamespace(propCollectionType if propCollectionType is not None else propType)) # if schema in current schema, then use it # elif namespace in References, use that # else we have no lead if soup.find('Schema', attrs={'Namespace': rst.getNamespace(currentType)}) is not None: success, baseObj = True, schemaObj elif baseLink is not None: baseObj = schemaObj.getSchemaFromReference( rst.getNamespaceUnversioned(currentType)) success = baseObj is not None else: success = False if not success: rsvLogger.error( "Schema of target {} not referenced in current resource, concluding type {} is not of expected type {}" .format(uri, currentType, propType)) rsvLogger.debug('success = {}, currentType = {}, baseLink = {}'.format( success, currentType, baseLink)) # Recurse through parent types, gather type hierarchy to check against if success and currentType is not None and baseObj.getTypeTagInSchema( currentType) is None and success: rsvLogger.error( '{}: Linked resource reports version {} not in Schema {}'. format(name.split(':')[-1], currentType, baseObj.origin)) elif success and currentType is not None: currentType = currentType.replace('#', '') allTypes = [] while currentType not in allTypes and success: allTypes.append(currentType) success, baseObj, currentType = baseObj.getParentType( currentType, 'EntityType') rsvLogger.debug('success = {}, currentType = {}'.format( success, currentType)) rsvLogger.debug( 'propType = {}, propCollectionType = {}, allTypes = {}'.format( propType, propCollectionType, allTypes)) paramPass = propType in allTypes or propCollectionType in allTypes if not paramPass: full_namespace = propCollectionType if propCollectionType is not None else propType rsvLogger.error( '{}: Linked resource reports schema version (or namespace): {} not found in typechain' .format(name.split(':')[-1], full_namespace)) else: rsvLogger.error( "{}: Could not get schema file for Entity check".format(name)) else: if "OriginOfCondition" in name: rsvLogger.verboseout( "{}: GET of resource at URI {} returned HTTP {}, but was a temporary resource." .format( name, uri, status if isinstance(status, int) and status >= 200 else "error")) return True else: rsvLogger.error( "{}: GET of resource at URI {} returned HTTP {}. Check URI.". format( name, uri, status if isinstance(status, int) and status >= 200 else "error")) return paramPass