Example #1
0
def checkComparison(val, compareType, target):
    """
    Validate a given comparison option, given a value and a target set
    """
    rsvLogger.info('Testing a comparison \n\t' +
                   str((val, compareType, target)))
    vallist = val if isinstance(val, list) else [val]
    paramPass = False
    if compareType is None:
        rsvLogger.error('CompareType not available in payload')
    if compareType == "AnyOf":
        for item in vallist:
            paramPass = item in target
            if paramPass:
                break
            else:
                continue

    if compareType == "AllOf":
        alltarget = set()
        for item in vallist:
            paramPass = item in target and item not in alltarget
            if paramPass:
                alltarget.add(item)
                if len(alltarget) == len(target):
                    break
            else:
                continue
        paramPass = len(alltarget) == len(target)
    if compareType == "LinkToResource":
        vallink = val.get('@odata.id')
        success, decoded, code, elapsed = rst.callResourceURI(vallink)
        if success:
            ourType = decoded.get('@odata.type')
            if ourType is not None:
                SchemaType = rst.getType(ourType)
                paramPass = SchemaType in target
            else:
                paramPass = False
        else:
            paramPass = False

    if compareType == "Absent":
        paramPass = val == 'DNE'
    if compareType == "Present":
        paramPass = val != 'DNE'

    if isinstance(target, list):
        if len(target) >= 1:
            target = target[0]
        else:
            target = 'DNE'

    if target != 'DNE':
        if compareType == "Equal":
            paramPass = val == target
        if compareType == "NotEqual":
            paramPass = val != target
        if compareType == "GreaterThan":
            paramPass = val > target
        if compareType == "GreaterThanOrEqual":
            paramPass = val >= target
        if compareType == "LessThan":
            paramPass = val < target
        if compareType == "LessThanOrEqual":
            paramPass = val <= target
    rsvLogger.info('\tpass ' + str(paramPass))
    if not paramPass:
        rsvLogger.error('\tNoPass')
    return msgInterop('Comparison', target, compareType, val, paramPass),\
        paramPass
