Skip to content

Commit

Permalink
Added Redfish API for following operation
Browse files Browse the repository at this point in the history
Reading a BIOS attribute using Redfish API
Set value to BIOS attribute using Redfish API
IOEnlarger capacity BIOS attribute can be update and read using
  • Loading branch information
abdhaleegit authored and maramsmurthy committed Jun 13, 2024
2 parents aa13626 + 6cc6b00 commit 198e561
Show file tree
Hide file tree
Showing 7 changed files with 458 additions and 3 deletions.
2 changes: 2 additions & 0 deletions OpTestConfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,8 @@ def get_parser():
help="Don't exit if we find unknown command line arguments")
misc_group.add_argument("--secvar-payload-url",
help="Specify a URL for the secvar test data payload")
misc_group.add_argument("--bisect-flag",
help="Specify if bisection is to be done or not")

return parser

Expand Down
60 changes: 60 additions & 0 deletions common/OpTestEBMC.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# permissions and limitations under the License.


import os
import time
import requests
import json
Expand Down Expand Up @@ -127,6 +128,65 @@ def wait_for_bmc_runtime(self, timeout=10):
key=['Status', 'State'],
minutes=timeout)
return status

def set_attribute_redfish(self, uri, attribute_name, attribute_value):
"""
Changing any attribute value using Redfish API
:param uri: redfish uri at which the attribute can be updated
:param attribute_name: Should be same as attribute name in redfish
:param attribute_value: Value want be be updated for attribute
"""
auth_token = self.generate_ssl_auth_token(ip_add=self.conf.args.bmc_ip)
content_type = "-H 'Content-Type: application/json'"
rest_server = "https://{}{}".format(self.conf.args.bmc_ip, uri)
attribute_param = '\'{"Attributes":{'+'{}:{}'.format(attribute_name, attribute_value)+'}}\''
curl_command = "curl -k -H"+" 'X-Auth-Token: "+auth_token+"' "+content_type+f" -X PATCH {rest_server} "+f"-d {attribute_param}"
log.info("Command to set attribut: "+curl_command)
try:
output = os.system(curl_command)
return output
except CommandFailed as cf:
return cf.output

def generate_ssl_auth_token(self, ip_add = None):
"""
Generates ssl key then returns the ssl key
"""
payload = {
"username": self.conf.args.bmc_username,
"password": self.conf.args.bmc_password
}
uri = f"https://{ip_add}/redfish/v1/SessionService/Sessions"
creds = '{"UserName":\"'+ self.conf.args.bmc_username + '","Password":\"' + self.conf.args.bmc_password + '"}'
file_name = "/tmp/headers-"+time.strftime("%Y%m%d%H%M%S")+".txt"
sess_cmd = 'curl -k -H "Content-Type: application/json" -X POST -D '+file_name+" "+uri+' -d '+"\'"+creds+"\'"
os.system(sess_cmd)
auth_file = open(file_name)
token = auth_file.read()
token = [line for line in token.split("\n") if "X-Auth-Token" in line][0].split(":")[1].strip()
if token:
return token
else:
log.info("Token not found in response")
return None

def get_bios_attribute_value(self, bios_attribute=None, minutes=BMC_CONST.HTTP_RETRY):
"""
Get BIOS current attribute value using redfish api
"""
uri = "/redfish/v1/Systems/system/Bios"
r = self.conf.util_bmc_server.get(uri=uri, minutes=minutes)
return r.json().get("Attributes").get(bios_attribute)

def set_bios_attribute(self, bios_attribute=None, bios_attribute_val=None):
'''
Set BMC BIOS attribute to provided value
'''
uri = '/redfish/v1/Systems/system/Bios/Settings'
return self.set_attribute_redfish(uri=uri,
attribute_name='"'+bios_attribute+'"',
attribute_value=bios_attribute_val)


