Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[tool.pytest.ini_options]
pythonpath = [".", "src"]
[pytest]
pythonpath = . src
asyncio_mode = auto
18 changes: 13 additions & 5 deletions src/badfish/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,15 @@ async def find_managers_resource(self):

raw = await response.text("utf-8", "ignore")
data = json.loads(raw.strip())
self.vendor = "Dell" if data.get("Oem") and "Dell" in data["Oem"] else "Supermicro"
oem = data.get("Oem") or {}
if "Dell" in oem:
self.vendor = "Dell"
elif "Supermicro" in oem:
self.vendor = "Supermicro"
elif "Hpe" in oem:
self.vendor = "HPE"
else:
self.vendor = "Unknown"

if "Managers" not in data:
raise BadfishException("Managers resource not found")
Expand Down Expand Up @@ -1117,7 +1125,7 @@ async def reboot_server(self, graceful=True):

async def reset_idrac(self, wait=False):
if self.vendor != "Dell":
self.logger.warning("Vendor isn't a Dell, if you are trying this on a Supermicro, use --bmc-reset instead.")
self.logger.warning("Vendor isn't a Dell, if you are trying this on a Supermicro or HPE, use --bmc-reset instead.")
return False
self.logger.debug("Running reset iDRAC.")
_reset_types = await self.get_reset_types(manager=True)
Expand Down Expand Up @@ -1154,8 +1162,8 @@ async def reset_idrac(self, wait=False):
return True

async def reset_bmc(self):
if self.vendor != "Supermicro":
self.logger.warning("Vendor isn't a Supermicro, if you are trying this on a Dell, use --racreset instead.")
if self.vendor not in ("Supermicro", "HPE"):
self.logger.warning("Vendor isn't a Supermicro or HPE, if you are trying this on a Dell, use --racreset instead.")
return False
self.logger.debug("Running reset BMC.")
_reset_types = await self.get_reset_types(manager=True, bmc=True)
Expand Down Expand Up @@ -1630,7 +1638,7 @@ async def boot_to_virtual_media(self):

_uri = "%s%s" % (self.host_uri, self.system_resource)
_headers = {"Content-Type": "application/json"}
if self.vendor == "Supermicro":
if self.vendor in ("Supermicro", "HPE"):
_payload = {"Boot": {"BootSourceOverrideEnabled": "Once"}}

_response = await self.get_request(_uri)
Expand Down
12 changes: 12 additions & 0 deletions tests/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,18 @@ def render_device_dict(index, device):
ROOT_RESP_SUPERMICRO,
MAN_RESP,
]
ROOT_RESP_HPE = (
'{"Managers":{"@odata.id":"/redfish/v1/Managers"},"Systems":{"@odata.id":"/redfish/v1/Systems"}, '
'"RedfishVersion": "1.0.2","Oem":{"Hpe":{"Manager":[{}]}}}'
)
MAN_RESP_HPE = '{"Members":[{"@odata.id":"/redfish/v1/Managers/1"}]}'
INIT_RESP_HPE = [
ROOT_RESP_HPE,
ROOT_RESP_HPE,
SYS_RESP,
ROOT_RESP_HPE,
MAN_RESP_HPE,
]