Example #2
0
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
Example #3
0
def validateURITree(URI,
                    uriName,
                    profile,
                    expectedType=None,
                    expectedSchema=None,
                    expectedJson=None):
    """
    Validates a Tree of URIs, traversing from the first given
    """
    traverseLogger = rst.getLogger()

    allLinks = set()
    allLinks.add(URI)
    refLinks = list()

    # Resource level validation
    rcounts = Counter()
    rerror = StringIO()
    rmessages = []
    r_exists = {}

    resource_info = dict(profile.get('Resources'))

    # Validate top URI
    validateSuccess, counts, results, links, thisobj = \
        validateSingleURI(URI, profile, uriName, expectedType,
                          expectedSchema, expectedJson)

    # parent first, then child execution
    # do top level root first, then do each child root, then their children...
    # hold refs for last (less recursion)
    if validateSuccess:
        serviceVersion = profile.get("Protocol")
        if serviceVersion is not None:
            serviceVersion = serviceVersion.get('MinVersion', '1.0.0')
            msg, m_success = commonInterop.validateMinVersion(
                thisobj.jsondata.get("RedfishVersion", "0"), serviceVersion)
            rmessages.append(msg)

        currentLinks = [(l, links[l], thisobj) for l in links]
        # todo : churning a lot of links, causing possible slowdown even with set checks
        while len(currentLinks) > 0:
            newLinks = list()
            for linkName, link, parent in currentLinks:
                linkURI, autoExpand, linkType, linkSchema, innerJson = link

                if linkURI is None:
                    continue

                if linkURI.rstrip(
                        '/') in allLinks or linkType == 'Resource.Item':
                    continue

                if refLinks is not currentLinks and (
                        'Links' in linkName.split('.')
                        or 'RelatedItem' in linkName.split('.')
                        or 'Redundancy' in linkName.split('.')):
                    refLinks.append((linkName, link, parent))
                    continue

                if autoExpand and linkType is not None:
                    linkSuccess, linkCounts, linkResults, innerLinks, linkobj = \
                        validateSingleURI(linkURI, profile, linkURI, linkType, linkSchema, innerJson, parent=parent)
                else:
                    linkSuccess, linkCounts, linkResults, innerLinks, linkobj = \
                        validateSingleURI(linkURI, profile, linkURI, linkType, linkSchema, parent=parent)

                allLinks.add(linkURI.rstrip('/'))

                if not linkSuccess:
                    continue

                innerLinksTuple = [(l, innerLinks[l], linkobj)
                                   for l in innerLinks]
                newLinks.extend(innerLinksTuple)
                results.update(linkResults)
                SchemaType = rst.getType(linkobj.typeobj.fulltype)

                r_exists[SchemaType] = True

            if refLinks is not currentLinks and len(
                    newLinks) == 0 and len(refLinks) > 0:
                currentLinks = refLinks
            else:
                currentLinks = newLinks

    # interop service level checks
    finalResults = OrderedDict()
    traverseLogger.info('Service Level Checks')
    if URI not in ["/redfish/v1", "/redfish/v1/"]:
        resultEnum = commonInterop.sEnum.WARN
        traverseLogger.info("We are not validating root, warn only")
    else:
        resultEnum = commonInterop.sEnum.FAIL

    for item in resource_info:
        # thisobj does not exist if we didn't find the first resource
        if thisobj and item == rst.getType(thisobj.typeobj.fulltype):
            continue

        exists = r_exists.get(item, False)

        if "ConditionalRequirements" in resource_info[item]:
            innerList = resource_info[item]["ConditionalRequirements"]
            for condreq in innerList:
                if commonInterop.checkConditionalRequirementResourceLevel(
                        r_exists, condreq, item):
                    traverseLogger.info(
                        'Service Conditional for {} applies'.format(item))
                    req = condreq.get("ReadRequirement", "Mandatory")
                    rmessages.append(
                        commonInterop.msgInterop(
                            item + '.Conditional.ReadRequirement', req,
                            'Must Exist' if req == "Mandatory" else 'Any',
                            'DNE' if not exists else 'Exists',
                            resultEnum if not exists and req == "Mandatory"
                            else commonInterop.sEnum.PASS))
                else:
                    traverseLogger.info(
                        'Service Conditional for {} does not apply'.format(
                            item))

        req = resource_info[item].get("ReadRequirement", "Mandatory")

        if not exists:
            rmessages.append(
                commonInterop.msgInterop(
                    item + '.ReadRequirement', req,
                    'Must Exist' if req == "Mandatory" else 'Any', 'DNE',
                    resultEnum
                    if req == "Mandatory" else commonInterop.sEnum.PASS))
        else:
            rmessages.append(
                commonInterop.msgInterop(
                    item + '.ReadRequirement', req,
                    'Must Exist' if req == "Mandatory" else 'Any', 'Exists',
                    commonInterop.sEnum.PASS))

    for item in rmessages:
        if item.success == commonInterop.sEnum.WARN:
            rcounts['warn'] += 1
        elif item.success == commonInterop.sEnum.PASS:
            rcounts['pass'] += 1
        elif item.success == commonInterop.sEnum.FAIL:
            rcounts['fail.{}'.format(item.name)] += 1

    finalResults['n/a'] = {
        'uri': "Service Level Requirements",
        'success': rcounts.get('fail', 0) == 0,
        'counts': rcounts,
        'messages': rmessages,
        'errors': rerror.getvalue(),
        'warns': '',
        'rtime': '',
        'context': '',
        'fulltype': ''
    }
    finalResults.update(results)
    rerror.close()

    return validateSuccess, counts, finalResults, refLinks, thisobj
