def __init__(self, schemaObj, propOwner, propChild, val, topVersion=None, customType=None, payloadName=None, versionList=None, parent=None, top_of_resource=None):
        try:
            self.name = propOwner + ':' + propChild
            self.propOwner, self.propChild = propOwner, propChild
            self.val = val
            highest_version_of_payload = [1, 0, 0]
            if top_of_resource is not None:
                highest_version_of_payload = splitVersionString(getNamespace(top_of_resource.typename))
            self.valid = topVersion is None or\
                    versionList is None or\
                    (getNamespace( propOwner ) in versionList) or\
                    (splitVersionString(propOwner) <= splitVersionString(topVersion)) or\
                    (splitVersionString(propOwner) <= (highest_version_of_payload))
            self.exists = val != 'n/a'
            self.payloadName = payloadName if payloadName is not None else propChild
            self.propDict = getPropertyDetails(
                schemaObj, propOwner, propChild, val, topVersion, customType, parent=parent, top_of_resource=top_of_resource)
            self.attr = self.propDict['attrs']

        except Exception as ex:
            rst.traverseLogger.debug('Exception caught while creating new PropItem', exc_info=1)
            rst.traverseLogger.error(
                    '{}:{} :  Could not get details on this property ({})'.format(str(propOwner), str(propChild), str(ex)))
            self.propDict = None
            self.attr = None
            return
        pass
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
Пример #3
0
    def getProperties(self, jsondata, topVersion=None, top_of_resource=None):
        node = self
        allPropList = []
        # collect all properties
        while node is not None:
            allPropList.extend(node.propList)
            node = node.parent

        props = []
        for prop in allPropList:
            schemaObj, newPropOwner, newProp = prop
            val = jsondata.get(newProp, 'n/a')
            pname = newProp
            # if our val is empty, do fuzzy check for property that exists in payload but not in all properties
            if val == 'n/a':
                pname = get_fuzzy_property(newProp, jsondata, allPropList)
            validTypes = [getNamespace(x) for x in self.getTypeChain()]
            if (topVersion in validTypes):
                validTypes = validTypes[validTypes.index(topVersion):]
            props.append(
                PropItem(schemaObj,
                         newPropOwner,
                         newProp,
                         val,
                         topVersion,
                         payloadName=pname,
                         versionList=validTypes,
                         top_of_resource=top_of_resource))

        return props
Пример #4
0
    def getParentType(self,
                      currentType,
                      tagType=['EntityType', 'ComplexType']):
        """getParentType

        Get parent of this Entity/ComplexType

        :param currentType: type string
        :param tagType: Array or single string containing the xml tag name
        """
        currentType = currentType.replace('#', '')
        typetag = self.getTypeTagInSchema(currentType, tagType)
        if typetag is not None:
            currentType = typetag.get('BaseType')
            if currentType is None:
                return False, None, None
            typetag = self.getTypeTagInSchema(currentType, tagType)
            if typetag is not None:
                return True, self, currentType
            else:
                namespace = getNamespace(currentType)
                schemaObj = self.getSchemaFromReference(namespace)
                if schemaObj is None:
                    return False, None, None
                propSchema = schemaObj.soup.find(
                    'Schema', attrs={'Namespace': namespace})
                if propSchema is None:
                    return False, None, None
                return True, schemaObj, currentType
        else:
            return False, None, None
Пример #5
0
    def __init__(self,
                 schemaObj,
                 propOwner,
                 propChild,
                 val,
                 topVersion=None,
                 customType=None,
                 payloadName=None):
        try:
            self.name = propOwner + ':' + propChild
            self.propOwner, self.propChild = propOwner, propChild
            self.val = val
            self.valid = topVersion is None or \
                    (getNamespaceUnversioned(propOwner) in topVersion and getNamespace(propOwner) <= topVersion)\
                    or getNamespaceUnversioned(propOwner) not in topVersion
            self.exists = val != 'n/a'
            self.payloadName = payloadName if payloadName is not None else propChild
            self.propDict = getPropertyDetails(schemaObj, propOwner, propChild,
                                               val, topVersion, customType)
            self.attr = self.propDict['attrs']

        except Exception as ex:
            rst.traverseLogger.debug(
                'Exception caught while creating new PropItem', exc_info=1)
            rst.traverseLogger.error(
                '{}:{} :  Could not get details on this property ({})'.format(
                    str(propOwner), str(propChild), str(ex)))
            self.propDict = None
            self.attr = None
            return
        pass
    def getSchemaFromReference(self, namespace):
        """getSchemaFromReference

        Get SchemaObj from generated references

        :param namespace: Namespace of reference
        """
        tup = self.refs.get(namespace)
        tupVersionless = self.refs.get(getNamespace(namespace))
        if tup is None:
            if tupVersionless is None:
                rst.traverseLogger.warning('No such reference {} in {}'.format(namespace, self.origin))
                return None
            else:
                tup = tupVersionless
                rst.traverseLogger.warning('No such reference {} in {}, using unversioned'.format(namespace, self.origin))
        typ, uri = tup
        newSchemaObj = getSchemaObject(typ, uri)
        return newSchemaObj
    def getTypeTagInSchema(self, currentType, tagType=['EntityType', 'ComplexType']):
        """getTypeTagInSchema

        Get type tag in schema

        :param currentType: type string
        :param tagType: Array or single string containing the xml tag name
        """
        pnamespace, ptype = getNamespace(currentType), getType(currentType)
        soup = self.soup

        currentSchema = soup.find(
            'Schema', attrs={'Namespace': pnamespace})

        if currentSchema is None:
            return None

        currentEntity = currentSchema.find(tagType, attrs={'Name': ptype}, recursive=False)

        return currentEntity
Пример #8
0
    def __init__(self, typename, schemaObj):
        # if we've generated this type, use it, else generate type
        self.initiated = False
        self.fulltype = typename
        self.snamespace, self.stype = getNamespace(self.fulltype), getType(
            self.fulltype)
        self.schemaObj = schemaObj

        self.parent = None

        self.propList = []
        self.actionList = []
        self.propPattern = None
        self.additional = False
        self.expectedURI = None

        # get all properties and actions in Type chain
        success, currentSchemaObj, baseType = True, self.schemaObj, self.fulltype
        try:
            newPropList, newActionList, self.additional, self.propPattern, self.expectedURI = getTypeDetails(
                currentSchemaObj, baseType)

            self.propList.extend(newPropList)
            self.actionList.extend(newActionList)

            success, currentSchemaObj, baseType = currentSchemaObj.getParentType(
                baseType)
            if success:
                self.parent = PropType(baseType, currentSchemaObj)
                if not self.additional:
                    self.additional = self.parent.additional
                if self.expectedURI is None:
                    self.expectedURI = self.parent.expectedURI
        except Exception as ex:
            rst.traverseLogger.debug(
                'Exception caught while creating new PropType', exc_info=1)
            rst.traverseLogger.error('{}:  Getting type failed for {}'.format(
                str(self.fulltype), str(baseType)))
            raise ex

        self.initiated = True