RESPONSE_INIT_CREDENTIALS_UNAUTHORIZED = (
f"- ERROR - Failed to authenticate. Verify your credentials for {MOCK_HOST}\n"
Expand Down
16 changes: 15 additions & 1 deletion tests/test_reset_bmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from tests.config import (
BOOT_SEQ_RESPONSE_DIRECTOR,
INIT_RESP,
INIT_RESP_HPE,
INIT_RESP_SUPERMICRO,
RESET_TYPE_RESP,
RESET_TYPE_RESP_NO_ALLOWABLE_VALUES,
Expand Down Expand Up @@ -66,4 +67,17 @@ def test_reset_bmc_wrong_vendor(self, mock_get, mock_post, mock_delete):
self.boot_seq = BOOT_SEQ_RESPONSE_DIRECTOR
self.args = [self.option_arg]
_, err = self.badfish_call()
assert err == RESPONSE_RESET_WRONG_VENDOR % ("Supermicro", "Dell", "--racreset")
assert err == RESPONSE_RESET_WRONG_VENDOR % ("Supermicro or HPE", "Dell", "--racreset")

@patch("aiohttp.ClientSession.delete")
@patch("aiohttp.ClientSession.post")
@patch("aiohttp.ClientSession.get")
def test_reset_bmc_hpe(self, mock_get, mock_post, mock_delete):
responses = INIT_RESP_HPE + [RESET_TYPE_RESP]
self.set_mock_response(mock_get, 200, responses)
self.set_mock_response(mock_post, [200, 200], "OK", True)
self.set_mock_response(mock_delete, 200, "OK")
self.boot_seq = BOOT_SEQ_RESPONSE_DIRECTOR
self.args = [self.option_arg]
_, err = self.badfish_call()
assert err == RESPONSE_RESET % ("200", "BMC", "BMC")
2 changes: 1 addition & 1 deletion tests/test_reset_idrac.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_reset_idrac_wrong_vendor(self, mock_get, mock_post, mock_delete):
self.boot_seq = BOOT_SEQ_RESPONSE_DIRECTOR
self.args = [self.option_arg]
_, err = self.badfish_call()
assert err == RESPONSE_RESET_WRONG_VENDOR % ("Dell", "Supermicro", "--bmc-reset")
assert err == RESPONSE_RESET_WRONG_VENDOR % ("Dell", "Supermicro or HPE", "--bmc-reset")

@patch("badfish.main.Badfish.wait_for_idrac_ready", new_callable=AsyncMock)
@patch("aiohttp.ClientSession.delete")
Expand Down
72 changes: 72 additions & 0 deletions tests/test_vendor_detection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import logging
from unittest.mock import AsyncMock, MagicMock

import pytest

from badfish.main import Badfish


ROOT_RESP_UNKNOWN_OEM = (
'{"Managers":{"@odata.id":"/redfish/v1/Managers"},'
'"RedfishVersion":"1.0.2","Oem":{"Lenovo":{}}}'
)
MAN_RESP = '{"Members":[{"@odata.id":"/redfish/v1/Managers/1"}]}'


def _make_resp(body: str) -> MagicMock:
resp = MagicMock()
resp.status = 200
resp.text = AsyncMock(return_value=body)
return resp


async def _init_vendor(root_body: str) -> str:
logger = MagicMock(spec=logging.Logger)
bf = Badfish("test_host", "user", "pass", logger, 1)
bf.http_client = MagicMock()
bf.http_client.get_request = AsyncMock(
side_effect=[_make_resp(root_body), _make_resp(MAN_RESP)]
)
await bf.find_managers_resource()
return bf.vendor


@pytest.mark.asyncio
async def test_vendor_dell():
root = (
'{"Managers":{"@odata.id":"/redfish/v1/Managers"},'
'"RedfishVersion":"1.0.2","Oem":{"Dell":{"ServiceTag":"T35T7A6"}}}'
)
assert await _init_vendor(root) == "Dell"


@pytest.mark.asyncio
async def test_vendor_supermicro():
root = (
'{"Managers":{"@odata.id":"/redfish/v1/Managers"},'
'"RedfishVersion":"1.0.2","Oem":{"Supermicro":{}}}'
)
assert await _init_vendor(root) == "Supermicro"


@pytest.mark.asyncio
async def test_vendor_hpe():
root = (
'{"Managers":{"@odata.id":"/redfish/v1/Managers"},'
'"RedfishVersion":"1.0.2","Oem":{"Hpe":{"Manager":[{}]}}}'
)
assert await _init_vendor(root) == "HPE"


@pytest.mark.asyncio
async def test_vendor_unknown_oem():
assert await _init_vendor(ROOT_RESP_UNKNOWN_OEM) == "Unknown"


@pytest.mark.asyncio
async def test_vendor_no_oem():
root = (
'{"Managers":{"@odata.id":"/redfish/v1/Managers"},'
'"RedfishVersion":"1.0.2"}'
)
assert await _init_vendor(root) == "Unknown"
21 changes: 21 additions & 0 deletions tests/test_virtual_media.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
BOOT_SOURCE_OVERRIDE_TARGET_CD,
BOOT_SOURCE_OVERRIDE_TARGET_USBCD,
INIT_RESP,
INIT_RESP_HPE,
INIT_RESP_SUPERMICRO,
JOB_OK_RESP,
RESPONSE_BOOT_TO,
Expand Down Expand Up @@ -431,6 +432,26 @@ def test_fail_sm(self, mock_get, mock_post, mock_delete, mock_patch):
_, err = self.badfish_call()
assert err == VMEDIA_BOOT_TO_SM_FAIL

@patch("aiohttp.ClientSession.patch")
@patch("aiohttp.ClientSession.delete")
@patch("aiohttp.ClientSession.post")
@patch("aiohttp.ClientSession.get")
def test_good_hpe(self, mock_get, mock_post, mock_delete, mock_patch):
responses_get = [
VMEDIA_GET_VM_CONFIG_RESP_DELL,
VMEDIA_MEMBER_RM_DISK_RESP,
VMEDIA_MEMBER_CD_RESP,
BOOT_SOURCE_OVERRIDE_TARGET_USBCD,
]
responses = INIT_RESP_HPE + responses_get
self.set_mock_response(mock_get, 200, responses)
self.set_mock_response(mock_post, 200, "OK")
self.set_mock_response(mock_delete, 200, "OK")
self.set_mock_response(mock_patch, 200, "OK")
self.args = [self.option_arg]
_, err = self.badfish_call()
assert err == VMEDIA_BOOT_TO_SM_PASS


class TestCheckRemoteImage(TestBase):
option_arg = "--check-remote-image"
Expand Down
Loading