class OpTestEBMC():
Expand Down
116 changes: 116 additions & 0 deletions common/OpTestUtil.py
Original file line number Diff line number Diff line change
Expand Up @@ -2353,6 +2353,122 @@ def prepare_source(self,spec_file, host, dest_path, package, build_option=None,
except OpTestError:
return ""

def err_message(self, er):
"""
To get entire error msg
"""
error_message = [str(item) for item in er]
combined_error = '\n'.join(error_message)
combined_error = re.sub(r'\x1b\[[0-9;]*[mK]|,', '', combined_error)
parts = combined_error.split('\x1b[')
cleaned_error = ''.join(char for char in combined_error if ord(char) < 128)
pattern = r'in file.*?compilation terminated'
match = re.search(pattern, cleaned_error, re.IGNORECASE | re.DOTALL)
if match:
relevant_part = match.group(0)
return relevant_part
return " "

def get_email(self, commit_id):
"""
To get email of Author from the identified bad commit
"""
connection = self.conf.host()
try :
connection.host_run_command("git config --global color.ui true")
result = connection.host_run_command("git show --format=%ce {} | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'".format(commit_id))
return result[0]
except subprocess.CalledProcessError as e:
log.info(e)
return None

def build_bisector(self, linux_path, good_commit, repo):
connection = self.conf.host()
connection.host_run_command(" if [ '$(pwd)' != {} ]; then cd {} || exit 1 ; fi ".format(linux_path,linux_path))
shallow = connection.host_run_command("git rev-parse --is-shallow-repository")
if shallow[-1] in [True,'true']:
connection.host_run_command("git fetch --unshallow",timeout=3000)
makefile_path = os.path.join(self.conf.basedir, "test_binaries/make.sh")
connection.copy_test_file_to_host(makefile_path, dstdir=linux_path)
connection.host_run_command("git bisect start")
folder_type=re.split(r'[\/\\.]',str(repo))[-2]
if folder_type == 'linux-next':
connection.host_run_command("git fetch --tags")
good_tag=connection.host_run_command("git tag -l 'v[0-9]*' | sort -V | tail -n 1")
connection.host_run_command("git bisect good {} ".format(good_tag))
else:
connection.host_run_command("git bisect good {} ".format(good_commit))
connection.host_run_command(" git bisect bad ")
connection.host_run_command("chmod +x ./make.sh ")
commit = connection.host_run_command(" git bisect run ./make.sh")
badCommit = [word for word in commit if word.endswith("is the first bad commit")]
badCommit= badCommit[0].split()[0]
email = self.get_email(badCommit)
connection.host_run_command("git bisect log")
connection.host_run_command("git bisect reset")
return email, badCommit

def get_commit_message(self, linux_path, commit_sha):
connection = self.conf.host()
try:
connection.host_run_command(" if [ '$(pwd)' != {} ]; then cd {} || exit 1 ; fi ".format(linux_path,linux_path))
commit_message = connection.host_run_command("git log -n 1 --pretty=format:%s {} | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'".format(commit_sha))
except subprocess.CalledProcessError as e:
log.info(e)
commit_message = None
return commit_message[0].strip()

def format_email(self, linux_path , repo):
connection = self.conf.host()
machine_type = connection.host_run_command("uname -m")
gcc_version = connection.host_run_command("gcc --version")[0]
kernel_version = connection.host_run_command("uname -r")
try:
with open("output.json", "r") as file:
data = json.load(file)
error_message = data.get("error", "")
err_long = data.get("err_msg","")
commit = str(data.get("commit", ""))[:7]
except FileNotFoundError:
log.error("Error: output.json not found.")
error_message = ""
commit = ""
fix_description = self.get_commit_message(linux_path,commit)
if "netdev/net" in repo:
linux = "netdev/net"
elif "netdev/net-next" in repo:
linux = "netdev/net-next"
elif "mkp/scsi" in repo :
linux = "scsi/scsi-queue"
elif "torvalds/linux" in repo:
linux = "mainline/master"
elif "next/linux-next" in repo:
linux = "linux-next/master"
else:
linux = "linux"
subject = "[{}][bisected {}] [{}] build fail with error: {}".format(linux,commit, machine_type, error_message)
body = """
Greetings,
Today's {} kernel fails to build on {} machine.
Kernel build fail at error: {}
{}
Kernel Version: {}
Machine Type: {}
gcc: {}
Bisected Commit: {}
kernel builds fine when the bad commit ({}) is reverted
{} - {}
--
Regards
Linux CI
""".format(linux, machine_type, error_message,err_long,kernel_version, machine_type, gcc_version, commit,commit, commit, fix_description)
with open("email.json","w") as email:
json.dump({"subject":subject,"body":body},email)


class Server(object):
'''
Expand Down
1 change: 1 addition & 0 deletions op-test
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ from testcases import GcovSetup
from testcases import Lcov
from testcases import BisectKernel
from testcases import OpTestHtxBootme
from testcases import OpTestKernelTest
import OpTestConfiguration
import sys
import time
Expand Down
21 changes: 21 additions & 0 deletions test_binaries/make.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash
# OpenPOWER Automated Test Project
#
# Contributors Listed Below - COPYRIGHT 2024
# [+] International Business Machines Corp.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
nproc=`nproc`
yes "" | make olddefconfig /dev/null 2>&1
make -j$nproc -S vmlinux > /dev/null 2>&1
56 changes: 53 additions & 3 deletions testcases/MachineConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@

import OpTestConfiguration
import OpTestLogger
from common import OpTestASM
from common import OpTestHMC
from common import OpTestInstallUtil
from common.OpTestEBMC import EBMCHostManagement
from common.OpTestUtil import OpTestUtil
from common.OpTestSystem import OpSystemState

Expand Down Expand Up @@ -140,6 +142,7 @@ def callConfig(self, key, lpar=""):
if key == "cec":
lmb_size = None
num_hugepages = None
ioenlargecapacity = None
setup = 0
if not self.cv_HMC.lpar_vios:
self.skipTest("Please pass lpar_vios in config file.")
Expand All @@ -158,10 +161,15 @@ def callConfig(self, key, lpar=""):
num_hugepages = re.findall(
'hugepages=[0-9]+',
str(self.machine_config))[0].split('=')[1]
if "iocapacity" in config_value:
setup=1
if self.bmc_type == "EBMC_PHYP":
ioenlargecapacity = re.findall(
'iocapacity=[0-9]+', str(self.machine_config))[0].split('=')[1]

status = CecConfig(self.cv_HMC, self.system_name, self.lpar_name,
self.lpar_prof, lmb=lmb_size,
hugepages=num_hugepages).CecSetup()
hugepages=num_hugepages, iocapacity=ioenlargecapacity).CecSetup()
if status:
self.fail(status)
if not setup:
Expand Down Expand Up @@ -474,7 +482,8 @@ class CecConfig():
'''

def __init__(self, cv_HMC=None, system_name=None,
lpar_name=None, lpar_prof=None, lmb=None, hugepages=None):
lpar_name=None, lpar_prof=None, lmb=None, hugepages=None,
iocapacity=None):