Example #4
0
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
Example #5
0
def validateURITree(URI,
                    uriName,
                    profile,
                    expectedType=None,
                    expectedSchema=None,
                    expectedJson=None):
    """
    Validates a Tree of URIs, traversing from the first given
    """
    traverseLogger = rst.getLogger()

    allLinks = set()
    allLinks.add(URI)
    refLinks = list()

    # Resource level validation
    rcounts = Counter()
    rmessages = []
    rerror = StringIO()

    objRes = dict(profile.get('Resources'))

    # Validate top URI
    validateSuccess, counts, results, links, thisobj = \
        validateSingleURI(URI, profile, uriName, expectedType,
                          expectedSchema, expectedJson)

    # parent first, then child execution
    # do top level root first, then do each child root, then their children...
    # hold refs for last (less recursion)
    if validateSuccess:
        serviceVersion = profile.get("Protocol")
        if serviceVersion is not None:
            serviceVersion = serviceVersion.get('MinVersion', '1.0.0')
            msg, mpss = commonInterop.validateMinVersion(
                thisobj.jsondata.get("RedfishVersion", "0"), serviceVersion)
            rmessages.append(msg)

        currentLinks = [(l, links[l], thisobj) for l in links]
        # todo : churning a lot of links, causing possible slowdown even with set checks
        while len(currentLinks) > 0:
            newLinks = list()
            for linkName, link, parent in currentLinks:
                linkURI, autoExpand, linkType, linkSchema, innerJson = link

                if linkURI in allLinks or linkType == 'Resource.Item':
                    continue

                if refLinks is not currentLinks and (
                        'Links' in linkName.split('.')
                        or 'RelatedItem' in linkName.split('.')
                        or 'Redundancy' in linkName.split('.')):
                    refLinks.append((linkName, link, parent))
                    continue

                if autoExpand and linkType is not None:
                    linkSuccess, linkCounts, linkResults, innerLinks, linkobj = \
                        validateSingleURI(linkURI, profile, linkURI, linkType, linkSchema, innerJson, parent=parent)
                else:
                    linkSuccess, linkCounts, linkResults, innerLinks, linkobj = \
                        validateSingleURI(linkURI, profile, linkURI, linkType, linkSchema, parent=parent)

                allLinks.add(linkURI)

                if not linkSuccess:
                    continue

                innerLinksTuple = [(l, innerLinks[l], linkobj)
                                   for l in innerLinks]
                newLinks.extend(innerLinksTuple)
                results.update(linkResults)
                SchemaType = rst.getType(linkobj.typeobj.fulltype)

                # Check schema level for requirements
                if SchemaType in objRes:
                    traverseLogger.info(
                        "Checking service requirement for {}".format(
                            SchemaType))
                    req = objRes[SchemaType].get("ReadRequirement",
                                                 "Mandatory")
                    msg, pss = commonInterop.validateRequirement(req, None)
                    if pss and not objRes[SchemaType].get('mark', False):
                        rmessages.append(msg)
                        msg.name = SchemaType + '.' + msg.name
                        objRes[SchemaType]['mark'] = True

                    if "ConditionalRequirements" in objRes[SchemaType]:
                        innerList = objRes[SchemaType][
                            "ConditionalRequirements"]
                        newList = list()
                        for condreq in innerList:
                            condtrue = commonInterop.checkConditionalRequirement(
                                linkobj, condreq, (linkobj.jsondata, None),
                                None)
                            if condtrue:
                                msg, cpss = commonInterop.validateRequirement(
                                    condreq.get("ReadRequirement",
                                                "Mandatory"), None)
                                if cpss:
                                    rmessages.append(msg)
                                    msg.name = SchemaType + '.Conditional.' + msg.name
                                else:
                                    newList.append(condreq)
                            else:
                                newList.append(condreq)
                        objRes[SchemaType]["ConditionalRequirements"] = newList

            if refLinks is not currentLinks and len(
                    newLinks) == 0 and len(refLinks) > 0:
                currentLinks = refLinks
            else:
                currentLinks = newLinks

    # interop service level checks
    finalResults = OrderedDict()
    if URI != "/redfish/v1":
        resultEnum = commonInterop.sEnum.WARN
        traverseLogger.info("We are not validating root, warn only")
    else:
        resultEnum = commonInterop.sEnum.FAIL
    for left in objRes:
        if not objRes[left].get('mark', False):
            req = objRes[left].get("ReadRequirement", "Mandatory")
            rmessages.append(
                commonInterop.msgInterop(
                    left + '.ReadRequirement', req,
                    'Must Exist' if req == "Mandatory" else 'Any', 'DNE',
                    resultEnum))
        if "ConditionalRequirements" in objRes[left]:
            innerList = objRes[left]["ConditionalRequirements"]
            for condreq in innerList:
                req = condreq.get("ReadRequirement", "Mandatory")
                rmessages.append(
                    commonInterop.msgInterop(
                        left + '.Conditional.ReadRequirement', req,
                        'Must Exist' if req == "Mandatory" else 'Any', 'DNE',
                        resultEnum))

    for item in rmessages:
        if item.success == commonInterop.sEnum.WARN:
            rcounts['warn'] += 1
        elif item.success == commonInterop.sEnum.PASS:
            rcounts['pass'] += 1
        elif item.success == commonInterop.sEnum.FAIL:
            rcounts['fail.{}'.format(item.name)] += 1

    finalResults['n/a'] = {'uri': "Service Level Requirements", 'success':rcounts.get('fail', 0) == 0,\
            'counts':rcounts,\
            'messages':rmessages, 'errors':rerror.getvalue(), 'warns': '',\
            'rtime':'', 'context':'', 'fulltype':''}
    print(len(allLinks))
    finalResults.update(results)
    rerror.close()

    return validateSuccess, counts, finalResults, refLinks, thisobj
