Skip to content

Commit

Permalink
Example of implementation and use of the WriteCheck flag
Browse files Browse the repository at this point in the history
- Example of passing an using the WarnCheck flag in code
- Adding sEnum "NA"
- Modification of the findPropItemforString method
- Adding the getPropValue method
- Sets default result value of property on N/A when WriteCheck flag is off
- Sets default result value of property on False when WriteCheck flag is on
- Example of updating property of resource.
- Comment with an example of logic for updating the resource and checking whether the operation was successful, written in pseudocode.
  • Loading branch information
zbigniewj-se committed Jan 4, 2024
1 parent 2d84492 commit 9f1ac11
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 15 deletions.
71 changes: 60 additions & 11 deletions redfish_interop_validator/interop.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@

import re, copy
from enum import Enum
from collections import Counter
from collections import Counter, OrderedDict

import logging
from redfish_interop_validator.redfish import getNamespaceUnversioned, getType, getNamespace
from redfish_interop_validator.traverseInterop import callResourceURI
import redfish_interop_validator.traverseInterop as traverseInterop
my_logger = logging.getLogger()
my_logger.setLevel(logging.DEBUG)

config = {'WarnRecommended': False, 'WriteCheck': False}
config = {'WarnRecommended': traverseInterop.config.get('warnrecommended'),
'WriteCheck': traverseInterop.config.get('writecheck')}

class sEnum(Enum):
FAIL = 'FAIL'
NOPASS = 'NO PASS'
PASS = 'PASS'
WARN = 'WARN'
OK = 'OK'
NA = 'N/A'

REDFISH_ABSENT = 'n/a'

Expand Down Expand Up @@ -180,12 +182,31 @@ def findPropItemforString(propObj, itemname):
Finds an appropriate object for an item
"""
for prop in propObj.getResourceProperties():
rf_payloadName = prop.name.split(':')[-1]
if prop.find(':') != -1:
rf_payloadName = prop.name.split(':')[-1]
else:
rf_payloadName = prop
if itemname == rf_payloadName:
return prop
return None


def getPropValue(propObj, itemname):
"""
Finds an appropriate object for an item
"""
properties = propObj.getResourceProperties()
for prop in properties:
if prop.find(':') != -1:
rf_payloadName = prop.name.split(':')[-1]
if itemname == rf_payloadName:
return prop.name.split(':')[1]
elif itemname == prop:
rf_payloadName = prop
return properties[prop]
return None


def validateWriteRequirement(propObj, profile_entry, itemname):
"""
Validates if a property is WriteRequirement or not
Expand All @@ -194,20 +215,48 @@ def validateWriteRequirement(propObj, profile_entry, itemname):
permission = 'Read'
expected = "OData.Permission/ReadWrite" if profile_entry else "Any"
if not config['WriteCheck']:
paramPass = True
paramPass = sEnum.NA
return msgInterop('WriteRequirement', profile_entry, expected, permission, paramPass),\
paramPass
if profile_entry:
targetProp = findPropItemforString(propObj, itemname.replace('#', ''))
propAttr = None
propVal = None
newAttr = None
changedAttr = None
if targetProp is not None:
propAttr = targetProp.propDict.get('OData.Permissions')
if propAttr is not None:
permission = propAttr.get('EnumMember', 'Read')
propVal = getPropValue(propObj, itemname.replace('#', ''))
if propVal is not None:
match propVal:
case str():
newAttr = propVal + 'a'
case int():
newAttr = propVal + 1
case float():
newAttr = propVal + 1.1
case dict():
newAttr['Test'].append('test1')
case OrderedDict():
newAttr['Test'].append('test2')
# NOTE: Here can be write new attribute to server
# (example in pseudo code)
# if newAttr is not None:
# curl update json message using 'login & password' / 'X-AuthTocken'
# changedAttr == get value of updated Prop from server
# permission = 'Write'
paramPass = permission \
== "OData.Permission/ReadWrite"

else:
paramPass = False