Пример #9
0
def getPropertyDetails(schemaObj,
                       propertyOwner,
                       propertyName,
                       val,
                       topVersion=None,
                       customType=None,
                       parent=None,
                       top_of_resource=None):
    """
    Get dictionary of tag attributes for properties given, including basetypes.

    param arg1: soup data
    param arg2: references
    ...
    """

    propEntry = dict()
    propEntry['val'] = val
    if val == 'n/a':
        val = None
    OwnerNamespace, OwnerType = getNamespace(propertyOwner), getType(
        propertyOwner)
    rst.traverseLogger.debug('___')
    rst.traverseLogger.debug('{}, {}:{}'.format(OwnerNamespace, propertyOwner,
                                                propertyName))

    soup, refs = schemaObj.soup, schemaObj.refs

    if customType is None:
        # Get Schema of the Owner that owns this prop
        ownerSchema = soup.find('Schema', attrs={'Namespace': OwnerNamespace})

        if ownerSchema is None:
            rst.traverseLogger.warning(
                "getPropertyDetails: Schema could not be acquired,  {}".format(
                    OwnerNamespace))
            return None

        # Get Entity of Owner, then the property of the Property we're targeting
        ownerEntity = ownerSchema.find(['EntityType', 'ComplexType'],
                                       attrs={'Name': OwnerType},
                                       recursive=False)

        # check if this property is a nav property
        # Checks if this prop is an annotation
        success, propertySoup, propertyRefs, propertyFullType = True, soup, refs, OwnerType

        if '@' not in propertyName:
            propEntry['isTerm'] = False  # not an @ annotation
            propertyTag = ownerEntity.find(['NavigationProperty', 'Property'],
                                           attrs={'Name': propertyName},
                                           recursive=False)

            # start adding attrs and props together
            propertyInnerTags = propertyTag.find_all(recursive=False)
            for tag in propertyInnerTags:
                if (not tag.get('Term')):
                    rst.traverseLogger.warn(tag,
                                            'does not contain a Term name')
                elif (tag.get('Term') == 'Redfish.Revisions'):
                    propEntry[tag['Term']] = tag.find_all('Record')
                else:
                    propEntry[tag['Term']] = tag.attrs
            propertyFullType = propertyTag.get('Type')
        else:
            propEntry['isTerm'] = True
            ownerEntity = ownerSchema.find(['Term'],
                                           attrs={'Name': OwnerType},
                                           recursive=False)
            if ownerEntity is None:
                ownerEntity = ownerSchema.find(['EntityType', 'ComplexType'],
                                               attrs={'Name': OwnerType},
                                               recursive=False)
            propertyTag = ownerEntity
            propertyFullType = propertyTag.get('Type', propertyOwner)

        propEntry['isNav'] = propertyTag.name == 'NavigationProperty'
        propEntry['attrs'] = propertyTag.attrs
        rst.traverseLogger.debug(propEntry)

        propEntry['realtype'] = 'none'

    else:
        propertyFullType = customType
        propEntry['realtype'] = 'none'
        propEntry['attrs'] = dict()
        propEntry['attrs']['Type'] = customType
        serviceRefs = rst.currentService.metadata.get_service_refs()
        serviceSchemaSoup = rst.currentService.metadata.get_soup()
        success, propertySoup, propertyRefs, propertyFullType = True, serviceSchemaSoup, serviceRefs, customType

    # find the real type of this, by inheritance
    while propertyFullType is not None:
        rst.traverseLogger.debug("HASTYPE")
        PropertyNamespace, PropertyType = getNamespace(
            propertyFullType), getType(propertyFullType)

        rst.traverseLogger.debug('{}, {}'.format(PropertyNamespace,
                                                 propertyFullType))

        # Type='Collection(Edm.String)'
        # If collection, check its inside type
        if re.match('Collection\(.*\)', propertyFullType) is not None:
            if val is not None and not isinstance(val, list):
                raise TypeError(
                    'This collection is not a List: {}'.format(val))
            propertyFullType = propertyFullType.replace('Collection(',
                                                        "").replace(')', "")
            propEntry['isCollection'] = propertyFullType
            continue
        else:
            if val is not None and isinstance(
                    val, list) and propEntry.get('isCollection') is None:
                raise TypeError('This item should not be a List')

        # If basic, just pass itself
        if 'Edm' in propertyFullType:
            propEntry['realtype'] = propertyFullType
            break

        # get proper soup, check if this Namespace is the same as its Owner, otherwise find its SchemaXml
        if PropertyNamespace.split('.')[0] != OwnerNamespace.split('.')[0]:
            schemaObj = schemaObj.getSchemaFromReference(PropertyNamespace)
            success = schemaObj is not None
            if success:
                uri = schemaObj.origin
                propertySoup = schemaObj.soup
                propertyRefs = schemaObj.refs
        else:
            success, propertySoup, uri = True, soup, 'of parent'

        if not success:
            rst.traverseLogger.warning(
                "getPropertyDetails: Could not acquire appropriate Schema for this item, {} {} {}"
                .format(propertyOwner, PropertyNamespace, propertyName))
            return propEntry

        # traverse tags to find the type
        propertySchema = propertySoup.find(
            'Schema', attrs={'Namespace': PropertyNamespace})
        if propertySchema is None:
            rst.traverseLogger.warning(
                'Schema element with Namespace attribute of {} not found in schema file {}'
                .format(PropertyNamespace, uri))
            break
        propertyTypeTag = propertySchema.find(
            ['EnumType', 'ComplexType', 'EntityType', 'TypeDefinition'],
            attrs={'Name': PropertyType},
            recursive=False)
        nameOfTag = propertyTypeTag.name if propertyTypeTag is not None else 'None'

        # perform more logic for each type
        if nameOfTag == 'TypeDefinition':  # Basic type
            # This piece of code is rather simple UNLESS this is an "enumeration"
            #   this is a unique deprecated enum, labeled as Edm.String

            propertyFullType = propertyTypeTag.get('UnderlyingType')
            isEnum = propertyTypeTag.find(
                'Annotation',
                attrs={'Term': 'Redfish.Enumeration'},
                recursive=False)

            if propertyFullType == 'Edm.String' and isEnum is not None:
                propEntry['realtype'] = 'deprecatedEnum'
                propEntry['typeprops'] = list()
                memberList = isEnum.find('Collection').find_all(
                    'PropertyValue')

                for member in memberList:
                    propEntry['typeprops'].append(member.get('String'))
                rst.traverseLogger.debug("{}".format(propEntry['typeprops']))
                break
            else:
                continue

        elif nameOfTag == 'ComplexType':  # go deeper into this type
            rst.traverseLogger.debug("go deeper in type")

            # We need to find the highest existence of this type vs topVersion schema
            # not ideal, but works for this solution
            success, baseSoup, baseRefs, baseType = True, propertySoup, propertyRefs, propertyFullType

            # If we're outside of our normal Soup, then do something different, otherwise elif
            if PropertyNamespace.split('.')[0] != OwnerNamespace.split(
                    '.')[0] and not customType:
                typelist = []
                schlist = []
                for schema in baseSoup.find_all('Schema'):
                    if schema.find('ComplexType', attrs={'Name': PropertyType
                                                         }) is None:
                        continue
                    newNamespace = schema.get('Namespace')
                    typelist.append(newNamespace)
                    schlist.append(schema)
                for item, schema in reversed(sorted(zip(typelist, schlist))):
                    rst.traverseLogger.debug(
                        "Working backwards: {}   {}".format(
                            item, getType(baseType)))
                    baseType = item + '.' + getType(baseType)
                    break
            elif topVersion is not None and (topVersion > OwnerNamespace):
                currentVersion = topVersion
                currentSchema = baseSoup.find(
                    'Schema', attrs={'Namespace': currentVersion})
                # Working backwards from topVersion schematag,
                #   created expectedType, check if currentTypeTag exists
                #   if it does, use our new expectedType, else continue down parent types
                #   until we exhaust all schematags in file
                while currentSchema is not None:
                    expectedType = currentVersion + '.' + PropertyType
                    currentTypeTag = currentSchema.find(
                        'ComplexType', attrs={'Name': PropertyType})
                    if currentTypeTag is not None:
                        baseType = expectedType
                        rst.traverseLogger.debug('new type: ' + baseType)
                        break
                    else:
                        nextEntity = currentSchema.find(
                            ['EntityType', 'ComplexType'],
                            attrs={'Name': OwnerType})
                        if nextEntity is None:
                            baseType = schemaObj.getHighestType(
                                baseType, topVersion)
                            break
                        nextType = nextEntity.get('BaseType')
                        currentVersion = getNamespace(nextType)
                        currentSchema = baseSoup.find(
                            'Schema', attrs={'Namespace': currentVersion})
                        continue
            propEntry['realtype'] = 'complex'
            if propEntry.get('isCollection') is None:
                propEntry['typeprops'] = rst.createResourceObject(
                    propertyName,
                    'complex',
                    val,
                    context=schemaObj.context,
                    typename=baseType,
                    isComplex=True,
                    topVersion=topVersion,
                    top_of_resource=top_of_resource)
            else:
                val = val if val is not None else []
                propEntry['typeprops'] = [
                    rst.createResourceObject(propertyName,
                                             'complex',
                                             item,
                                             context=schemaObj.context,
                                             typename=baseType,
                                             isComplex=True,
                                             topVersion=topVersion,
                                             top_of_resource=top_of_resource)
                    for item in val
                ]
            break

        elif nameOfTag == 'EnumType':  # If enum, get all members
            propEntry['realtype'] = 'enum'
            propEntry['typeprops'] = list()
            for MemberName in propertyTypeTag.find_all('Member'):
                propEntry['typeprops'].append(MemberName['Name'])
            break

        elif nameOfTag == 'EntityType':  # If entity, do nothing special (it's a reference link)
            propEntry['realtype'] = 'entity'
            if val is not None:
                if propEntry.get('isCollection') is None:
                    val = [val]
                val = val if val is not None else []
                for innerVal in val:
                    linkURI = innerVal.get('@odata.id')
                    autoExpand = propEntry.get('OData.AutoExpand', None) is not None or\
                        propEntry.get('OData.AutoExpand'.lower(), None) is not None
                    linkType = propertyFullType
                    linkSchema = propertyFullType
                    innerJson = innerVal
                    propEntry[
                        'typeprops'] = linkURI, autoExpand, linkType, linkSchema, innerJson
            else:
                propEntry['typeprops'] = None
            rst.traverseLogger.debug("typeEntityTag found {}".format(
                propertyTypeTag['Name']))
            break

        else:
            rst.traverseLogger.error(
                'Type {} not found under namespace {} in schema {}'.format(
                    PropertyType, PropertyNamespace, uri))
            break

    return propEntry
