def getAnnotations(metadata, decoded, prefix=''): """ Function to gather @ additional props in a payload """ allowed_annotations = ['odata', 'Redfish', 'Privileges', 'Message'] if metadata is not None: schemaObj = metadata.schema_obj else: traverseLogger.warn("Cannot work on annotations without a service or metadata") return False, [] additionalProps = list() # For every ...@ in decoded, check for its presence in refs # get the schema file for it # concat type info together annotationsFound = 0 for key in [k for k in decoded if prefix + '@' in k and '@odata' not in k]: annotationsFound += 1 splitKey = key.split('@', 1) fullItem = splitKey[1] if getNamespace(fullItem) not in allowed_annotations: traverseLogger.error("getAnnotations: {} is not an allowed annotation namespace, please check spelling/capitalization.".format(fullItem)) continue elif metadata is not None: # add the namespace to the set of namespaces referenced by this service metadata.add_service_namespace(getNamespace(fullItem)) annotationSchemaObj = schemaObj.getSchemaFromReference(getNamespace(fullItem)) traverseLogger.debug('{}, {}, {}'.format(key, splitKey, decoded[key])) if annotationSchemaObj is not None: realType = annotationSchemaObj.name realItem = realType + '.' + fullItem.split('.', 1)[1] additionalProps.append( rfSchema.PropItem(annotationSchemaObj, realItem, key, decoded[key])) traverseLogger.debug("Annotations generated: {} out of {}".format(len(additionalProps), annotationsFound)) return True, additionalProps
def __init__(self, name: str, uri: str, jsondata: dict, typename: str, context: str, parent=None, isComplex=False, forceType=False): self.initiated = False self.parent = parent self.uri, self.name = uri, name self.rtime = 0 self.status = -1 self.isRegistry = False self.errorIndex = {} oem = config.get('oemcheck', True) # Check if this is a Registry resource parent_type = parent.typename if parent is not None and parent is not None else None if parent_type is not None and getType( parent_type) == 'MessageRegistryFile': traverseLogger.debug('{} is a Registry resource'.format(self.uri)) self.isRegistry = True self.context = None context = None # Check if we provide a valid json self.jsondata = jsondata traverseLogger.debug("payload: {}".format( json.dumps(self.jsondata, indent=4, sort_keys=True))) if not isinstance(self.jsondata, dict): traverseLogger.error("Resource no longer a dictionary...") raise ValueError('This Resource is no longer a Dictionary') # Check for @odata.id (todo: regex) odata_id = self.jsondata.get('@odata.id') if odata_id is None and not isComplex: if self.isRegistry: traverseLogger.debug( '{}: @odata.id missing, but not required for Registry resource' .format(self.uri)) else: traverseLogger.log( 'SERVICE', '{}: Json does not contain @odata.id'.format(self.uri)) # Get our real type (check for version) acquiredtype = typename if forceType else jsondata.get( '@odata.type', typename) if acquiredtype is None: traverseLogger.error( '{}: Json does not contain @odata.type or NavType'.format( uri)) raise ValueError if acquiredtype is not typename and isComplex: context = None if typename is not None: if not oem and 'OemObject' in typename: acquiredtype = typename if currentService: if not oem and 'OemObject' in acquiredtype: pass else: if jsondata.get('@odata.type') is not None: currentService.metadata.add_service_namespace( getNamespace(jsondata.get('@odata.type'))) if jsondata.get('@odata.context') is not None: # add the namespace to the set of namespaces referenced by this service ns = getNamespace( jsondata.get('@odata.context').split('#')[-1]) if '/' not in ns and not ns.endswith('$entity'): currentService.metadata.add_service_namespace(ns) # Provide a context for this (todo: regex) if context is None: context = self.jsondata.get('@odata.context') if context is None: context = createContext(acquiredtype) if self.isRegistry: # If this is a Registry resource, @odata.context is not required; do our best to construct one traverseLogger.debug( '{}: @odata.context missing from Registry resource; constructed context {}' .format(acquiredtype, context)) elif isComplex: pass else: traverseLogger.debug( '{}: Json does not contain @odata.context'.format( uri)) self.context = context # Get Schema object self.schemaObj = rfSchema.getSchemaObject(acquiredtype, self.context) if self.schemaObj is None: traverseLogger.error( "ResourceObject creation: No schema XML for {} {} {}".format( typename, acquiredtype, self.context)) raise ValueError # Use string comprehension to get highest type if acquiredtype is typename and not forceType: acquiredtype = self.schemaObj.getHighestType(typename, parent_type) if not isComplex: traverseLogger.debug( 'No @odata.type present, assuming highest type {} {}'. format(typename, acquiredtype)) # Check if we provide a valid type (todo: regex) self.typename = acquiredtype typename = self.typename self.initiated = True # get our metadata metadata = currentService.metadata if currentService else None self.typeobj = rfSchema.getTypeObject(typename, self.schemaObj) self.propertyList = self.typeobj.getProperties( self.jsondata, topVersion=getNamespace(typename)) propertyList = [prop.payloadName for prop in self.propertyList] # get additional self.additionalList = [] propTypeObj = self.typeobj if propTypeObj.propPattern is not None and len( propTypeObj.propPattern) > 0: prop_pattern = propTypeObj.propPattern.get('Pattern', '.*') prop_type = propTypeObj.propPattern.get('Type', 'Resource.OemObject') regex = re.compile(prop_pattern) for key in [ k for k in self.jsondata if k not in propertyList and regex.fullmatch(k) ]: val = self.jsondata.get(key) value_obj = rfSchema.PropItem(propTypeObj.schemaObj, propTypeObj.fulltype, key, val, customType=prop_type) self.additionalList.append(value_obj) if config['uricheck'] and self.typeobj.expectedURI is not None: my_id = self.jsondata.get('Id') self.errorIndex[ 'bad_uri_schema_uri'] = not self.typeobj.compareURI( uri, my_id) self.errorIndex[ 'bad_uri_schema_odata'] = not self.typeobj.compareURI( odata_id, my_id) if self.errorIndex['bad_uri_schema_uri']: traverseLogger.error('{}: URI not in Redfish.Uris: {}'.format( uri, self.typename)) if my_id != uri.rsplit('/', 1)[-1]: traverseLogger.error( 'Id {} in payload doesn\'t seem to match URI'.format( my_id)) else: traverseLogger.debug('{} in Redfish.Uris: {}'.format( uri, self.typename)) if self.errorIndex['bad_uri_schema_odata']: traverseLogger.error( '{}: odata_id not in Redfish.Uris: {}'.format( odata_id, self.typename)) if my_id != uri.rsplit('/', 1)[-1]: traverseLogger.error( 'Id {} in payload doesn\'t seem to match URI'.format( my_id)) else: traverseLogger.debug('{} in Redfish.Uris: {}'.format( odata_id, self.typename)) # get annotation successService, annotationProps = getAnnotations( metadata, self.jsondata) if successService: self.additionalList.extend(annotationProps) # list illegitimate properties together self.unknownProperties = [ k for k in self.jsondata if k not in propertyList + [prop.payloadName for prop in self.additionalList] and '@odata' not in k ] self.links = OrderedDict() sample = config.get('sample') linklimits = config.get('linklimits', {}) self.links.update( self.typeobj.getLinksFromType(self.jsondata, self.context, self.propertyList, oem, linklimits, sample)) self.links.update( getAllLinks(self.jsondata, self.additionalList, self.schemaObj, context=context, linklimits=linklimits, sample_size=sample, oemCheck=oem))