# NOTE: Here can be check if newAttr is the same as the Attr retrived from the server
# (example in pseudo code)
# if changedAttr is not None and changedAttr == newAttr:
# paramPass = True
#
# else:
# paramPass = False

else:
paramPass = True

Expand Down Expand Up @@ -249,7 +298,7 @@ def checkComparison(val, compareType, target):
paramPass = len(alltarget) == len(target)
if compareType == "LinkToResource":
vallink = val.get('@odata.id')
success, rf_payload, code, elapsed = callResourceURI(vallink)
success, rf_payload, code, elapsed = traverseInterop.callResourceURI(vallink)
if success:
ourType = rf_payload.get('@odata.type')
if ourType is not None:
Expand Down Expand Up @@ -595,7 +644,7 @@ def validateActionRequirement(profile_entry, rf_payload_tuple, actionname):
return msgs, counts
if "@Redfish.ActionInfo" in rf_payload_item:
vallink = rf_payload_item['@Redfish.ActionInfo']
success, rf_payload_action, code, elapsed = callResourceURI(vallink)
success, rf_payload_action, code, elapsed = traverseInterop.callResourceURI(vallink)
if not success:
rf_payload_action = None

Expand Down
8 changes: 7 additions & 1 deletion redfish_interop_validator/traverseInterop.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,13 @@ def __init__(self, name: str, uri: str, jsondata: dict, typename: str, context:
self.initiated = True

def getResourceProperties(self):
allprops = self.propertyList + self.additionalList[:min(len(self.additionalList), 100)]
allprops = list
if hasattr(self, 'jsondata'):
allprops = self.jsondata
elif hasattr(self, 'propertyList'):
allprops = self.propertyList
if hasattr(self, 'additionalList'):
allprops += self.additionalList[:min(len(self.additionalList), 100)]
return allprops

@staticmethod
Expand Down
12 changes: 9 additions & 3 deletions redfish_interop_validator/validateResource.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ def validateSingleURI(URI, profile, uriName='', expectedType=None, expectedSchem
"""
# rs-assertion: 9.4.1
# Initial startup here
interop.config['WarnRecommended'] = traverseInterop.config['warnrecommended']
interop.config['WriteCheck'] = traverseInterop.config['writecheck']

counts = Counter()
results, messages = {}, []

Expand Down Expand Up @@ -218,11 +221,14 @@ def getURIfromOdata(property):
if '/redfish/v1' in property or urlCheck.match(property):
return property
return None

def validateURITree(URI, profile, uriName, expectedType=None, expectedSchema=None, expectedJson=None):
"""name
Validates a Tree of URIs, traversing from the first given
"""
interop.config['WarnRecommended'] = traverseInterop.config['warnrecommended']
interop.config['WriteCheck'] = traverseInterop.config['writecheck']

allLinks = set()
allLinks.add(URI.rstrip('/'))
refLinks = list()
Expand All @@ -243,7 +249,7 @@ def validateURITree(URI, profile, uriName, expectedType=None, expectedSchema=Non
SchemaType = getType(resource_obj.jsondata.get('@odata.type', 'NoType'))
resource_stats[SchemaType] = {
"Exists": True,
"Writeable": False,
"Writeable": traverseInterop.config['writecheck'],
"URIsFound": [URI.rstrip('/')],
"SubordinateTo": set(),
"UseCasesFound": set()
Expand Down Expand Up @@ -307,7 +313,7 @@ def validateURITree(URI, profile, uriName, expectedType=None, expectedSchema=Non
if resource_stats.get(SchemaType) is None:
resource_stats[SchemaType] = {
"Exists": True,
"Writeable": False,
"Writeable": traverseInterop.config['writecheck'],
"URIsFound": [link.rstrip('/')],
"SubordinateTo": set([tuple(reversed(subordinate_tree))]),
"UseCasesFound": set(usecases_found),
Expand Down

0 comments on commit 9f1ac11

Please sign in to comment.