Пример #10
0
def getTypeDetails(schemaObj, SchemaAlias):
    """
    Gets list of surface level properties for a given SchemaType,
    """
    PropertyList = list()
    ActionList = list()
    PropertyPattern = None
    additional = False

    soup, refs = schemaObj.soup, schemaObj.refs

    SchemaNamespace, SchemaType = getNamespace(SchemaAlias), getType(
        SchemaAlias)

    rst.traverseLogger.debug("Generating type: {}".format(SchemaAlias))
    rst.traverseLogger.debug("Schema is {}, {}".format(SchemaType,
                                                       SchemaNamespace))

    innerschema = soup.find('Schema', attrs={'Namespace': SchemaNamespace})

    if innerschema is None:
        uri = schemaObj.origin
        rst.traverseLogger.error(
            'getTypeDetails: Schema namespace {} not found in schema file {}. Will not be able to gather type details.'
            .format(SchemaNamespace, uri))
        return PropertyList, ActionList, False, PropertyPattern, '.*'

    element = innerschema.find(['EntityType', 'ComplexType'],
                               attrs={'Name': SchemaType},
                               recursive=False)

    if element is None:
        uri = schemaObj.origin
        rst.traverseLogger.error(
            'getTypeDetails: Element {} not found in schema namespace {}. Will not be able to gather type details.'
            .format(SchemaType, SchemaNamespace))
        return PropertyList, ActionList, False, PropertyPattern, '.*'

    rst.traverseLogger.debug("___")
    rst.traverseLogger.debug(element.get('Name'))
    rst.traverseLogger.debug(element.attrs)
    rst.traverseLogger.debug(element.get('BaseType'))

    additionalElement = element.find(
        'Annotation', attrs={'Term': 'OData.AdditionalProperties'})
    additionalElementOther = element.find(
        'Annotation', attrs={'Term': 'Redfish.DynamicPropertyPatterns'})
    uriElement = element.find('Annotation', attrs={'Term': 'Redfish.Uris'})

    if additionalElement is not None:
        additional = additionalElement.get('Bool', False)
        if additional in ['false', 'False', False]:
            additional = False
        if additional in ['true', 'True']:
            additional = True
    else:
        additional = False

    if additionalElementOther is not None:
        # create PropertyPattern dict containing pattern and type for DynamicPropertyPatterns validation
        rst.traverseLogger.debug(
            'getTypeDetails: Redfish.DynamicPropertyPatterns found, element = {}, SchemaAlias = {}'
            .format(element, SchemaAlias))
        pattern_elem = additionalElementOther.find("PropertyValue",
                                                   Property="Pattern")
        pattern = prop_type = None
        if pattern_elem is not None:
            pattern = pattern_elem.get("String")
        type_elem = additionalElementOther.find("PropertyValue",
                                                Property="Type")
        if type_elem is not None:
            prop_type = type_elem.get("String")
        rst.traverseLogger.debug(
            'getTypeDetails: pattern = {}, type = {}'.format(
                pattern, prop_type))
        if pattern is not None and prop_type is not None:
            PropertyPattern = dict()
            PropertyPattern['Pattern'] = pattern
            PropertyPattern['Type'] = prop_type
        additional = True

    expectedURI = None
    if uriElement is not None:
        try:
            all_strings = uriElement.find('Collection').find_all('String')
            expectedURI = [e.contents[0] for e in all_strings]
        except Exception as e:
            rst.traverseLogger.debug('Exception caught while checking URI',
                                     exc_info=1)
            rst.traverseLogger.warn(
                'Could not gather info from Redfish.Uris annotation')
            expectedURI = None

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

    for innerelement in usableProperties:
        rst.traverseLogger.debug(innerelement['Name'])
        rst.traverseLogger.debug(innerelement.get('Type'))
        rst.traverseLogger.debug(innerelement.attrs)
        newPropOwner = SchemaAlias if SchemaAlias is not None else 'SomeSchema'
        newProp = innerelement['Name']
        rst.traverseLogger.debug("ADDING :::: {}:{}".format(
            newPropOwner, newProp))
        PropertyList.append((schemaObj, newPropOwner, newProp))

    # get actions
    usableActions = innerschema.find_all(['Action'], recursive=False)

    for act in usableActions:
        newPropOwner = getNamespace(
            SchemaAlias) if SchemaAlias is not None else 'SomeSchema'
        newProp = act['Name']
        rst.traverseLogger.debug("ADDING ACTION :::: {}:{}".format(
            newPropOwner, newProp))
        ActionList.append(PropAction(newPropOwner, newProp, act))

    return PropertyList, ActionList, additional, PropertyPattern, expectedURI
