Add BIOS keywords to manage bmc via Redfish API

Implements BiosKeywords class to provide BIOS management capabilities
for StarlingX test automation. The keywords support connecting to
server BMCs using the Redfish API standard for hardware management.

Key features:
- Automatic system discovery and connection via BMC IP
- Boot order configuration with override settings
- System information retrieval (vendor, model, system ID)
- Support for common boot devices (PXE, HDD, CD, USB, BIOS setup)

The implementation uses the python-redfish library to communicate
with BMC endpoints.

Usage:
    lab_config = ConfigurationManager.get_lab_config()
    bm_password = lab_config.get_bm_password()
    node = lab_config.get_node(host_name)
    bm_ip = node.get_bm_ip()
    bm_username = node.get_bm_username()

    bios_keywords = BiosKeywords(bmc_ip=bm_ip, username=bm_username, password=bm_password)
    print(bios_keywords.get_boot_order())
    bios_keywords.set_boot_order()
    print(bios_keywords.get_boot_order())

Test Plan:
- Manual testing with Dell PowerEdge servers
- Verified boot order changes via BMC interface
- Tested system discovery and connection establishment

Change-Id: I6efc4eec136f67730d14b225fe7a78b318c12eb8
Signed-off-by: Abhishek jaiswal <abhishek.jaiswal@windriver.com>
This commit is contained in:
Abhishek jaiswal
2025-08-25 08:46:22 -04:00
parent 6723f696ec
commit 730296f83f
3 changed files with 110 additions and 0 deletions

View File

@@ -33,3 +33,4 @@ sphinx = "==8.1.3"
sphinx-autobuild = "==2024.2.4"
openstackdocstheme = "==3.4.1"
reno = "==4.1.0"
python-redfish = "==0.4.4"

8
Pipfile.lock generated
View File

@@ -865,6 +865,14 @@
"markers": "python_version >= '3.8'",
"version": "==8.1.1"
},
"python-redfish": {
"hashes": [
"sha256:6c37652481dcd5b391e3741234b34f80ece3bcb62b07d9169f6ee3d6495c9e2f",
"sha256:ef5fbbf62cd8d474075c9844a369182c4e1b10dac2e7d4bd461f52c6f1e89ce8"
],
"index": "pypi",
"version": "==0.4.4"
},
"pyyaml": {
"hashes": [
"sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff",

View File

@@ -0,0 +1,101 @@
import redfish
from framework.logging.automation_logger import get_logger
from keywords.base_keyword import BaseKeyword
class BiosKeywords(BaseKeyword):
"""Keywords for BIOS operations using Redfish API."""
def __init__(self, bmc_ip: str, username: str, password: str):
"""Initialize Redfish client for Bios keywords.
Args:
bmc_ip (str): BMC IP address.
username (str): Username for authentication.
password (str): Password for authentication.
"""
self.bmc_ip = bmc_ip
self.username = username
self.password = password
self.system_id = None
self.vendor = None
self.model = None
self.redfish_client = redfish.redfish_client(
base_url=f"https://{self.bmc_ip}",
username=self.username,
password=self.password,
timeout=30,
default_prefix="/redfish/v1"
)
self.redfish_client.login(auth='session')
self.discover_system_info()
def discover_system_info(self):
"""Discover system ID from Redfish API."""
# Detect system ID
systems_resp = self.redfish_client.get("/redfish/v1/Systems")
if systems_resp.status != 200:
raise Exception(f"Failed to get systems: {systems_resp.status}")
members = systems_resp.dict.get("Members", [])
if not members:
raise Exception("No system members found in Redfish response")
self.system_id = list(members[0].values())[0] # e.g. /redfish/v1/Systems/System.Embedded.1
# Get system details
sys_info = self.get_system_info()
self.vendor = sys_info.get("Manufacturer", "Unknown")
self.model = sys_info.get("Model", "Unknown")
get_logger().log_info(f"Connected to {self.vendor} {self.model}, System ID: {self.system_id}")
def get_system_info(self):
"""Fetch system information"""
if not self.system_id:
raise Exception("Not connected to system")
resp = self.redfish_client.get(self.system_id)
if resp.status == 200:
# get_logger().log_info(f"system_info {resp.dict}")
return resp.dict
else:
raise Exception(f"Failed to fetch system info: {resp.status}")
def get_boot_order(self):
"""Fetch current boot order and override settings"""
if not self.system_id:
raise Exception("Not connected to system")
resp = self.redfish_client.get(self.system_id)
if resp.status == 200:
return resp.dict.get("Boot", {})
else:
raise Exception(f"Failed to fetch system info: {resp.status}")
return self.boot_order
def set_boot_order(self, device="Pxe", enabled="Once"):
"""Set boot order (override target device).
:param device (str): Boot device (e.g., "Pxe", "Hdd", "Cd", "Usb", "BiosSetup")
:param enabled (str): Boot override enabled mode ("Once", "Continuous", "Disabled")
"""
if not self.system_id:
raise Exception("Not connected to system")
payload = {
"Boot": {
"BootSourceOverrideTarget": device,
"BootSourceOverrideEnabled": enabled
}
}
resp = self.redfish_client.patch(self.system_id, body=payload)
if resp.status in [200, 204]:
get_logger().log_info(f"Boot device set to {device}, mode={enabled}")
else:
raise Exception(f"Failed to set boot order: {resp.status}, {resp.text}")