Example #6
0
def checkPropertyConformance(schemaObj,
                             PropertyName,
                             prop,
                             decoded,
                             ParentItem=None,
                             parentURI=""):
    """checkPropertyConformance

    Given a dictionary of properties, check the validitiy of each item, and return a
    list of counted properties

    :param schemaObj:
    :param PropertyName:
    :param PropertyItem:
    :param decoded:
    :param ParentItem:
    :param parentURI:
    """

    resultList = OrderedDict()
    counts = Counter()

    rsvLogger.verboseout(PropertyName)
    item = prop.payloadName

    propValue = prop.val
    rsvLogger.verboseout("\tvalue: {} {}".format(propValue, type(propValue)))

    propExists = not (propValue == 'n/a')

    if ParentItem is not None:
        item = ParentItem + '.' + item

    PropertyDict = prop.propDict

    if PropertyDict is None:
        if not propExists:
            rsvLogger.verboseout('{}: Item is skipped, no schema'.format(item))
            counts['skipNoSchema'] += 1
            return {
                item: ('-', '-', 'Yes' if propExists else 'No', 'NoSchema')
            }, counts
        else:
            rsvLogger.error(
                '{}: Item is present, but no schema found'.format(item))
            counts['failNoSchema'] += 1
            return {
                item: ('-', '-', 'Yes' if propExists else 'No', 'FAIL')
            }, counts

    propAttr = PropertyDict['attrs']

    propType = propAttr.get('Type')
    propRealType = PropertyDict.get('realtype')
    rsvLogger.verboseout("\thas Type: {} {}".format(propType, propRealType))

    # why not actually check oem
    # rs-assertion: 7.4.7.2
    if 'Oem' in PropertyName and not rst.config.get('oemcheck', False):
        rsvLogger.verboseout('\tOem is skipped')
        counts['skipOem'] += 1
        return {item: ('-', '-', 'Yes' if propExists else 'No', 'OEM')}, counts

    propMandatory = False
    propMandatoryPass = True

    if 'Redfish.Required' in PropertyDict:
        propMandatory = True
        propMandatoryPass = True if propExists else False
        rsvLogger.verboseout("\tMandatory Test: {}".format(
            'OK' if propMandatoryPass else 'FAIL'))
    else:
        rsvLogger.verboseout("\tis Optional")
        if not propExists:
            rsvLogger.verboseout("\tprop Does not exist, skip...")
            counts['skipOptional'] += 1
            return {
                item: ('-', displayType(propType, propRealType),
                       'Yes' if propExists else 'No', 'Optional')
            }, counts

    nullable_attr = propAttr.get('Nullable')
    propNullable = False if nullable_attr == 'false' else True  # default is true

    # rs-assertion: Check for permission change
    propPermissions = PropertyDict.get('OData.Permissions')
    propPermissionsValue = None
    if propPermissions is not None:
        propPermissionsValue = propPermissions['EnumMember']
        rsvLogger.verboseout("\tpermission {}".format(propPermissionsValue))

    autoExpand = PropertyDict.get('OData.AutoExpand', None) is not None or\
        PropertyDict.get('OData.AutoExpand'.lower(), None) is not None

    validPatternAttr = PropertyDict.get('Validation.Pattern')
    validMinAttr = PropertyDict.get('Validation.Minimum')
    validMaxAttr = PropertyDict.get('Validation.Maximum')

    paramPass = propNullablePass = deprecatedPass = nullValid = True

    # <Annotation Term="Redfish.Deprecated" String="This property has been Deprecated in favor of Thermal.v1_1_0.Thermal.Fan.Name"/>
    validDeprecated = PropertyDict.get('Redfish.Deprecated')
    if validDeprecated is not None:
        deprecatedPass = False
        counts['warnDeprecated'] += 1
        rsvLogger.warning('{}: The given property is deprecated: {}'.format(
            item, validDeprecated.get('String', '')))

    validDeprecated = PropertyDict.get('Redfish.Revisions')
    if validDeprecated is not None:
        for tag_item in validDeprecated:
            revision_tag = tag_item.find('PropertyValue',
                                         attrs={
                                             'EnumMember':
                                             'Redfish.RevisionKind/Deprecated',
                                             'Property': 'Kind'
                                         })
            if (revision_tag):
                desc_tag = tag_item.find('PropertyValue',
                                         attrs={'Property': 'Description'})
                deprecatedPass = False
                counts['warnDeprecated'] += 1
                if (desc_tag):
                    rsvLogger.warning(
                        '{}: The given property is deprecated by revision: {}'.
                        format(item, desc_tag.attrs.get('String', '')))
                else:
                    rsvLogger.warning(
                        '{}: The given property is deprecated by revision'.
                        format(item))

    validMin, validMax = int(validMinAttr['Int']) if validMinAttr is not None else None, \
        int(validMaxAttr['Int']) if validMaxAttr is not None else None
    validPattern = validPatternAttr.get(
        'String', '') if validPatternAttr is not None else None

    # Note: consider http://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/csprd01/odata-csdl-xml-v4.01-csprd01.html#_Toc472333112
    # Note: make sure it checks each one
    propCollectionType = PropertyDict.get('isCollection')
    isCollection = propCollectionType is not None
    if isCollection and propValue is None:
        # illegal for a collection to be null
        if prop.propChild == 'HttpHeaders' and rst.getType(
                prop.propOwner) == 'EventDestination':
            rsvLogger.info('Value HttpHeaders can be Null')
            propNullable = True
            propValueList = []
            resultList[item] = ('Array (size: null)',
                                displayType(propType,
                                            propRealType,
                                            is_collection=True),
                                'Yes' if propExists else 'No', '...')
        else:
            rsvLogger.error(
                '{}: Value of Collection property is null but Collections cannot be null, only their entries'
                .format(item))
            counts['failNullCollection'] += 1
            return {
                item:
                ('-', displayType(propType, propRealType, is_collection=True),
                 'Yes' if propExists else 'No', 'FAIL')
            }, counts
    elif isCollection and propValue is not None:
        # note: handle collections correctly, this needs a nicer printout
        # rs-assumption: do not assume URIs for collections
        # rs-assumption: check @odata.count property
        # rs-assumption: check @odata.link property
        rsvLogger.verboseout("\tis Collection")
        if propValue == 'n/a':
            propValueList = []
            resultList[item] = ('Array (absent)'.format(len(propValue)),
                                displayType(propType,
                                            propRealType,
                                            is_collection=True),
                                'Yes' if propExists else 'No',
                                'PASS' if propMandatoryPass else 'FAIL')
            rsvLogger.error("{}: Mandatory prop does not exist".format(item))
            counts['failMandatoryExist'] += 1
        else:
            propValueList = propValue
            resultList[item] = ('Array (size: {})'.format(len(propValue)),
                                displayType(propType,
                                            propRealType,
                                            is_collection=True),
                                'Yes' if propExists else 'No', '...')
    else:
        # not a collection
        propValueList = [propValue]
    # note: make sure we don't enter this on null values, some of which are
    # OK!
    for cnt, val in enumerate(propValueList):
        appendStr = (('[' + str(cnt) + ']') if isCollection else '')
        sub_item = item + appendStr
        if isinstance(val, str):
            if val == '' and propPermissionsValue == 'OData.Permission/Read':
                rsvLogger.warning(
                    '{}: Empty string found - Services should omit properties if not supported'
                    .format(sub_item))
                nullValid = False
            if val.lower() == 'null':
                rsvLogger.warning(
                    '{}: "null" string found - Did you mean to use an actual null value?'
                    .format(sub_item))
                nullValid = False
        if propRealType is not None and propExists:
            paramPass = propNullablePass = True
            if val is None:
                if propNullable:
                    rsvLogger.debug(
                        'Property {} is nullable and is null, so Nullable checking passes'
                        .format(sub_item))
                else:
                    propNullablePass = False

            elif propRealType == 'Edm.Boolean':
                paramPass = isinstance(val, bool)
                if not paramPass:
                    rsvLogger.error("{}: Not a boolean".format(sub_item))

            elif propRealType == 'Edm.DateTimeOffset':
                paramPass = simpletypes.validateDatetime(sub_item, val)

            elif propRealType == 'Edm.Duration':
                paramPass = simpletypes.validateDayTimeDuration(sub_item, val)

            elif propRealType == 'Edm.Int16' or propRealType == 'Edm.Int32' or\
                    propRealType == 'Edm.Int64' or propRealType == 'Edm.Int':
                paramPass = simpletypes.validateInt(sub_item, val, validMin,
                                                    validMax)

            elif propRealType == 'Edm.Decimal' or propRealType == 'Edm.Double':
                paramPass = simpletypes.validateNumber(sub_item, val, validMin,
                                                       validMax)

            elif propRealType == 'Edm.Guid':
                paramPass = simpletypes.validateGuid(sub_item, val)

            elif propRealType == 'Edm.String':
                paramPass = simpletypes.validateString(sub_item, val,
                                                       validPattern)

            elif propRealType == 'Edm.Primitive' or propRealType == 'Edm.PrimitiveType':
                paramPass = simpletypes.validatePrimitive(sub_item, val)

            else:
                if propRealType == 'complex':
                    if PropertyDict['typeprops'] is not None:
                        if isCollection:
                            innerComplex = PropertyDict['typeprops'][cnt]
                            innerPropType = PropertyDict['typeprops'][
                                cnt].typeobj
                        else:
                            innerComplex = PropertyDict['typeprops']
                            innerPropType = PropertyDict['typeprops'].typeobj

                        success, complexCounts, complexMessages = validateComplex(
                            sub_item, val, innerComplex,
                            decoded.get('@odata.type'),
                            decoded.get('AttributeRegistry'))
                    else:
                        success = False

                    if not success:
                        counts['failComplex'] += 1
                        resultList[sub_item] = ('[JSON Object]',
                                                displayType(
                                                    propType, propRealType),
                                                'Yes' if propExists else 'No',
                                                'FAIL')
                        continue
                    resultList[sub_item] = ('[JSON Object]',
                                            displayType(
                                                propType, propRealType),
                                            'Yes' if propExists else 'No',
                                            'complex')

                    counts.update(complexCounts)
                    resultList.update(complexMessages)
                    allowAdditional = innerPropType.additional
                    for key in innerComplex.unknownProperties:
                        if sub_item + '.' + key not in complexMessages and not allowAdditional:
                            rsvLogger.error(
                                '{} not defined in schema {} (check version, spelling and casing)'
                                .format(sub_item + '.' + key,
                                        innerPropType.snamespace))
                            counts['failComplexAdditional'] += 1
                            resultList[sub_item + '.' + key] = (displayValue(
                                val[key]), '-', '-', 'FAIL')
                        elif sub_item + '.' + key not in complexMessages:
                            rsvLogger.warn(
                                '{} not defined in schema {} (check version, spelling and casing)'
                                .format(sub_item + '.' + key,
                                        innerPropType.snamespace))
                            counts['unverifiedComplexAdditional'] += 1
                            resultList[sub_item + '.' + key] = (displayValue(
                                val[key]), '-', '-', 'Additional')
                    continue

                elif propRealType == 'enum':
                    paramPass = simpletypes.validateEnum(
                        sub_item, val, PropertyDict['typeprops'])

                elif propRealType == 'deprecatedEnum':
                    paramPass = simpletypes.validateDeprecatedEnum(
                        sub_item, val, PropertyDict['typeprops'])

                elif propRealType == 'entity':
                    paramPass = validateEntity(sub_item, val, propType,
                                               propCollectionType, schemaObj,
                                               autoExpand, parentURI)
                else:
                    rsvLogger.error("%s: This type is invalid %s" %
                                    (sub_item, propRealType))
                    paramPass = False

        if not paramPass or not propMandatoryPass or not propNullablePass:
            result_str = 'FAIL'
        elif not deprecatedPass:
            result_str = 'Deprecated'
        elif not nullValid:
            counts['invalidPropertyValue'] += 1
            result_str = 'WARN'
        else:
            result_str = 'PASS'
        resultList[sub_item] = (displayValue(val,
                                             sub_item if autoExpand else None),
                                displayType(propType, propRealType),
                                'Yes' if propExists else 'No', result_str)
        if paramPass and propNullablePass and propMandatoryPass:
            counts['pass'] += 1
            rsvLogger.verboseout("\tSuccess")
        else:
            counts['err.' + str(propType)] += 1
            if not paramPass:
                if propMandatory:
                    counts['failMandatoryProp'] += 1
                else:
                    counts['failProp'] += 1
            elif not propMandatoryPass:
                rsvLogger.error(
                    "{}: Mandatory prop does not exist".format(sub_item))
                counts['failMandatoryExist'] += 1
            elif not propNullablePass:
                rsvLogger.error(
                    '{}: Property is null but is not Nullable'.format(
                        sub_item))
                counts['failNullable'] += 1
            rsvLogger.verboseout("\tFAIL")

    return resultList, counts