Пример #11
0
def getSchemaDetails(SchemaType, SchemaURI):
    """
    Find Schema file for given Namespace.

    param SchemaType: Schema Namespace, such as ServiceRoot
    param SchemaURI: uri to grab schema, given LocalOnly is False
    return: (success boolean, a Soup object, origin)
    """
    rst.traverseLogger.debug('getting Schema of {} {}'.format(
        SchemaType, SchemaURI))
    currentService = rst.currentService

    if SchemaType is None:
        return False, None, None

    if currentService is None:
        return getSchemaDetailsLocal(SchemaType, SchemaURI)

    elif currentService.active and getNamespace(
            SchemaType) in currentService.metadata.schema_store:
        result = rst.currentService.metadata.schema_store[getNamespace(
            SchemaType)]
        if result is not None:
            return True, result.soup, result.origin

    if not currentService.config[
            'preferonline'] and '$metadata' not in SchemaURI:
        success, soup, origin = getSchemaDetailsLocal(SchemaType, SchemaURI)
        if success:
            return success, soup, origin

    xml_suffix = currentService.config['schemasuffix']

    config = rst.currentService.config
    LocalOnly, SchemaLocation, ServiceOnly = config['localonlymode'], config[
        'metadatafilepath'], config['servicemode']

    scheme, netloc, path, params, query, fragment = urlparse(SchemaURI)
    inService = scheme is None and netloc is None

    if (SchemaURI is not None
            and not LocalOnly) or (SchemaURI is not None
                                   and '/redfish/v1/$metadata' in SchemaURI):
        # Get our expected Schema file here
        # if success, generate Soup, then check for frags to parse
        #   start by parsing references, then check for the refLink
        if '#' in SchemaURI:
            base_schema_uri, frag = tuple(SchemaURI.rsplit('#', 1))
        else:
            base_schema_uri, frag = SchemaURI, None
        success, data, status, elapsed = rst.callResourceURI(base_schema_uri)
        if success:
            soup = BeautifulSoup(data, "xml")
            # if frag, look inside xml for real target as a reference
            if frag is not None:
                # prefer type over frag, truncated down
                # using frag, check references
                frag = getNamespace(SchemaType)
                frag = frag.split('.', 1)[0]
                refType, refLink = getReferenceDetails(
                    soup, name=base_schema_uri).get(frag, (None, None))
                if refLink is not None:
                    success, linksoup, newlink = getSchemaDetails(
                        refType, refLink)
                    if success:
                        return True, linksoup, newlink
                    else:
                        rst.traverseLogger.error(
                            "SchemaURI couldn't call reference link {} inside {}"
                            .format(frag, base_schema_uri))
                else:
                    rst.traverseLogger.error(
                        "SchemaURI missing reference link {} inside {}".format(
                            frag, base_schema_uri))
                    # error reported; assume likely schema uri to allow continued validation
                    uri = 'http://redfish.dmtf.org/schemas/v1/{}{}'.format(
                        frag, xml_suffix)
                    rst.traverseLogger.info(
                        "Continue assuming schema URI for {} is {}".format(
                            SchemaType, uri))
                    return getSchemaDetails(SchemaType, uri)
            else:
                storeSchemaToLocal(data, base_schema_uri)
                return True, soup, base_schema_uri
        if not inService and ServiceOnly:
            rst.traverseLogger.debug(
                "Nonservice URI skipped: {}".format(base_schema_uri))
        else:
            rst.traverseLogger.debug(
                "SchemaURI called unsuccessfully: {}".format(base_schema_uri))
    if LocalOnly:
        rst.traverseLogger.debug("This program is currently LOCAL ONLY")
    if ServiceOnly:
        rst.traverseLogger.debug("This program is currently SERVICE ONLY")
    if not LocalOnly and not ServiceOnly or (not inService
                                             and config['preferonline']):
        rst.traverseLogger.warning(
            "SchemaURI {} was unable to be called, defaulting to local storage in {}"
            .format(SchemaURI, SchemaLocation))
    return getSchemaDetailsLocal(SchemaType, SchemaURI)