self.cv_HMC = cv_HMC
self.system_name = system_name
Expand All @@ -483,7 +492,13 @@ def __init__(self, cv_HMC=None, system_name=None,
self.lmb_size = lmb
self.num_hugepages = hugepages
self.setup = 0
self.cec_dict = {'lmb_cec': None, 'hugepages': None}
self.iocapacity = iocapacity
self.cec_dict = {'lmb_cec': None, 'hugepages': None, 'iocapacity': None}
self.config = OpTestConfiguration.conf
self.BMCHostMgmt = EBMCHostManagement(conf=self.config,
ip=self.config.args.bmc_ip,
username=self.config.args.bmc_username,
password=self.config.args.bmc_password)

def CecSetup(self):

Expand All @@ -494,6 +509,8 @@ def CecSetup(self):
self.lmb_setup()
if self.cec_dict['hugepages'] is not None:
self.hugepage_16gb_setup()
if self.cec_dict['iocapacity'] is not None:
self.io_enlarge_cpacity()
if self.setup:
self.cv_HMC.poweron_system()
self.ValidateCEC_Setup()
Expand All @@ -519,6 +536,8 @@ def ValidateCEC_Setup(self):
self.setting_16gb_hugepage_profile()
else:
self.cec_dict['hugepages'] = self.num_hugepages
if self.iocapacity:
self.io_enlarge_capacity()

def lmb_setup(self):
# Configure the lmb as per user request
Expand All @@ -536,6 +555,37 @@ def setting_16gb_hugepage_profile(self):
int(self.current_hgpg[0]))
self.cv_HMC.set_lpar_cfg(attrs)

def io_enlarge_capacity(self):
"""
Calling set IO Enlarge capacity if provided value is not same as current value
"""
cur_iocapacity = self.get_current_ioadapter_enlarged_capacity()
log.info("Setting up ioenlarge capacity")
log.info("Current ioenlarge capacity value:"+str(cur_iocapacity))
if cur_iocapacity != self.iocapacity:
self.set_ioenlarge_capacity()
else:
log.info("Provided IO Enlarge capacity value is same as current value, Exiting...")

def get_current_ioadapter_enlarged_capacity(self):
"""
Get ioadapter enlarged capcity value
"""
log.debug("=====Get current IOAdapter Enlarge Capacity=====")
return self.BMCHostMgmt.get_bios_attribute_value(
bios_attribute="hb_ioadapter_enlarged_capacity_current"
)

def set_ioenlarge_capacity(self):
"""
Set ioadapter enlarged capcity value
"""
log.debug("=====Set IOAdapter Enlarge Capacity=====")
self.BMCHostMgmt.set_bios_attribute(
bios_attribute="hb_ioadapter_enlarged_capacity",
bios_attribute_val=self.iocapacity
)


class OsConfig():
'''
Expand Down
Loading

0 comments on commit 198e561

Please sign in to comment.