Пример #12
0
def getSchemaDetailsLocal(SchemaType, SchemaURI):
    """
    Find Schema file for given Namespace, from local directory

    param SchemaType: Schema Namespace, such as ServiceRoot
    param SchemaURI: uri to grab schem (generate information from it)
    return: (success boolean, a Soup object, origin)
    """
    Alias = getNamespaceUnversioned(SchemaType)
    config = rst.config
    SchemaLocation, SchemaSuffix = config['metadatafilepath'], config[
        'schemasuffix']
    if SchemaURI is not None:
        uriparse = SchemaURI.split('/')[-1].split('#')
        xml = uriparse[0]
    else:
        rst.traverseLogger.warning(
            "SchemaURI was empty, must generate xml name from type {}".format(
                SchemaType)),
        return getSchemaDetailsLocal(SchemaType, Alias + SchemaSuffix)
    rst.traverseLogger.debug(
        (SchemaType, SchemaURI, SchemaLocation + '/' + xml))
    filestring = Alias + SchemaSuffix if xml is None else xml
    try:
        # get file
        with open(SchemaLocation + '/' + xml, "r") as filehandle:
            data = filehandle.read()

        # get tags
        soup = BeautifulSoup(data, "xml")
        edmxTag = soup.find('edmx:Edmx', recursive=False)
        parentTag = edmxTag.find('edmx:DataServices', recursive=False)
        child = parentTag.find('Schema', recursive=False)
        SchemaNamespace = child['Namespace']
        FoundAlias = SchemaNamespace.split(".")[0]
        rst.traverseLogger.debug(FoundAlias)

        if '/redfish/v1/$metadata' in SchemaURI:
            if len(uriparse) > 1:
                frag = getNamespace(SchemaType)
                frag = frag.split('.', 1)[0]
                refType, refLink = getReferenceDetails(soup,
                                                       name=SchemaLocation +
                                                       '/' + filestring).get(
                                                           frag, (None, None))
                if refLink is not None:
                    rst.traverseLogger.debug(
                        'Entering {} inside {}, pulled from $metadata'.format(
                            refType, refLink))
                    return getSchemaDetails(refType, refLink)
                else:
                    rst.traverseLogger.error(
                        'Could not find item in $metadata {}'.format(frag))
                    return False, None, None
            else:
                return True, soup, "localFile:" + SchemaLocation + '/' + filestring

        if FoundAlias in Alias:
            return True, soup, "localFile:" + SchemaLocation + '/' + filestring

    except FileNotFoundError:
        # if we're looking for $metadata locally... ditch looking for it, go straight to file
        if '/redfish/v1/$metadata' in SchemaURI and Alias != '$metadata':
            rst.traverseLogger.warning(
                "Unable to find a harddrive stored $metadata at {}, defaulting to {}"
                .format(SchemaLocation, Alias + SchemaSuffix))
            return getSchemaDetailsLocal(SchemaType, Alias + SchemaSuffix)
        else:
            rst.traverseLogger.warn
            ("Schema file {} not found in {}".format(filestring,
                                                     SchemaLocation))
            if Alias == '$metadata':
                rst.traverseLogger.warning(
                    "If $metadata cannot be found, Annotations may be unverifiable"
                )
    except Exception as ex:
        rst.traverseLogger.error(
            "A problem when getting a local schema has occurred {}".format(
                SchemaURI))
        rst.traverseLogger.warning("output: ", exc_info=True)
    return False, None, None
Пример #13
0
def renderHtml(results, tool_version, startTick, nowTick, service, printCSV):
    # Render html
    config = service.config
    config_str = ', '.join(sorted(list(config.keys() - set(['systeminfo', 'targetip', 'password', 'description']))))
    sysDescription, ConfigURI = (config['systeminfo'], config['targetip'])
    rsvLogger = rst.getLogger()
    logpath = config['logpath']
    error_lines, finalCounts = count_errors(results)
    if service.metadata is not None:
        finalCounts.update(service.metadata.get_counter())

    # wrap html
    htmlPage = ''
    htmlStrTop = '<head><title>Conformance Test Summary</title>\
            <style>\
            .pass {background-color:#99EE99}\
            .column {\
                float: left;\
                width: 45%;\
            }\
            .fail {background-color:#EE9999}\
            .warn {background-color:#EEEE99}\
            .bluebg {background-color:#BDD6EE}\
            .button {padding: 12px; display: inline-block}\
            .center {text-align:center;}\
            .log {text-align:left; white-space:pre-wrap; word-wrap:break-word; font-size:smaller; padding: 6px}\
            .title {background-color:#DDDDDD; border: 1pt solid; font-height: 30px; padding: 8px}\
            .titlesub {padding: 8px}\
            .titlerow {border: 2pt solid}\
            .results {transition: visibility 0s, opacity 0.5s linear; display: none; opacity: 0}\
            .resultsShow {display: block; opacity: 1}\
            body {background-color:lightgrey; border: 1pt solid; text-align:center; margin-left:auto; margin-right:auto}\
            th {text-align:center; background-color:beige; border: 1pt solid}\
            td {text-align:left; background-color:white; border: 1pt solid; word-wrap:break-word; overflow:hidden;}\
            table {width:90%; margin: 0px auto; table-layout:fixed;}\
            .titletable {width:100%}\
            </style>\
            </head>'
    htmlStrBodyHeader = ''
    # Logo and logname
    infos = [wrapTag('##### Redfish Conformance Test Report #####', 'h2')]
    infos.append(wrapTag('<img align="center" alt="DMTF Redfish Logo" height="203" width="288"'
                         'src="data:image/gif;base64,' + logo.logo + '">', 'h4'))
    infos.append('<h4><a href="https://github.com/DMTF/Redfish-Service-Validator">'
                 'https://github.com/DMTF/Redfish-Service-Validator</a></h4>')
    infos.append('Tool Version: {}'.format(tool_version))
    infos.append(startTick.strftime('%c'))
    infos.append('(Run time: {})'.format(
        str(nowTick - startTick).rsplit('.', 1)[0]))
    infos.append('<h4>This tool is provided and maintained by the DMTF. '
                 'For feedback, please open issues<br>in the tool\'s Github repository: '
                 '<a href="https://github.com/DMTF/Redfish-Service-Validator/issues">'
                 'https://github.com/DMTF/Redfish-Service-Validator/issues</a></h4>')

    htmlStrBodyHeader += tag.tr(tag.th(infoBlock(infos)))

    infos = {x: config[x] for x in config if x not in ['systeminfo', 'targetip', 'password', 'description']}
    infos_left, infos_right = dict(), dict()
    for key in sorted(infos.keys()):
        if len(infos_left) <= len(infos_right):
            infos_left[key] = infos[key]
        else:
            infos_right[key] = infos[key]

    block = tag.td(tag.div(infoBlock(infos_left), 'class=\'column log\'') \
            + tag.div(infoBlock(infos_right), 'class=\'column log\''), 'id=\'resNumConfig\' class=\'results resultsShow\'')

    htmlButtons = '<div class="button warn" onClick="arr = document.getElementsByClassName(\'results\'); for (var i = 0; i < arr.length; i++){arr[i].classList.add(\'resultsShow\')};">Expand All</div>'
    htmlButtons += '<div class="button fail" onClick="arr = document.getElementsByClassName(\'results\'); for (var i = 0; i < arr.length; i++){arr[i].classList.remove(\'resultsShow\')};">Collapse All</div>'
    htmlButtons += tag.div('Toggle Config', attr='class="button pass" onClick="document.getElementById(\'resNumConfig\').classList.toggle(\'resultsShow\');"')

    htmlStrBodyHeader += tag.tr(tag.th(htmlButtons))
    htmlStrBodyHeader += tag.tr(tag.th('Test Summary'))

    infos = {'System': ConfigURI, 'Description': sysDescription}
    htmlStrBodyHeader += tag.tr(tag.th(infoBlock(infos)))

    errors = error_lines
    if len(errors) == 0:
        errors = ['No errors located.']
    errorTags = tag.td(infoBlock(errors), 'class="log"')

    infos_left, infos_right = dict(), dict()
    for key in sorted(finalCounts.keys()):
        if finalCounts.get(key) == 0:
            continue
        if len(infos_left) <= len(infos_right):
            infos_left[key] = finalCounts[key]
        else:
            infos_right[key] = finalCounts[key]

    htmlStrCounts = (tag.div(infoBlock(infos_left), 'class=\'column log\'') + tag.div(infoBlock(infos_right), 'class=\'column log\''))

    htmlStrBodyHeader += tag.tr(tag.td(htmlStrCounts))

    htmlStrBodyHeader += tag.tr(errorTags)

    htmlStrBodyHeader += tag.tr(block)


    if service.metadata is not None:
        htmlPage = service.metadata.to_html()
    for cnt, item in enumerate(results):
        entry = []
        val = results[item]
        rtime = '(response time: {})'.format(val['rtime'])
        rcode = '({})'.format(val['rcode'])
        payload = val.get('payload', {})

        # uri block
        prop_type = val['fulltype']
        if prop_type is not None:
            namespace = getNamespace(prop_type)
            type_name = getType(prop_type)

        infos = [str(val.get(x)) for x in ['uri', 'samplemapped'] if val.get(x) not in ['',None]]
        infos.append(rtime)
        infos.append(type_name)
        uriTag = tag.tr(tag.th(infoBlock(infos, '&ensp;'), 'class="titlerow bluebg"'))
        entry.append(uriTag)

        # info block
        infos = [str(val.get(x)) for x in ['uri'] if val.get(x) not in ['',None]]
        infos.append(rtime)
        infos.append(tag.div('Show Results', attr='class="button warn"\
                onClick="document.getElementById(\'payload{}\').classList.remove(\'resultsShow\');\
                document.getElementById(\'resNum{}\').classList.toggle(\'resultsShow\');"'.format(cnt, cnt)))
        infos.append(tag.div('Show Payload', attr='class="button pass"\
                onClick="document.getElementById(\'payload{}\').classList.toggle(\'resultsShow\');\
                document.getElementById(\'resNum{}\').classList.add(\'resultsShow\');"'.format(cnt, cnt)))
        buttonTag = tag.td(infoBlock(infos), 'class="title" style="width:30%"')

        infos = [str(val.get(x)) for x in ['context', 'origin', 'fulltype']]
        infos = {y: x for x, y in zip(infos, ['Context', 'File Origin', 'Resource Type'])}
        infosTag = tag.td(infoBlock(infos), 'class="titlesub log" style="width:40%"')

        success = val['success']
        if success:
            getTag = tag.td('GET Success HTTP Code {}'.format(rcode), 'class="pass"')
        else:
            getTag = tag.td('GET Failure HTTP Code {}'.format(rcode), 'class="fail"')

        countsTag = tag.td(infoBlock(val['counts'], split='', ffunc=applyInfoSuccessColor), 'class="log"')

        rhead = ''.join([buttonTag, infosTag, getTag, countsTag])
        for x in [('tr',), ('table', 'class=titletable'), ('td', 'class=titlerow'), ('tr')]:
            rhead = wrapTag(''.join(rhead), *x)
        entry.append(rhead)

        # actual table
        rows = [[str(m)] + list([str(x) for x in val['messages'][m]]) for m in val['messages']]
        titles = ['Property Name', 'Value', 'Type', 'Exists', 'Result']
        widths = ['15', '30', '30', '10', '15']
        tableHeader = tableBlock(rows, titles, widths, ffunc=applySuccessColor)

        #    lets wrap table and errors and warns into one single column table
        tableHeader = tag.tr(tag.td((tableHeader)))

        infos_a = [str(val.get(x)) for x in ['uri'] if val.get(x) not in ['',None]]
        infos_a.append(rtime)

        if(printCSV):
            rsvLogger.info(','.join(infos_a))
            rsvLogger.info(','.join(infos))
            rsvLogger.info(','.join(titles))
            rsvLogger.info('\n'.join([','.join(x) for x in rows]))
            rsvLogger.info(',')

        # warns and errors
        errors = val['errors']
        if len(errors) == 0:
            errors = 'No errors'
        infos = errors.split('\n')
        errorTags = tag.tr(tag.td(infoBlock(infos), 'class="fail log"'))

        warns = val['warns']
        if len(warns) == 0:
            warns = 'No warns'
        infos = warns.split('\n')
        warnTags = tag.tr(tag.td(infoBlock(infos), 'class="warn log"'))

        payloadTag = tag.td(json.dumps(payload, indent=4, sort_keys=True), 'id=\'payload{}\' class=\'results log\''.format(cnt))

        tableHeader += errorTags
        tableHeader += warnTags
        tableHeader += payloadTag
        tableHeader = tag.table(tableHeader)
        tableHeader = tag.td(tableHeader, 'class="results" id=\'resNum{}\''.format(cnt))

        entry.append(tableHeader)



        # append
        htmlPage += ''.join([tag.tr(x) for x in entry])

    return wrapTag(wrapTag(htmlStrTop + wrapTag(htmlStrBodyHeader + htmlPage, 'table'), 'body'), 'html')
    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))
def getAllLinks(jsonData,
                propList,
                schemaObj,
                prefix='',
                context='',
                linklimits=None,
                sample_size=0,
                oemCheck=True):
    """
    Function that returns all links provided in a given JSON response.
    This result will include a link to itself.

    :param arg1: json dict
    :param arg2: property dict
    :param arg3: reference dict
    :param prefix: default blank, for deeper links
    :param context: default blank, for AutoExpanded types
    :return: list of links
    """
    linkList = OrderedDict()
    if linklimits is None:
        linklimits = {}
    # check keys in propertyDictionary
    # if it is a Nav property, check that it exists
    #   if it is not a Nav Collection, add it to list
    #   otherwise, add everything IN Nav collection
    # if it is a Complex property, check that it exists
    #   if it is, recurse on collection or individual item
    if not isinstance(jsonData, dict):
        traverseLogger.error("Generating links requires a dict")
    refDict = schemaObj.refs
    try:
        for propx in propList:
            propDict = propx.propDict
            if propDict is None:
                continue

            isNav = propDict.get('isNav', False)
            key = propx.name
            item = getType(key).split(':')[-1]

            insideItem = propx.val if propx.exists else None
            autoExpand = propDict.get('OData.AutoExpand', None) is not None or\
                propDict.get('OData.AutoExpand'.lower(), None) is not None
            cType = propDict.get('isCollection')
            ownerNS = propx.propOwner.split('.')[0]
            ownerType = propx.propOwner.split('.')[-1]

            if isNav:
                if insideItem is not None:
                    if cType is not None:
                        cTypeName = getType(cType)
                        cSchema = refDict.get(getNamespace(cType),
                                              (None, None))[1]
                        if cSchema is None:
                            cSchema = context
                        for cnt, listItem in enumerate_collection(
                                insideItem, cTypeName, linklimits,
                                sample_size):
                            linkList[prefix + str(item) + '.' + cTypeName +
                                     '#' +
                                     str(cnt)] = (listItem.get('@odata.id'),
                                                  autoExpand, cType, cSchema,
                                                  listItem)
                    else:
                        cType = propDict['attrs'].get('Type')
                        cSchema = refDict.get(getNamespace(cType),
                                              (None, None))[1]
                        if cSchema is None:
                            cSchema = context
                        linkList[prefix + str(item) + '.' +
                                 getType(propDict['attrs']['Name'])] = (
                                     insideItem.get('@odata.id'), autoExpand,
                                     cType, cSchema, insideItem)
            elif item == 'Uri' and ownerNS == 'MessageRegistryFile' and ownerType == 'Location':
                # special handling for MessageRegistryFile Location Uri
                if insideItem is not None and isinstance(
                        insideItem, str) and len(insideItem) > 0:
                    uriItem = {'@odata.id': insideItem}
                    cType = ownerNS + '.' + ownerNS
                    cSchema = refDict.get(getNamespace(cType), (None, None))[1]
                    if cSchema is None:
                        cSchema = context
                    traverseLogger.debug(
                        'Registry Location Uri: resource = {}, type = {}, schema = {}'
                        .format(insideItem, cType, cSchema))
                    linkList[prefix + str(item) + '.' +
                             getType(propDict['attrs']['Name'])] = (
                                 uriItem.get('@odata.id'), autoExpand, cType,
                                 cSchema, uriItem)
            elif item == 'Actions':
                # special handling for @Redfish.ActionInfo payload annotations
                if isinstance(insideItem, dict):
                    cType = 'ActionInfo.ActionInfo'
                    cSchema = refDict.get(getNamespace(cType), (None, None))[1]
                    for k, v in insideItem.items():
                        if not isinstance(v, dict):
                            continue
                        uri = v.get('@Redfish.ActionInfo')
                        if isinstance(uri, str):
                            uriItem = {'@odata.id': uri}
                            traverseLogger.debug(
                                '{}{}: @Redfish.ActionInfo annotation uri = {}'
                                .format(item, k, uri))
                            linkList[prefix + str(item) + k + '.' +
                                     cType] = (uriItem.get('@odata.id'),
                                               autoExpand, cType, cSchema,
                                               uriItem)

        for propx in propList:
            propDict = propx.propDict
            if propDict is None:
                continue
            propDict = propx.propDict
            key = propx.name
            item = getType(key).split(':')[-1]
            if 'Oem' in item and not oemCheck:
                continue
            cType = propDict.get('isCollection')
            if propDict is None:
                continue
            elif propDict['realtype'] == 'complex':
                tp = propDict['typeprops']
                if jsonData.get(item) is not None and tp is not None:
                    if cType is not None:
                        cTypeName = getType(cType)
                        for item in tp:
                            linkList.update(item.links)
                    else:
                        linkList.update(tp.links)
        traverseLogger.debug(str(linkList))
    except Exception as e:
        traverseLogger.debug('Exception caught while getting all links',
                             exc_info=1)
        traverseLogger.error(
            'Unexpected error while extracting links from payload: {}'.format(
                repr(e)))
    # contents of Registries may be needed to validate other resources (like Bios), so move to front of linkList
    if 'Registries.Registries' in linkList:
        linkList.move_to_end('Registries.Registries', last=False)
        traverseLogger.debug(
            'getAllLinks: Moved Registries.Registries to front of list')
    return linkList
Пример #16
0
def renderHtml(results, finalCounts, tool_version, startTick, nowTick,
               printCSV):
    # Render html
    config = rst.config
    config_str = ', '.join(
        sorted(
            list(config.keys() -
                 set(['systeminfo', 'targetip', 'password', 'description']))))
    rsvLogger = rst.getLogger()
    sysDescription, ConfigURI = (config['systeminfo'], config['targetip'])
    logpath = config['logpath']

    # wrap html
    htmlPage = ''
    htmlStrTop = '<head><title>Conformance Test Summary</title>\
            <style>\
            .pass {background-color:#99EE99}\
            .fail {background-color:#EE9999}\
            .warn {background-color:#EEEE99}\
            .bluebg {background-color:#BDD6EE}\
            .button {padding: 12px; display: inline-block}\
            .center {text-align:center;}\
            .log {text-align:left; white-space:pre-wrap; word-wrap:break-word; font-size:smaller}\
            .title {background-color:#DDDDDD; border: 1pt solid; font-height: 30px; padding: 8px}\
            .titlesub {padding: 8px}\
            .titlerow {border: 2pt solid}\
            .results {transition: visibility 0s, opacity 0.5s linear; display: none; opacity: 0}\
            .resultsShow {display: block; opacity: 1}\
            body {background-color:lightgrey; border: 1pt solid; text-align:center; margin-left:auto; margin-right:auto}\
            th {text-align:center; background-color:beige; border: 1pt solid}\
            td {text-align:left; background-color:white; border: 1pt solid; word-wrap:break-word;}\
            table {width:90%; margin: 0px auto; table-layout:fixed;}\
            .titletable {width:100%}\
            </style>\
            </head>'

    htmlStrBodyHeader = ''
    # Logo and logname
    infos = [wrapTag('##### Redfish Conformance Test Report #####', 'h2')]
    infos.append(
        wrapTag(
            '<img align="center" alt="DMTF Redfish Logo" height="203" width="288"'
            'src="data:image/gif;base64,' + logo.logo + '">', 'h4'))
    infos.append(
        '<h4><a href="https://github.com/DMTF/Redfish-Interop-Validator">'
        'https://github.com/DMTF/Redfish-Interop-Validator</a></h4>')
    infos.append('Tool Version: {}'.format(tool_version))
    infos.append(startTick.strftime('%c'))
    infos.append('(Run time: {})'.format(
        str(nowTick - startTick).rsplit('.', 1)[0]))
    infos.append(
        '<h4>This tool is provided and maintained by the DMTF. '
        'For feedback, please open issues<br>in the tool\'s Github repository: '
        '<a href="https://github.com/DMTF/Redfish-Interop-Validator/issues">'
        'https://github.com/DMTF/Redfish-Interop-Validator/issues</a></h4>')

    htmlStrBodyHeader += tr(th(infoBlock(infos)))

    infos = {'System': ConfigURI, 'Description': sysDescription}
    htmlStrBodyHeader += tr(th(infoBlock(infos)))

    infos = {'Profile': config['profile'], 'Schema': config['schema']}
    htmlStrBodyHeader += tr(th(infoBlock(infos)))

    infos = {
        x: config[x]
        for x in config if x not in [
            'systeminfo', 'targetip', 'password', 'description', 'profile',
            'schema'
        ]
    }
    block = tr(th(infoBlock(infos, '|||')))
    for num, block in enumerate(block.split('|||'), 1):
        sep = '<br/>' if num % 4 == 0 else ',&ensp;'
        sep = '' if num == len(infos) else sep
        htmlStrBodyHeader += block + sep

    htmlStrTotal = '<div>Final counts: '
    for countType in sorted(finalCounts.keys()):
        if finalCounts.get(countType) == 0:
            continue
        htmlStrTotal += '{p}: {q},   '.format(p=countType,
                                              q=finalCounts.get(countType, 0))
    htmlStrTotal += '</div><div class="button warn" onClick="arr = document.getElementsByClassName(\'results\'); for (var i = 0; i < arr.length; i++){arr[i].className = \'results resultsShow\'};">Expand All</div>'
    htmlStrTotal += '</div><div class="button fail" onClick="arr = document.getElementsByClassName(\'results\'); for (var i = 0; i < arr.length; i++){arr[i].className = \'results\'};">Collapse All</div>'

    htmlStrBodyHeader += tr(td(htmlStrTotal))

    htmlPage = rst.currentService.metadata.to_html()
    for cnt, item in enumerate(results):
        entry = []
        val = results[item]
        rtime = '(response time: {})'.format(val['rtime'])

        if len(val['messages']) == 0 and len(val['errors']) == 0:
            continue

        # uri block
        prop_type = val['fulltype']
        if prop_type is not None:
            namespace = getNamespace(prop_type)
            type_name = getType(prop_type)

        infos_a = [
            str(val.get(x)) for x in ['uri', 'samplemapped']
            if val.get(x) not in ['', None]
        ]
        infos_a.append(rtime)
        infos_a.append(type_name)
        uriTag = tr(th(infoBlock(infos_a, '&ensp;'),
                       'class="titlerow bluebg"'))
        entry.append(uriTag)

        # info block
        infos_b = [
            str(val.get(x)) for x in ['uri'] if val.get(x) not in ['', None]
        ]
        infos_b.append(rtime)
        infos_b.append(
            div('Show Results',
                attr=
                'class="button warn" onClick="document.getElementById(\'resNum{}\').classList.toggle(\'resultsShow\');"'
                .format(cnt)))
        buttonTag = td(infoBlock(infos_b), 'class="title" style="width:30%"')

        infos_content = [
            str(val.get(x)) for x in ['context', 'origin', 'fulltype']
        ]
        infos_c = {
            y: x
            for x, y in zip(infos_content,
                            ['Context', 'File Origin', 'Resource Type'])
        }
        infosTag = td(infoBlock(infos_c),
                      'class="titlesub log" style="width:40%"')

        success = val['success']
        if success:
            getTag = td('GET Success', 'class="pass"')
        else:
            getTag = td('GET Failure', 'class="fail"')

        countsTag = td(
            infoBlock(val['counts'], split='', ffunc=applyInfoSuccessColor),
            'class="log"')

        rhead = ''.join([buttonTag, infosTag, getTag, countsTag])
        for x in [('tr', ), ('table', 'class=titletable'),
                  ('td', 'class=titlerow'), ('tr')]:
            rhead = wrapTag(''.join(rhead), *x)
        entry.append(rhead)

        # actual table
        rows = [(str(i.name), str(i.entry), str(i.expected), str(i.actual),
                 str(i.success.value)) for i in val['messages']]
        titles = ['Property Name', 'Value', 'Expected', 'Actual', 'Result']
        widths = ['15', '30', '30', '10', '15']
        tableHeader = tableBlock(rows, titles, widths, ffunc=applySuccessColor)

        #    lets wrap table and errors and warns into one single column table
        tableHeader = tr(td((tableHeader)))

        if (printCSV):
            rsvLogger.info(','.join(infos_a))
            rsvLogger.info(','.join(infos_content))
            rsvLogger.info(','.join(titles))
            rsvLogger.info('\n'.join([','.join(x) for x in rows]))
            rsvLogger.info(',')

        # warns and errors
        errors = val['errors']
        if len(errors) == 0:
            errors = 'No errors'
        infos = errors.split('\n')
        errorTags = tr(td(infoBlock(infos), 'class="fail log"'))

        warns = val['warns']
        if len(warns) == 0:
            warns = 'No warns'
        infos = warns.split('\n')
        warnTags = tr(td(infoBlock(infos), 'class="warn log"'))

        tableHeader += errorTags
        tableHeader += warnTags
        tableHeader = table(tableHeader)
        tableHeader = td(tableHeader,
                         'class="results" id=\'resNum{}\''.format(cnt))

        entry.append(tableHeader)

        # append
        htmlPage += ''.join([tr(x) for x in entry])

    return wrapTag(
        wrapTag(htmlStrTop + wrapTag(htmlStrBodyHeader + htmlPage, 'table'),
                'body'), 'html')